static int image_make( const char *pretty, int dfd, const char *path, const char *filename, Image **ret) { struct stat st; bool read_only; int r; assert(filename); /* We explicitly *do* follow symlinks here, since we want to * allow symlinking trees into /var/lib/machines/, and treat * them normally. */ if (fstatat(dfd, filename, &st, 0) < 0) return -errno; read_only = (path && path_startswith(path, "/usr")) || (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS); if (S_ISDIR(st.st_mode)) { _cleanup_close_ int fd = -1; unsigned file_attr = 0; if (!ret) return 1; if (!pretty) pretty = filename; fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY); if (fd < 0) return -errno; /* btrfs subvolumes have inode 256 */ if (st.st_ino == 256) { r = btrfs_is_filesystem(fd); if (r < 0) return r; if (r) { BtrfsSubvolInfo info; /* It's a btrfs subvolume */ r = btrfs_subvol_get_info_fd(fd, 0, &info); if (r < 0) return r; r = image_new(IMAGE_SUBVOLUME, pretty, path, filename, info.read_only || read_only, info.otime, 0, ret); if (r < 0) return r; if (btrfs_quota_scan_ongoing(fd) == 0) { BtrfsQuotaInfo quota; r = btrfs_subvol_get_subtree_quota_fd(fd, 0, "a); if (r >= 0) { (*ret)->usage = quota.referenced; (*ret)->usage_exclusive = quota.exclusive; (*ret)->limit = quota.referenced_max; (*ret)->limit_exclusive = quota.exclusive_max; } } return 1; } } /* If the IMMUTABLE bit is set, we consider the * directory read-only. Since the ioctl is not * supported everywhere we ignore failures. */ (void) read_attr_fd(fd, &file_attr); /* It's just a normal directory. */ r = image_new(IMAGE_DIRECTORY, pretty, path, filename, read_only || (file_attr & FS_IMMUTABLE_FL), 0, 0, ret); if (r < 0) return r; return 1; } else if (S_ISREG(st.st_mode) && endswith(filename, ".raw")) { usec_t crtime = 0; /* It's a RAW disk image */ if (!ret) return 1; fd_getcrtime_at(dfd, filename, &crtime, 0); if (!pretty) pretty = strndupa(filename, strlen(filename) - 4); r = image_new(IMAGE_RAW, pretty, path, filename, !(st.st_mode & 0222) || read_only, crtime, timespec_load(&st.st_mtim), ret); if (r < 0) return r; (*ret)->usage = (*ret)->usage_exclusive = st.st_blocks * 512; (*ret)->limit = (*ret)->limit_exclusive = st.st_size; return 1; } return 0; }
static int image_make( const char *pretty, int dfd, const char *path, const char *filename, const struct stat *st, Image **ret) { _cleanup_free_ char *pretty_buffer = NULL; struct stat stbuf; bool read_only; int r; assert(dfd >= 0 || dfd == AT_FDCWD); assert(filename); /* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block * devices into /var/lib/machines/, and treat them normally. * * This function returns -ENOENT if we can't find the image after all, and -EMEDIUMTYPE if it's not a file we * recognize. */ if (!st) { if (fstatat(dfd, filename, &stbuf, 0) < 0) return -errno; st = &stbuf; } read_only = (path && path_startswith(path, "/usr")) || (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS); if (S_ISDIR(st->st_mode)) { _cleanup_close_ int fd = -1; unsigned file_attr = 0; if (!ret) return 0; if (!pretty) { r = extract_pretty(filename, NULL, &pretty_buffer); if (r < 0) return r; pretty = pretty_buffer; } fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY); if (fd < 0) return -errno; /* btrfs subvolumes have inode 256 */ if (st->st_ino == 256) { r = btrfs_is_filesystem(fd); if (r < 0) return r; if (r) { BtrfsSubvolInfo info; /* It's a btrfs subvolume */ r = btrfs_subvol_get_info_fd(fd, 0, &info); if (r < 0) return r; r = image_new(IMAGE_SUBVOLUME, pretty, path, filename, info.read_only || read_only, info.otime, 0, ret); if (r < 0) return r; if (btrfs_quota_scan_ongoing(fd) == 0) { BtrfsQuotaInfo quota; r = btrfs_subvol_get_subtree_quota_fd(fd, 0, "a); if (r >= 0) { (*ret)->usage = quota.referenced; (*ret)->usage_exclusive = quota.exclusive; (*ret)->limit = quota.referenced_max; (*ret)->limit_exclusive = quota.exclusive_max; } } return 0; } } /* If the IMMUTABLE bit is set, we consider the * directory read-only. Since the ioctl is not * supported everywhere we ignore failures. */ (void) read_attr_fd(fd, &file_attr); /* It's just a normal directory. */ r = image_new(IMAGE_DIRECTORY, pretty, path, filename, read_only || (file_attr & FS_IMMUTABLE_FL), 0, 0, ret); if (r < 0) return r; return 0; } else if (S_ISREG(st->st_mode) && endswith(filename, ".raw")) { usec_t crtime = 0; /* It's a RAW disk image */ if (!ret) return 0; (void) fd_getcrtime_at(dfd, filename, &crtime, 0); if (!pretty) { r = extract_pretty(filename, ".raw", &pretty_buffer); if (r < 0) return r; pretty = pretty_buffer; } r = image_new(IMAGE_RAW, pretty, path, filename, !(st->st_mode & 0222) || read_only, crtime, timespec_load(&st->st_mtim), ret); if (r < 0) return r; (*ret)->usage = (*ret)->usage_exclusive = st->st_blocks * 512; (*ret)->limit = (*ret)->limit_exclusive = st->st_size; return 0; } else if (S_ISBLK(st->st_mode)) { _cleanup_close_ int block_fd = -1; uint64_t size = UINT64_MAX; /* A block device */ if (!ret) return 0; if (!pretty) { r = extract_pretty(filename, NULL, &pretty_buffer); if (r < 0) return r; pretty = pretty_buffer; } block_fd = openat(dfd, filename, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); if (block_fd < 0) log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path, filename); else { /* Refresh stat data after opening the node */ if (fstat(block_fd, &stbuf) < 0) return -errno; st = &stbuf; if (!S_ISBLK(st->st_mode)) /* Verify that what we opened is actually what we think it is */ return -ENOTTY; if (!read_only) { int state = 0; if (ioctl(block_fd, BLKROGET, &state) < 0) log_debug_errno(errno, "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path, filename); else if (state) read_only = true; } if (ioctl(block_fd, BLKGETSIZE64, &size) < 0) log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path, filename); block_fd = safe_close(block_fd); } r = image_new(IMAGE_BLOCK, pretty, path, filename, !(st->st_mode & 0222) || read_only, 0, 0, ret); if (r < 0) return r; if (size != 0 && size != UINT64_MAX) (*ret)->usage = (*ret)->usage_exclusive = (*ret)->limit = (*ret)->limit_exclusive = size; return 0; } return -EMEDIUMTYPE; }
int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) { _cleanup_close_ int sfd = -1; int r; assert(e); assert(path); assert(fd >= 0); assert(compress < _IMPORT_COMPRESS_TYPE_MAX); assert(compress != IMPORT_COMPRESS_UNKNOWN); if (e->output_fd >= 0) return -EBUSY; sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC); if (sfd < 0) return -errno; if (fstat(sfd, &e->st) < 0) return -errno; r = fd_nonblock(fd, true); if (r < 0) return r; r = free_and_strdup(&e->path, path); if (r < 0) return r; e->quota_referenced = (uint64_t) -1; if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */ BtrfsQuotaInfo q; r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q); if (r >= 0) e->quota_referenced = q.referenced; e->temp_path = mfree(e->temp_path); r = tempfn_random(path, NULL, &e->temp_path); if (r < 0) return r; /* Let's try to make a snapshot, if we can, so that the export is atomic */ r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE); if (r < 0) { log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path); e->temp_path = mfree(e->temp_path); } } r = import_compress_init(&e->compress, compress); if (r < 0) return r; r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e); if (r == -EPERM) { r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e); if (r < 0) return r; r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON); } if (r < 0) return r; e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid); if (e->tar_fd < 0) { e->output_event_source = sd_event_source_unref(e->output_event_source); return e->tar_fd; } e->output_fd = fd; return r; }