From 95e63822c26682efea6ca52550989e27d7e82188 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 15 Jul 2025 17:03:58 +0200 Subject: [PATCH 1/2] btrfs-util: use BTRFS_IOC_GET_SUBVOL_INFO ioctl() Since kernel 4.18 BTRFS_IOC_GET_SUBVOL_INFO exists to query subvolume metadata without privs. This is much better than the manual approach with finding objects in the fs tree (which is priv). Let's use it, and drop the old code (since 4.18 is older than our baseline). --- src/shared/btrfs-util.c | 90 +++++++++-------------------------------- 1 file changed, 18 insertions(+), 72 deletions(-) diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 61fe50e012..869eb490f8 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -299,23 +299,6 @@ static int btrfs_iterate(BtrfsForeachIterator *i) { btrfs_iterate(&iterator) > 0; ) int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) { - struct btrfs_ioctl_search_args args = { - /* Tree of tree roots */ - .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, - - /* Look precisely for the subvolume items */ - .key.min_type = BTRFS_ROOT_ITEM_KEY, - .key.max_type = BTRFS_ROOT_ITEM_KEY, - - .key.min_offset = 0, - .key.max_offset = UINT64_MAX, - - /* No restrictions on the other components */ - .key.min_transid = 0, - .key.max_transid = UINT64_MAX, - }; - - bool found = false; int r; assert(fd >= 0); @@ -327,66 +310,29 @@ int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) { if (fd < 0) return fd; - if (subvol_id == 0) { - r = btrfs_subvol_get_id_fd(fd, &subvol_id); - if (r < 0) - return r; - } else { - r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC); - if (r < 0) - return r; - if (r == 0) - return -ENOTTY; - } + r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC); + if (r < 0) + return r; + if (r == 0) + return -ENOTTY; - args.key.min_objectid = args.key.max_objectid = subvol_id; + struct btrfs_ioctl_get_subvol_info_args info; + if (ioctl(fd, BTRFS_IOC_GET_SUBVOL_INFO, &info) < 0) + return -errno; - while (btrfs_ioctl_search_args_compare(&args) <= 0) { - struct btrfs_ioctl_search_header sh; - const void *body; + *ret = (BtrfsSubvolInfo) { + .subvol_id = info.treeid, + .otime = info.otime.sec * USEC_PER_SEC + (info.otime.nsec / NSEC_PER_USEC), + .read_only = FLAGS_SET(info.flags, BTRFS_SUBVOL_RDONLY), + }; - args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return -errno; + assert_cc(sizeof(info.uuid) == sizeof(sd_id128_t)); + memcpy(&ret->uuid, info.uuid, sizeof(sd_id128_t)); - if (args.key.nr_items <= 0) - break; + assert_cc(sizeof(info.parent_uuid) == sizeof(sd_id128_t)); + memcpy(&ret->parent_uuid, info.parent_uuid, sizeof(sd_id128_t)); - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(sh, body, args) { - /* Make sure we start the next search at least from this entry */ - btrfs_ioctl_search_args_set(&args, &sh); - - if (sh.objectid != subvol_id) - continue; - if (sh.type != BTRFS_ROOT_ITEM_KEY) - continue; - - /* Older versions of the struct lacked the otime setting */ - if (sh.len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec)) - continue; - - const struct btrfs_root_item *ri = body; - ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC + - (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC; - - ret->subvol_id = subvol_id; - ret->read_only = le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY; - - assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid)); - memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid)); - memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid)); - - found = true; - goto finish; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - -finish: - return found ? 0 : -ENODATA; + return 0; } int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) { From 4b27025223206c392c259e6c7cb98c40781801fd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 15 Jul 2025 17:21:03 +0200 Subject: [PATCH 2/2] discover-image: pick up subvol ctime, too The btrfs subvol ctime is conceptually what we call the images "mtime", hence expose it like that, since we get it anyway these days. --- src/shared/btrfs-util.c | 1 + src/shared/btrfs-util.h | 3 ++- src/shared/discover-image.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 869eb490f8..349a169107 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -323,6 +323,7 @@ int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) { *ret = (BtrfsSubvolInfo) { .subvol_id = info.treeid, .otime = info.otime.sec * USEC_PER_SEC + (info.otime.nsec / NSEC_PER_USEC), + .ctime = info.ctime.sec * USEC_PER_SEC + (info.ctime.nsec / NSEC_PER_USEC), .read_only = FLAGS_SET(info.flags, BTRFS_SUBVOL_RDONLY), }; diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index 63fc1d0b32..d002618bd8 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -8,7 +8,8 @@ typedef struct BtrfsSubvolInfo { uint64_t subvol_id; - usec_t otime; + usec_t otime; /* creation time */ + usec_t ctime; /* change time */ sd_id128_t uuid; sd_id128_t parent_uuid; diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index d39c3a1356..58777c683b 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -430,7 +430,7 @@ static int image_make( filename, info.read_only || read_only, info.otime, - 0, + info.ctime, ret); if (r < 0) return r;