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; }
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; }
int main(int argc, char *argv[]) { BtrfsQuotaInfo quota; int r, fd; fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); if (fd < 0) log_error_errno(errno, "Failed to open root directory: %m"); else { char ts[FORMAT_TIMESTAMP_MAX], bs[FORMAT_BYTES_MAX]; BtrfsSubvolInfo info; r = btrfs_subvol_get_info_fd(fd, 0, &info); if (r < 0) log_error_errno(r, "Failed to get subvolume info: %m"); else { log_info("otime: %s", format_timestamp(ts, sizeof(ts), info.otime)); log_info("read-only (search): %s", yes_no(info.read_only)); } r = btrfs_qgroup_get_quota_fd(fd, 0, "a); if (r < 0) log_error_errno(r, "Failed to get quota info: %m"); else { log_info("referenced: %s", strna(format_bytes(bs, sizeof(bs), quota.referenced))); log_info("exclusive: %s", strna(format_bytes(bs, sizeof(bs), quota.exclusive))); log_info("referenced_max: %s", strna(format_bytes(bs, sizeof(bs), quota.referenced_max))); log_info("exclusive_max: %s", strna(format_bytes(bs, sizeof(bs), quota.exclusive_max))); } r = btrfs_subvol_get_read_only_fd(fd); if (r < 0) log_error_errno(r, "Failed to get read only flag: %m"); else log_info("read-only (ioctl): %s", yes_no(r)); safe_close(fd); } r = btrfs_subvol_make("/xxxtest"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); r = write_string_file("/xxxtest/afile", "ljsadhfljasdkfhlkjdsfha", WRITE_STRING_FILE_CREATE); if (r < 0) log_error_errno(r, "Failed to write file: %m"); r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest2", 0); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest3", BTRFS_SNAPSHOT_READ_ONLY); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); r = btrfs_subvol_remove("/xxxtest", BTRFS_REMOVE_QUOTA); if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); r = btrfs_subvol_remove("/xxxtest2", BTRFS_REMOVE_QUOTA); if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); r = btrfs_subvol_remove("/xxxtest3", BTRFS_REMOVE_QUOTA); if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); r = btrfs_subvol_snapshot("/etc", "/etc2", BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); r = btrfs_subvol_remove("/etc2", BTRFS_REMOVE_QUOTA); if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); r = btrfs_subvol_make("/xxxrectest"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); r = btrfs_subvol_make("/xxxrectest/xxxrectest2"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); r = btrfs_subvol_make("/xxxrectest/xxxrectest3"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); r = btrfs_subvol_make("/xxxrectest/xxxrectest3/sub"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); if (mkdir("/xxxrectest/dir", 0755) < 0) log_error_errno(errno, "Failed to make directory: %m"); r = btrfs_subvol_make("/xxxrectest/dir/xxxrectest4"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); if (mkdir("/xxxrectest/dir/xxxrectest4/dir", 0755) < 0) log_error_errno(errno, "Failed to make directory: %m"); r = btrfs_subvol_make("/xxxrectest/dir/xxxrectest4/dir/xxxrectest5"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); if (mkdir("/xxxrectest/mnt", 0755) < 0) log_error_errno(errno, "Failed to make directory: %m"); r = btrfs_subvol_snapshot("/xxxrectest", "/xxxrectest2", BTRFS_SNAPSHOT_RECURSIVE); if (r < 0) log_error_errno(r, "Failed to snapshot subvolume: %m"); r = btrfs_subvol_remove("/xxxrectest", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE); if (r < 0) log_error_errno(r, "Failed to recursively remove subvolume: %m"); r = btrfs_subvol_remove("/xxxrectest2", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE); if (r < 0) log_error_errno(r, "Failed to recursively remove subvolume: %m"); r = btrfs_subvol_make("/xxxquotatest"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); r = btrfs_subvol_auto_qgroup("/xxxquotatest", 0, true); if (r < 0) log_error_errno(r, "Failed to set up auto qgroup: %m"); r = btrfs_subvol_make("/xxxquotatest/beneath"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); r = btrfs_subvol_auto_qgroup("/xxxquotatest/beneath", 0, false); if (r < 0) log_error_errno(r, "Failed to set up auto qgroup: %m"); r = btrfs_qgroup_set_limit("/xxxquotatest/beneath", 0, 4ULL * 1024 * 1024 * 1024); if (r < 0) log_error_errno(r, "Failed to set up quota limit: %m"); r = btrfs_subvol_set_subtree_quota_limit("/xxxquotatest", 0, 5ULL * 1024 * 1024 * 1024); if (r < 0) log_error_errno(r, "Failed to set up quota limit: %m"); r = btrfs_subvol_snapshot("/xxxquotatest", "/xxxquotatest2", BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA); if (r < 0) log_error_errno(r, "Failed to setup snapshot: %m"); r = btrfs_qgroup_get_quota("/xxxquotatest2/beneath", 0, "a); if (r < 0) log_error_errno(r, "Failed to query quota: %m"); assert_se(quota.referenced_max == 4ULL * 1024 * 1024 * 1024); r = btrfs_subvol_get_subtree_quota("/xxxquotatest2", 0, "a); if (r < 0) log_error_errno(r, "Failed to query quota: %m"); assert_se(quota.referenced_max == 5ULL * 1024 * 1024 * 1024); r = btrfs_subvol_remove("/xxxquotatest", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE); if (r < 0) log_error_errno(r, "Failed remove subvolume: %m"); r = btrfs_subvol_remove("/xxxquotatest2", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE); if (r < 0) log_error_errno(r, "Failed remove subvolume: %m"); return 0; }
int main(int argc, char *argv[]) { int r; int fd; fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); if (fd < 0) log_error_errno(errno, "Failed to open root directory: %m"); else { BtrfsSubvolInfo info; BtrfsQuotaInfo quota; char ts[FORMAT_TIMESTAMP_MAX], bs[FORMAT_BYTES_MAX]; r = btrfs_subvol_get_info_fd(fd, &info); if (r < 0) log_error_errno(r, "Failed to get subvolume info: %m"); else { log_info("otime: %s", format_timestamp(ts, sizeof(ts), info.otime)); log_info("read-only (search): %s", yes_no(info.read_only)); } r = btrfs_subvol_get_quota_fd(fd, "a); if (r < 0) log_error_errno(r, "Failed to get quota info: %m"); else { log_info("referred: %s", strna(format_bytes(bs, sizeof(bs), quota.referred))); log_info("exclusive: %s", strna(format_bytes(bs, sizeof(bs), quota.exclusive))); log_info("referred_max: %s", strna(format_bytes(bs, sizeof(bs), quota.referred_max))); log_info("exclusive_max: %s", strna(format_bytes(bs, sizeof(bs), quota.exclusive_max))); } r = btrfs_subvol_get_read_only_fd(fd); if (r < 0) log_error_errno(r, "Failed to get read only flag: %m"); else log_info("read-only (ioctl): %s", yes_no(r)); safe_close(fd); } r = btrfs_subvol_make("/xxxtest"); if (r < 0) log_error_errno(r, "Failed to make subvolume: %m"); r = write_string_file("/xxxtest/afile", "ljsadhfljasdkfhlkjdsfha"); if (r < 0) log_error_errno(r, "Failed to write file: %m"); r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest2", false, false); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest3", true, false); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); r = btrfs_subvol_remove("/xxxtest"); if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); r = btrfs_subvol_remove("/xxxtest2"); if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); r = btrfs_subvol_remove("/xxxtest3"); if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); r = btrfs_subvol_snapshot("/etc", "/etc2", true, true); if (r < 0) log_error_errno(r, "Failed to make snapshot: %m"); r = btrfs_subvol_remove("/etc2"); if (r < 0) log_error_errno(r, "Failed to remove subvolume: %m"); return 0; }