Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package grub2 for openSUSE:Factory checked in at 2026-08-31 15:56:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/grub2 (Old) and /work/SRC/openSUSE:Factory/.grub2.new.1265 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "grub2" Mon Aug 31 15:56:16 2026 rev:398 rq:1374774 version:2.14 Changes: -------- --- /work/SRC/openSUSE:Factory/grub2/grub2.changes 2026-08-06 16:18:32.932796830 +0200 +++ /work/SRC/openSUSE:Factory/.grub2.new.1265/grub2.changes 2026-08-31 15:56:32.017626951 +0200 @@ -1,0 +2,15 @@ +Thu Aug 27 08:24:15 UTC 2026 - Michael Chang <[email protected]> + +- Backport merged upstream patch + * 0001-fs-btrfs-mark-Btrfs-on-disk-structs-as-GRUB_PACKED.patch + * 0002-fs-btrfs-add-btrfs-info-command.patch + * 0003-fs-btrfs-add-btrfs-list-subvols-command.patch + * 0004-fs-btrfs-add-btrfs-get-default-subvol-command.patch +- Patch rebased + * grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch + * grub2-btrfs-06-subvol-mount.patch + * grub2-btrfs-10-config-directory.patch +- Drop local patch + * grub2-btrfs-09-get-default-subvolume.patch + +------------------------------------------------------------------- Old: ---- grub2-btrfs-09-get-default-subvolume.patch New: ---- 0001-fs-btrfs-mark-Btrfs-on-disk-structs-as-GRUB_PACKED.patch 0002-fs-btrfs-add-btrfs-info-command.patch 0003-fs-btrfs-add-btrfs-list-subvols-command.patch 0004-fs-btrfs-add-btrfs-get-default-subvol-command.patch ----------(Old B)---------- Old:- Drop local patch * grub2-btrfs-09-get-default-subvolume.patch ----------(Old E)---------- ----------(New B)---------- New:- Backport merged upstream patch * 0001-fs-btrfs-mark-Btrfs-on-disk-structs-as-GRUB_PACKED.patch * 0002-fs-btrfs-add-btrfs-info-command.patch New: * 0001-fs-btrfs-mark-Btrfs-on-disk-structs-as-GRUB_PACKED.patch * 0002-fs-btrfs-add-btrfs-info-command.patch * 0003-fs-btrfs-add-btrfs-list-subvols-command.patch New: * 0002-fs-btrfs-add-btrfs-info-command.patch * 0003-fs-btrfs-add-btrfs-list-subvols-command.patch * 0004-fs-btrfs-add-btrfs-get-default-subvol-command.patch New: * 0003-fs-btrfs-add-btrfs-list-subvols-command.patch * 0004-fs-btrfs-add-btrfs-get-default-subvol-command.patch - Patch rebased ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ grub2.spec ++++++ --- /var/tmp/diff_new_pack.Pb5Vh4/_old 2026-08-31 15:56:35.893762184 +0200 +++ /var/tmp/diff_new_pack.Pb5Vh4/_new 2026-08-31 15:56:35.896762289 +0200 @@ -198,6 +198,10 @@ Source18: grub2-check-default.sh Source19: grub2-instdev-fixup.pl Source1000: PATCH_POLICY +Patch: 0001-fs-btrfs-mark-Btrfs-on-disk-structs-as-GRUB_PACKED.patch +Patch: 0002-fs-btrfs-add-btrfs-info-command.patch +Patch: 0003-fs-btrfs-add-btrfs-list-subvols-command.patch +Patch: 0004-fs-btrfs-add-btrfs-get-default-subvol-command.patch Patch: 0001-lvm-allocate-metadata-buffer-from-raw-contents.patch Patch: 0001-net-http-Check-result-of-grub_netbuff_put-in-http_re.patch Patch: 0002-efinet-Add-structures-for-PXE-messages.patch @@ -269,7 +273,6 @@ Patch: grub2-btrfs-06-subvol-mount.patch Patch: grub2-btrfs-07-subvol-fallback.patch Patch: grub2-btrfs-08-workaround-snapshot-menu-default-entry.patch -Patch: grub2-btrfs-09-get-default-subvolume.patch Patch: grub2-btrfs-10-config-directory.patch Patch: grub2-efi-xen-chainload.patch Patch: grub2-efi-xen-cmdline.patch ++++++ 0001-fs-btrfs-mark-Btrfs-on-disk-structs-as-GRUB_PACKED.patch ++++++ >From 28e4c0b4f681e8f49f2d945888fe84ac078cb88e Mon Sep 17 00:00:00 2001 From: Michael Chang <[email protected]> Date: Tue, 16 Jun 2026 12:00:02 +0800 Subject: [PATCH 1/4] fs/btrfs: mark Btrfs on-disk structs as GRUB_PACKED This also aligns them with other Btrfs on-disk structs in this header that already use GRUB_PACKED. Signed-off-by: Michael Chang <[email protected]> Reviewed-by: Neal Gompa <[email protected]> Reviewed-by: Leo Sandoval <[email protected]> Part-of: <https://gitlab.freedesktop.org/gnu-grub/grub/-/merge_requests/160> --- include/grub/btrfs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h index 9d93fb6c1..9aad88754 100644 --- a/include/grub/btrfs.h +++ b/include/grub/btrfs.h @@ -43,7 +43,7 @@ struct grub_btrfs_root_item grub_uint8_t dummy[0xb0]; grub_uint64_t tree; grub_uint64_t inode; -}; +} GRUB_PACKED; struct grub_btrfs_key { @@ -59,13 +59,13 @@ struct grub_btrfs_root_backref grub_uint64_t seqnr; grub_uint16_t n; char name[0]; -}; +} GRUB_PACKED; struct grub_btrfs_inode_ref { grub_uint64_t idxid; grub_uint16_t n; char name[0]; -}; +} GRUB_PACKED; #endif -- 2.55.0 ++++++ 0002-fs-btrfs-add-btrfs-info-command.patch ++++++ >From 1cc790ce376fc77a736e3430b32bf330bff13fb3 Mon Sep 17 00:00:00 2001 From: Michael Chang <[email protected]> Date: Mon, 8 Jun 2026 18:51:40 +0800 Subject: [PATCH 2/4] fs/btrfs: add btrfs-info command Add new "btrfs-info" GRUB command to print basic filesystem metadata for a given device, including label, UUID, total device count, and bytes used. Signed-off-by: Michael Chang <[email protected]> Reviewed-by: Neal Gompa <[email protected]> Reviewed-by: Leo Sandoval <[email protected]> Part-of: <https://gitlab.freedesktop.org/gnu-grub/grub/-/merge_requests/160> --- grub-core/fs/btrfs.c | 103 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 61dd1676a..5948530ff 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -41,6 +41,7 @@ #include <grub/crypto.h> #include <grub/diskfilter.h> #include <grub/safemath.h> +#include <grub/command.h> GRUB_MOD_LICENSE ("GPLv3+"); @@ -79,9 +80,11 @@ struct grub_btrfs_superblock grub_uint64_t generation; grub_uint64_t root_tree; grub_uint64_t chunk_tree; - grub_uint8_t dummy2[0x20]; + grub_uint8_t dummy2[0x18]; + grub_uint64_t bytes_used; grub_uint64_t root_dir_objectid; - grub_uint8_t dummy3[0x41]; + grub_uint64_t num_devices; + grub_uint8_t dummy3[0x39]; struct grub_btrfs_device this_device; char label[0x100]; grub_uint8_t dummy4[0x100]; @@ -2282,6 +2285,20 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) data->tree, file->offset, buf, len); } +static char * +btrfs_unparse_uuid(struct grub_btrfs_data *data) +{ + return grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + grub_be_to_cpu16 (data->sblock.uuid[0]), + grub_be_to_cpu16 (data->sblock.uuid[1]), + grub_be_to_cpu16 (data->sblock.uuid[2]), + grub_be_to_cpu16 (data->sblock.uuid[3]), + grub_be_to_cpu16 (data->sblock.uuid[4]), + grub_be_to_cpu16 (data->sblock.uuid[5]), + grub_be_to_cpu16 (data->sblock.uuid[6]), + grub_be_to_cpu16 (data->sblock.uuid[7])); +} + static grub_err_t grub_btrfs_uuid (grub_device_t device, char **uuid) { @@ -2293,15 +2310,7 @@ grub_btrfs_uuid (grub_device_t device, char **uuid) if (!data) return grub_errno; - *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - grub_be_to_cpu16 (data->sblock.uuid[0]), - grub_be_to_cpu16 (data->sblock.uuid[1]), - grub_be_to_cpu16 (data->sblock.uuid[2]), - grub_be_to_cpu16 (data->sblock.uuid[3]), - grub_be_to_cpu16 (data->sblock.uuid[4]), - grub_be_to_cpu16 (data->sblock.uuid[5]), - grub_be_to_cpu16 (data->sblock.uuid[6]), - grub_be_to_cpu16 (data->sblock.uuid[7])); + *uuid = btrfs_unparse_uuid (data); grub_btrfs_unmount (data); @@ -2431,6 +2440,71 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), } #endif +static grub_err_t +grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, + char **argv) +{ + grub_device_t dev; + char *devname; + struct grub_btrfs_data *data; + char *uuid; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount (dev); + if (!data) + { + grub_device_close(dev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to open fs"); + } + + if (*data->sblock.label != '\0') + { + char *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label)); + if (!label) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return grub_errno; + } + grub_printf("Label: '%s' ", label); + grub_free (label); + } + else + grub_printf("Label: none "); + + uuid = btrfs_unparse_uuid (data); + if (!uuid) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return grub_errno; + } + + grub_printf (" uuid: %s\n\tTotal devices %" PRIuGRUB_UINT64_T + " FS bytes used %" PRIuGRUB_UINT64_T "\n", + uuid, + grub_le_to_cpu64 (data->sblock.num_devices), + grub_le_to_cpu64 (data->sblock.bytes_used)); + + grub_free (uuid); + grub_btrfs_unmount (data); + grub_device_close (dev); + + return GRUB_ERR_NONE; +} + static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, @@ -2446,13 +2520,20 @@ static struct grub_fs grub_btrfs_fs = { #endif }; +static grub_command_t cmd_info; + GRUB_MOD_INIT (btrfs) { grub_btrfs_fs.mod = mod; grub_fs_register (&grub_btrfs_fs); + cmd_info = grub_register_command ("btrfs-info", + grub_cmd_btrfs_info, + "DEVICE", + "Print BtrFS info about DEVICE."); } GRUB_MOD_FINI (btrfs) { grub_fs_unregister (&grub_btrfs_fs); + grub_unregister_command (cmd_info); } -- 2.55.0 ++++++ 0003-fs-btrfs-add-btrfs-list-subvols-command.patch ++++++ >From 38136c6a8aa0c8d31a4dbf6f21efc432c14b3506 Mon Sep 17 00:00:00 2001 From: Michael Chang <[email protected]> Date: Tue, 16 Jun 2026 11:33:23 +0800 Subject: [PATCH 3/4] fs/btrfs: add btrfs-list-subvols command Add a new "btrfs-list-subvols" GRUB command to enumerate subvolumes on a Btrfs device. Support output filtering and formatting with options for path-only, id-only, read-only, and storing output in an environment variable. Signed-off-by: Michael Chang <[email protected]> Reviewed-by: Neal Gompa <[email protected]> Reviewed-by: Leo Sandoval <[email protected]> Part-of: <https://gitlab.freedesktop.org/gnu-grub/grub/-/merge_requests/160> --- grub-core/fs/btrfs.c | 412 +++++++++++++++++++++++++++++++++++++++++++ include/grub/btrfs.h | 6 +- 2 files changed, 417 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 5948530ff..5714642af 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -42,6 +42,8 @@ #include <grub/diskfilter.h> #include <grub/safemath.h> #include <grub/command.h> +#include <grub/env.h> +#include <grub/extcmd.h> GRUB_MOD_LICENSE ("GPLv3+"); @@ -194,6 +196,14 @@ struct grub_btrfs_leaf_descriptor } *data; }; +struct grub_btrfs_root_ref +{ + grub_uint64_t dirid; + grub_uint64_t sequence; + grub_uint16_t name_len; + const char name[0]; +} GRUB_PACKED; + struct grub_btrfs_time { grub_int64_t sec; @@ -239,6 +249,10 @@ struct grub_btrfs_extent_data #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 +#define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL +#define GRUB_BTRFS_ROOT_REF_KEY 156 +#define GRUB_BTRFS_ROOT_ITEM_KEY 132 + static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2 }; @@ -248,6 +262,13 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, void *buf, grub_size_t size, int recursion_depth); +static grub_err_t +grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data, + grub_uint64_t child_id, + const char *child_path, + grub_uint64_t *parent_id, + char **path_out); + static grub_err_t read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) { @@ -1790,6 +1811,103 @@ get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, return GRUB_ERR_NONE; } +static grub_err_t +find_pathname(struct grub_btrfs_data *data, grub_uint64_t objectid, + grub_uint64_t fs_root, const char *name, char **pathname) +{ + grub_err_t err; + struct grub_btrfs_key key = { + .object_id = grub_cpu_to_le64 (objectid), + .type = GRUB_BTRFS_ITEM_TYPE_INODE_REF, + .offset = 0ULL, + }; + struct grub_btrfs_key key_out; + struct grub_btrfs_leaf_descriptor desc; + char *p = grub_strdup (name); + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_size_t alloc = grub_strlen(name) + 1; + + err = lower_bound(data, &key, &key_out, fs_root, + &elemaddr, &elemsize, &desc, 0); + if (err) + { + free_iterator(&desc); + return grub_error(err, "lower_bound caught %d\n", err); + } + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + free_iterator(&desc); + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + { + return grub_error(GRUB_ERR_FILE_NOT_FOUND, + "Can't find inode ref for {%"PRIuGRUB_UINT64_T + ", %u, %"PRIuGRUB_UINT64_T"} %"PRIuGRUB_UINT64_T + "/%"PRIuGRUB_SIZE"\n", + key_out.object_id, key_out.type, + key_out.offset, elemaddr, elemsize); + } + + while (key_out.type == GRUB_BTRFS_ITEM_TYPE_INODE_REF && + key_out.object_id != key_out.offset) + { + struct grub_btrfs_inode_ref *inode_ref; + char *new; + grub_size_t sz; + + if (grub_add (elemsize, 1, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + + inode_ref = grub_malloc(sz); + if (!inode_ref) + return grub_error(GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate memory for inode_ref (%"PRIuGRUB_SIZE")\n", elemsize); + + err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0); + if (err) + return grub_error(err, "read_logical caught %d\n", err); + + if (grub_add (grub_le_to_cpu16 (inode_ref->n), 2, &sz) || + grub_add (alloc, sz, &alloc)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + + new = grub_malloc(alloc); + if (!new) + return grub_error(GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate memory for name (%"PRIuGRUB_SIZE")\n", alloc); + + grub_memcpy(new, inode_ref->name, grub_le_to_cpu16 (inode_ref->n)); + if (p) + { + new[grub_le_to_cpu16 (inode_ref->n)] = '/'; + grub_strcpy (new + grub_le_to_cpu16 (inode_ref->n) + 1, p); + grub_free(p); + } + else + new[grub_le_to_cpu16 (inode_ref->n)] = 0; + grub_free(inode_ref); + + p = new; + key.object_id = key_out.offset; + err = lower_bound(data, &key, &key_out, fs_root, &elemaddr, + &elemsize, &desc, 0); + if (err) + { + free_iterator(&desc); + return grub_error(err, "lower_bound caught %d\n", err); + } + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + next(data, &desc, &elemaddr, &elemsize, &key_out); + free_iterator(&desc); + } + + *pathname = p; + return 0; +} + static grub_err_t find_path (struct grub_btrfs_data *data, const char *path, struct grub_btrfs_key *key, @@ -2505,6 +2623,293 @@ grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, return GRUB_ERR_NONE; } +static grub_err_t +get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, + grub_uint64_t *fs_root, grub_uint64_t *flags) +{ + grub_err_t err; + struct grub_btrfs_key key_in = { + .object_id = grub_cpu_to_le64 (objectid), + .type = GRUB_BTRFS_ROOT_ITEM_KEY, + .offset = grub_cpu_to_le64 (offset), + }, key_out; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_root_item ri; + + err = lower_bound(data, &key_in, &key_out, tree, + &elemaddr, &elemsize, NULL, 0); + if (err) + return err; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM || elemaddr == 0) + { + return grub_error(GRUB_ERR_FILE_NOT_FOUND, + N_("can't find fs root for subvol %"PRIuGRUB_UINT64_T"\n"), + key_in.object_id); + } + + err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri), 0); + if (err) + return err; + + *fs_root = ri.tree; + + if (flags) + *flags = grub_le_to_cpu64 (ri.flags); + + return GRUB_ERR_NONE; +} + +static const struct grub_arg_option opt_list_subvols[] = { + {"output", 'o', 0, N_("Output to a variable instead of the console."), + N_("VARNAME"), ARG_TYPE_STRING}, + {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0}, + {"id-only", 'n', 0, N_("Show only the id of the subvolume."), 0, 0}, + {"read-only", 'r', 0, N_("Show only read-only subvolume."), 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + +static grub_err_t +grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + struct grub_btrfs_data *data = NULL; + grub_device_t dev = NULL; + char *devname; + grub_uint64_t tree; + struct grub_btrfs_key key_in = { + .object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), + .type = GRUB_BTRFS_ROOT_REF_KEY, + .offset = 0ULL, + }, key_out; + struct grub_btrfs_leaf_descriptor desc = {0}; + grub_disk_addr_t elemaddr; + grub_uint64_t fs_root = 0; + grub_size_t elemsize; + int r = 0; + grub_err_t err; + int print = 1; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + int read_only = ctxt->state[3].set; + char *varname = NULL; + char *output = NULL; + + if (ctxt->state[0].set) { + varname = ctxt->state[0].arg; + print = 0; + } + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount(dev); + if (!data) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "could not open device"); + } + + tree = data->sblock.root_tree; + err = get_fs_root(data, tree, GRUB_BTRFS_FS_TREE_OBJECTID, + 0ULL, &fs_root, NULL); + if (err) + goto out; + + err = lower_bound(data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + + if (err) + goto out; + + do + { + char *p = NULL; + grub_uint64_t id = grub_le_to_cpu64 (key_out.offset); + grub_uint64_t flags; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF || elemaddr == 0) + continue; + + err = get_fs_root (data, tree, id, (grub_uint64_t) -1, &fs_root, &flags); + if (err != GRUB_ERR_NONE) + { + grub_print_error (); + continue; + } + + if (read_only && !(flags & GRUB_BTRFS_ROOT_SUBVOL_RDONLY)) + continue; + + if (id == GRUB_BTRFS_ROOT_VOL_OBJECTID) + p = grub_strdup (""); + else + while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID) + { + grub_uint64_t parent_id; + char *path_out; + + err = grub_btrfs_get_parent_subvol_path (data, id, p, &parent_id, &path_out); + grub_free (p); + if (err) + { + grub_print_error (); + goto out; + } + p = path_out; + id = parent_id; + } + + if (print) + { + if (num_only) + grub_printf("ID %"PRIuGRUB_UINT64_T"\n", grub_le_to_cpu64 (key_out.offset)); + else if (path_only) + grub_printf("%s\n", p); + else + grub_printf("ID %"PRIuGRUB_UINT64_T" path %s\n", grub_le_to_cpu64 (key_out.offset), p); + } + else + { + char *old = output; + if (num_only) + output = grub_xasprintf("%s%"PRIuGRUB_UINT64_T"\n", + old ?: "", grub_le_to_cpu64 (key_out.offset)); + else if (path_only) + output = grub_xasprintf("%s%s\n", old ?: "", p); + else + output = grub_xasprintf("%sID %"PRIuGRUB_UINT64_T" path %s\n", + old ?: "", grub_le_to_cpu64 (key_out.offset), p); + if (old) + grub_free(old); + } + + grub_free (p); + + } while ((r = next(data, &desc, &elemaddr, &elemsize, &key_out)) > 0); + + if (output) + grub_env_set(varname, output); + +out: + grub_free (output); + free_iterator(&desc); + + if (data) + grub_btrfs_unmount(data); + + if (dev) + grub_device_close (dev); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data, + grub_uint64_t child_id, + const char *child_path, + grub_uint64_t *parent_id, + char **path_out) +{ + grub_uint64_t fs_root = 0; + struct grub_btrfs_key key_in = { + .object_id = grub_cpu_to_le64 (child_id), + .type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF, + .offset = 0ULL, + }, key_out; + struct grub_btrfs_root_ref *ref; + char *buf; + struct grub_btrfs_leaf_descriptor desc; + grub_size_t elemsize; + grub_disk_addr_t elemaddr; + grub_err_t err; + char *parent_path = NULL; + grub_size_t sz; + + *parent_id = 0; + *path_out = 0; + + err = lower_bound(data, &key_in, &key_out, data->sblock.root_tree, + &elemaddr, &elemsize, &desc, 0); + if (err) + return err; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF || elemaddr == 0) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF) + { + free_iterator(&desc); + return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root backrefs")); + } + + if (grub_add (elemsize, 1, &sz)) + { + free_iterator(&desc); + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + } + + buf = grub_malloc(sz); + if (!buf) + { + free_iterator(&desc); + return grub_errno; + } + + err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + buf[elemsize] = 0; + ref = (struct grub_btrfs_root_ref *)buf; + + err = get_fs_root(data, data->sblock.root_tree, grub_le_to_cpu64 (key_out.offset), + (grub_uint64_t) -1, &fs_root, NULL); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + err = find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + if (child_path) + { + *path_out = grub_xasprintf ("%s/%s", parent_path, child_path); + grub_free (parent_path); + } + else + *path_out = parent_path; + + *parent_id = grub_le_to_cpu64 (key_out.offset); + + grub_free(buf); + free_iterator(&desc); + return GRUB_ERR_NONE; +} + static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, @@ -2521,6 +2926,7 @@ static struct grub_fs grub_btrfs_fs = { }; static grub_command_t cmd_info; +static grub_extcmd_t cmd_list_subvols; GRUB_MOD_INIT (btrfs) { @@ -2530,10 +2936,16 @@ GRUB_MOD_INIT (btrfs) grub_cmd_btrfs_info, "DEVICE", "Print BtrFS info about DEVICE."); + cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", + grub_cmd_btrfs_list_subvols, 0, + "[-p|-n] [-r] [-o var] DEVICE", + "Print list of BtrFS subvolumes on " + "DEVICE.", opt_list_subvols); } GRUB_MOD_FINI (btrfs) { grub_fs_unregister (&grub_btrfs_fs); grub_unregister_command (cmd_info); + grub_unregister_extcmd (cmd_list_subvols); } diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h index 9aad88754..19de49311 100644 --- a/include/grub/btrfs.h +++ b/include/grub/btrfs.h @@ -29,6 +29,7 @@ enum GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84, GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90, GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8, + GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c, GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4 }; @@ -38,11 +39,14 @@ enum GRUB_BTRFS_TREE_ROOT_OBJECTID = 0x100, }; +#define GRUB_BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0) + struct grub_btrfs_root_item { grub_uint8_t dummy[0xb0]; grub_uint64_t tree; - grub_uint64_t inode; + grub_uint8_t dummy2[0x18]; + grub_uint64_t flags; } GRUB_PACKED; struct grub_btrfs_key -- 2.55.0 ++++++ 0004-fs-btrfs-add-btrfs-get-default-subvol-command.patch ++++++ >From aead2255d307ee00756c8cc19e9aecd166ca282e Mon Sep 17 00:00:00 2001 From: Michael Chang <[email protected]> Date: Tue, 16 Jun 2026 15:16:12 +0800 Subject: [PATCH 4/4] fs/btrfs: add btrfs-get-default-subvol command Add a new Btrfs command, "btrfs-get-default-subvol", to print the filesystem default subvolume. The command supports printing only the path with `-p`, printing only the subvolume ID with `-n`, and storing output in an environment variable with `-o VAR`. Signed-off-by: Michael Chang <[email protected]> Reviewed-by: Neal Gompa <[email protected]> Reviewed-by: Leo Sandoval <[email protected]> Part-of: <https://gitlab.freedesktop.org/gnu-grub/grub/-/merge_requests/160> --- grub-core/fs/btrfs.c | 199 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 5714642af..1cd842ec0 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2671,6 +2671,14 @@ static const struct grub_arg_option opt_list_subvols[] = { {0, 0, 0, 0, 0, 0} }; +static const struct grub_arg_option opt_default_subvol[] = { + {"output", 'o', 0, N_("Output to a variable instead of the console."), + N_("VARNAME"), ARG_TYPE_STRING}, + {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0}, + {"id-only", 'n', 0, N_("Show only the id of the subvolume."), 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + static grub_err_t grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, int argc, char **argv) @@ -2910,6 +2918,190 @@ grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data, return GRUB_ERR_NONE; } +static grub_err_t +grub_btrfs_get_default_subvolume_id (struct grub_btrfs_data *data, grub_uint64_t *id) +{ + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_key key, key_out; + struct grub_btrfs_dir_item *direl = NULL; + const char *ctoken = "default"; + grub_size_t ctokenlen = sizeof ("default") - 1; + grub_size_t sz; + + *id = 0; + key.object_id = data->sblock.root_dir_objectid; + key.type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key.offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); + err = lower_bound (data, &key, &key_out, data->sblock.root_tree, &elemaddr, &elemsize, + NULL, 0); + if (err) + return err; + + if (key_cmp (&key, &key_out) != 0) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + + struct grub_btrfs_dir_item *cdirel; + + if (grub_add (elemsize, 1, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + + direl = grub_malloc (sz); + if (!direl) + return grub_errno; + + err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0); + if (err) + { + grub_free (direl); + return err; + } + for (cdirel = direl; + (grub_uint8_t *) cdirel - (grub_uint8_t *) direl + < (grub_ssize_t) elemsize; + cdirel = (void *) ((grub_uint8_t *) (direl + 1) + + grub_le_to_cpu16 (cdirel->n) + + grub_le_to_cpu16 (cdirel->m))) + { + if (ctokenlen == grub_le_to_cpu16 (cdirel->n) + && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0) + break; + } + if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl + >= (grub_ssize_t) elemsize) + { + grub_free (direl); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + return err; + } + + if (cdirel->key.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM) + { + grub_free (direl); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + return err; + } + + *id = grub_le_to_cpu64 (cdirel->key.object_id); + grub_free (direl); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_btrfs_get_default_subvol (const char *name, grub_uint64_t *ret_subvolid, char **ret_subvol) +{ + char *devname; + grub_device_t dev; + struct grub_btrfs_data *data; + grub_err_t err; + grub_uint64_t id; + char *subvol = NULL; + grub_uint64_t subvolid = 0; + + devname = grub_file_get_device_name(name); + if (!devname) + return grub_errno; + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount(dev); + if (!data) + { + grub_device_close (dev); + grub_dprintf ("btrfs", "failed to open fs\n"); + return grub_errno; + } + + err = grub_btrfs_get_default_subvolume_id (data, &subvolid); + if (err) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return err; + } + + id = subvolid; + + if (id == GRUB_BTRFS_ROOT_VOL_OBJECTID) + subvol = grub_strdup (""); + else + { + while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID) + { + grub_uint64_t parent_id; + char *path_out; + + err = grub_btrfs_get_parent_subvol_path (data, id, subvol, &parent_id, &path_out); + if (err) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return err; + } + + grub_free (subvol); + subvol = path_out; + id = parent_id; + } + } + + if (ret_subvolid) + *ret_subvolid = subvolid; + if (ret_subvol) + *ret_subvol = subvol; + + grub_btrfs_unmount (data); + grub_device_close (dev); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + grub_err_t err; + char *subvol = NULL; + grub_uint64_t subvolid = 0; + char *varname = NULL; + char *output = NULL; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + + if (ctxt->state[0].set) + varname = ctxt->state[0].arg; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + if ((err = grub_btrfs_get_default_subvol (argv[0], &subvolid, &subvol)) != GRUB_ERR_NONE) + { + if (err == GRUB_ERR_BAD_FS) + err = grub_errno = GRUB_ERR_NONE; + return err; + } + + if (num_only && path_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol); + else if (num_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T, subvolid); + else + output = grub_xasprintf ("/%s", subvol); + + if (varname) + grub_env_set(varname, output); + else + grub_printf ("%s\n", output); + + grub_free (output); + grub_free (subvol); + + return GRUB_ERR_NONE; +} + static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, @@ -2927,6 +3119,7 @@ static struct grub_fs grub_btrfs_fs = { static grub_command_t cmd_info; static grub_extcmd_t cmd_list_subvols; +static grub_extcmd_t cmd_get_default_subvol; GRUB_MOD_INIT (btrfs) { @@ -2941,6 +3134,11 @@ GRUB_MOD_INIT (btrfs) "[-p|-n] [-r] [-o var] DEVICE", "Print list of BtrFS subvolumes on " "DEVICE.", opt_list_subvols); + cmd_get_default_subvol = grub_register_extcmd("btrfs-get-default-subvol", + grub_cmd_btrfs_get_default_subvol, 0, + "[-p|-n] [-o var] DEVICE", + "Print default BtrFS subvolume on " + "DEVICE.", opt_default_subvol); } GRUB_MOD_FINI (btrfs) @@ -2948,4 +3146,5 @@ GRUB_MOD_FINI (btrfs) grub_fs_unregister (&grub_btrfs_fs); grub_unregister_command (cmd_info); grub_unregister_extcmd (cmd_list_subvols); + grub_unregister_extcmd (cmd_get_default_subvol); } -- 2.55.0 ++++++ grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch ++++++ --- /var/tmp/diff_new_pack.Pb5Vh4/_old 2026-08-31 15:56:36.550785107 +0200 +++ /var/tmp/diff_new_pack.Pb5Vh4/_new 2026-08-31 15:56:36.553785212 +0200 @@ -36,31 +36,7 @@ --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c -@@ -41,6 +41,9 @@ - #include <grub/crypto.h> - #include <grub/diskfilter.h> - #include <grub/safemath.h> -+#include <grub/command.h> -+#include <grub/env.h> -+#include <grub/extcmd.h> - - GRUB_MOD_LICENSE ("GPLv3+"); - -@@ -79,9 +82,11 @@ - grub_uint64_t generation; - grub_uint64_t root_tree; - grub_uint64_t chunk_tree; -- grub_uint8_t dummy2[0x20]; -+ grub_uint8_t dummy2[0x18]; -+ grub_uint64_t bytes_used; - grub_uint64_t root_dir_objectid; -- grub_uint8_t dummy3[0x41]; -+ grub_uint64_t num_devices; -+ grub_uint8_t dummy3[0x39]; - struct grub_btrfs_device this_device; - char label[0x100]; - grub_uint8_t dummy4[0x100]; -@@ -121,6 +126,7 @@ +@@ -126,6 +126,7 @@ grub_uint64_t exttree; grub_size_t extsize; struct grub_btrfs_extent_data *extent; @@ -68,44 +44,29 @@ }; struct grub_btrfs_chunk_item -@@ -191,6 +197,14 @@ - } *data; - }; - -+struct grub_btrfs_root_ref -+{ -+ grub_uint64_t dirid; -+ grub_uint64_t sequence; -+ grub_uint16_t name_len; -+ const char name[0]; -+} __attribute__ ((packed)); -+ - struct grub_btrfs_time - { - grub_int64_t sec; -@@ -236,6 +250,14 @@ +@@ -249,10 +250,14 @@ #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 +#define GRUB_BTRFS_ROOT_TREE_OBJECTID 1ULL -+#define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL -+#define GRUB_BTRFS_ROOT_REF_KEY 156 -+#define GRUB_BTRFS_ROOT_ITEM_KEY 132 -+ + #define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL + #define GRUB_BTRFS_ROOT_REF_KEY 156 + #define GRUB_BTRFS_ROOT_ITEM_KEY 132 + +static grub_uint64_t btrfs_default_subvolid = 0; +static char *btrfs_default_subvol = NULL; + static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2 }; -@@ -1252,6 +1274,62 @@ +@@ -1276,6 +1281,62 @@ return GRUB_ERR_NONE; } +static grub_err_t +get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, -+ grub_uint64_t *fs_root); ++ grub_uint64_t *fs_root, grub_uint64_t *flags); + +static grub_err_t +lookup_root_by_id(struct grub_btrfs_data *data, grub_uint64_t id) @@ -113,7 +74,7 @@ + grub_err_t err; + grub_uint64_t tree; + -+ err = get_fs_root(data, data->sblock.root_tree, id, -1, &tree); ++ err = get_fs_root (data, data->sblock.root_tree, id, -1, &tree, NULL); + if (!err) + data->fs_tree = tree; + return err; @@ -161,7 +122,7 @@ static struct grub_btrfs_data * grub_btrfs_mount (grub_device_t dev) { -@@ -1287,6 +1365,13 @@ +@@ -1311,6 +1372,13 @@ data->devices_attached[0].dev = dev; data->devices_attached[0].id = data->sblock.this_device.device_id; @@ -175,106 +136,7 @@ return data; } -@@ -1788,6 +1873,98 @@ - } - - static grub_err_t -+find_pathname(struct grub_btrfs_data *data, grub_uint64_t objectid, -+ grub_uint64_t fs_root, const char *name, char **pathname) -+{ -+ grub_err_t err; -+ struct grub_btrfs_key key = { -+ .object_id = objectid, -+ .type = GRUB_BTRFS_ITEM_TYPE_INODE_REF, -+ .offset = 0, -+ }; -+ struct grub_btrfs_key key_out; -+ struct grub_btrfs_leaf_descriptor desc; -+ char *p = grub_strdup (name); -+ grub_disk_addr_t elemaddr; -+ grub_size_t elemsize; -+ grub_size_t alloc = grub_strlen(name) + 1; -+ -+ err = lower_bound(data, &key, &key_out, fs_root, -+ &elemaddr, &elemsize, &desc, 0); -+ if (err) -+ return grub_error(err, "lower_bound caught %d\n", err); -+ -+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) -+ next(data, &desc, &elemaddr, &elemsize, &key_out); -+ -+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) -+ { -+ return grub_error(GRUB_ERR_FILE_NOT_FOUND, -+ "Can't find inode ref for {%"PRIuGRUB_UINT64_T -+ ", %u, %"PRIuGRUB_UINT64_T"} %"PRIuGRUB_UINT64_T -+ "/%"PRIuGRUB_SIZE"\n", -+ key_out.object_id, key_out.type, -+ key_out.offset, elemaddr, elemsize); -+ } -+ -+ -+ while (key_out.type == GRUB_BTRFS_ITEM_TYPE_INODE_REF && -+ key_out.object_id != key_out.offset) { -+ struct grub_btrfs_inode_ref *inode_ref; -+ char *new; -+ grub_size_t sz; -+ -+ if (grub_add (elemsize, 1, &sz)) -+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); -+ -+ inode_ref = grub_malloc(sz); -+ if (!inode_ref) -+ return grub_error(GRUB_ERR_OUT_OF_MEMORY, -+ "couldn't allocate memory for inode_ref (%"PRIuGRUB_SIZE")\n", elemsize); -+ -+ err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0); -+ if (err) -+ return grub_error(err, "read_logical caught %d\n", err); -+ -+ if (grub_add (grub_le_to_cpu16 (inode_ref->n), 2, &sz) || -+ grub_add (alloc, sz, &alloc)) -+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); -+ -+ new = grub_malloc(alloc); -+ if (!new) -+ return grub_error(GRUB_ERR_OUT_OF_MEMORY, -+ "couldn't allocate memory for name (%"PRIuGRUB_SIZE")\n", alloc); -+ -+ grub_memcpy(new, inode_ref->name, grub_le_to_cpu16 (inode_ref->n)); -+ if (p) -+ { -+ new[grub_le_to_cpu16 (inode_ref->n)] = '/'; -+ grub_strcpy (new + grub_le_to_cpu16 (inode_ref->n) + 1, p); -+ grub_free(p); -+ } -+ else -+ new[grub_le_to_cpu16 (inode_ref->n)] = 0; -+ grub_free(inode_ref); -+ -+ p = new; -+ -+ key.object_id = key_out.offset; -+ -+ err = lower_bound(data, &key, &key_out, fs_root, &elemaddr, -+ &elemsize, &desc, 0); -+ if (err) -+ return grub_error(err, "lower_bound caught %d\n", err); -+ -+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) -+ next(data, &desc, &elemaddr, &elemsize, &key_out); -+ -+ } -+ -+ *pathname = p; -+ return 0; -+} -+ -+static grub_err_t - find_path (struct grub_btrfs_data *data, - const char *path, struct grub_btrfs_key *key, - grub_uint64_t *tree, grub_uint8_t *type) -@@ -1806,14 +1983,26 @@ +@@ -1927,14 +1995,26 @@ unsigned symlinks_max = 32; grub_size_t sz; @@ -305,14 +167,18 @@ while (1) { while (path[0] == '/') -@@ -2000,13 +2189,25 @@ +@@ -2121,13 +2201,25 @@ path = path_alloc = tmp; if (path[0] == '/') { - err = get_root (data, key, tree, type); - if (err) + if (data->fs_tree) -+ { + { +- grub_free (direl); +- grub_free (path_alloc); +- grub_free (origpath); +- return err; + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ @@ -321,11 +187,7 @@ + key->offset = 0; + } + else - { -- grub_free (direl); -- grub_free (path_alloc); -- grub_free (origpath); -- return err; ++ { + err = get_root (data, key, tree, type); + if (err) + { @@ -337,305 +199,10 @@ } } continue; -@@ -2282,6 +2483,20 @@ - data->tree, file->offset, buf, len); - } - -+static char * -+btrfs_unparse_uuid(struct grub_btrfs_data *data) -+{ -+ return grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", -+ grub_be_to_cpu16 (data->sblock.uuid[0]), -+ grub_be_to_cpu16 (data->sblock.uuid[1]), -+ grub_be_to_cpu16 (data->sblock.uuid[2]), -+ grub_be_to_cpu16 (data->sblock.uuid[3]), -+ grub_be_to_cpu16 (data->sblock.uuid[4]), -+ grub_be_to_cpu16 (data->sblock.uuid[5]), -+ grub_be_to_cpu16 (data->sblock.uuid[6]), -+ grub_be_to_cpu16 (data->sblock.uuid[7])); -+} -+ - static grub_err_t - grub_btrfs_uuid (grub_device_t device, char **uuid) - { -@@ -2293,15 +2508,7 @@ - if (!data) - return grub_errno; - -- *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", -- grub_be_to_cpu16 (data->sblock.uuid[0]), -- grub_be_to_cpu16 (data->sblock.uuid[1]), -- grub_be_to_cpu16 (data->sblock.uuid[2]), -- grub_be_to_cpu16 (data->sblock.uuid[3]), -- grub_be_to_cpu16 (data->sblock.uuid[4]), -- grub_be_to_cpu16 (data->sblock.uuid[5]), -- grub_be_to_cpu16 (data->sblock.uuid[6]), -- grub_be_to_cpu16 (data->sblock.uuid[7])); -+ *uuid = btrfs_unparse_uuid(data); - - grub_btrfs_unmount (data); - -@@ -2431,6 +2638,253 @@ - } - #endif - -+static grub_err_t -+grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, -+ char **argv) -+{ -+ grub_device_t dev; -+ char *devname; -+ struct grub_btrfs_data *data; -+ char *uuid; -+ -+ if (argc < 1) -+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); -+ -+ devname = grub_file_get_device_name(argv[0]); -+ -+ if (!devname) -+ return grub_errno; -+ -+ dev = grub_device_open (devname); -+ grub_free (devname); -+ if (!dev) -+ return grub_errno; -+ -+ data = grub_btrfs_mount (dev); -+ if (!data) -+ { -+ grub_device_close(dev); -+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to open fs"); -+ } -+ -+ if (*data->sblock.label != '\0') -+ grub_printf("Label: '%s' ", data->sblock.label); -+ else -+ grub_printf("Label: none "); -+ -+ uuid = btrfs_unparse_uuid(data); -+ -+ grub_printf(" uuid: %s\n\tTotal devices %" PRIuGRUB_UINT64_T -+ " FS bytes used %" PRIuGRUB_UINT64_T "\n", -+ uuid, grub_cpu_to_le64(data->sblock.num_devices), -+ grub_cpu_to_le64(data->sblock.bytes_used)); -+ -+ grub_btrfs_unmount (data); -+ -+ return 0; -+} -+ -+static grub_err_t -+get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, -+ grub_uint64_t objectid, grub_uint64_t offset, -+ grub_uint64_t *fs_root) -+{ -+ grub_err_t err; -+ struct grub_btrfs_key key_in = { -+ .object_id = objectid, -+ .type = GRUB_BTRFS_ROOT_ITEM_KEY, -+ .offset = offset, -+ }, key_out; -+ struct grub_btrfs_leaf_descriptor desc; -+ grub_disk_addr_t elemaddr; -+ grub_size_t elemsize; -+ struct grub_btrfs_root_item ri; -+ -+ err = lower_bound(data, &key_in, &key_out, tree, -+ &elemaddr, &elemsize, &desc, 0); -+ -+ if (err) -+ return err; -+ -+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM || elemaddr == 0) -+ return grub_error(GRUB_ERR_FILE_NOT_FOUND, -+ N_("can't find fs root for subvol %"PRIuGRUB_UINT64_T"\n"), -+ key_in.object_id); -+ -+ err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri), 0); -+ if (err) -+ return err; -+ -+ *fs_root = ri.tree; -+ -+ return GRUB_ERR_NONE; -+} -+ -+static const struct grub_arg_option options[] = { -+ {"output", 'o', 0, N_("Output to a variable instead of the console."), -+ N_("VARNAME"), ARG_TYPE_STRING}, -+ {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0}, -+ {"id-only", 'i', 0, N_("Show only the id of the subvolume."), 0, 0}, -+ {0, 0, 0, 0, 0, 0} -+}; -+ -+static grub_err_t -+grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, -+ int argc, char **argv) -+{ -+ struct grub_btrfs_data *data; -+ grub_device_t dev; -+ char *devname; -+ grub_uint64_t tree; -+ struct grub_btrfs_key key_in = { -+ .object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), -+ .type = GRUB_BTRFS_ROOT_REF_KEY, -+ .offset = 0, -+ }, key_out; -+ struct grub_btrfs_leaf_descriptor desc; -+ grub_disk_addr_t elemaddr; -+ grub_uint64_t fs_root = 0; -+ grub_size_t elemsize; -+ grub_size_t allocated = 0; -+ int r = 0; -+ grub_err_t err; -+ char *buf = NULL; -+ int print = 1; -+ int path_only = ctxt->state[1].set; -+ int num_only = ctxt->state[2].set; -+ char *varname = NULL; -+ char *output = NULL; -+ -+ if (ctxt->state[0].set) { -+ varname = ctxt->state[0].arg; -+ print = 0; -+ } -+ -+ if (argc < 1) -+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); -+ -+ devname = grub_file_get_device_name(argv[0]); -+ if (!devname) -+ return grub_errno; -+ -+ dev = grub_device_open (devname); -+ grub_free (devname); -+ if (!dev) -+ return grub_errno; -+ -+ data = grub_btrfs_mount(dev); -+ if (!data) -+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "could not open device"); -+ -+ tree = data->sblock.root_tree; -+ err = get_fs_root(data, tree, grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), -+ 0, &fs_root); -+ if (err) -+ goto out; -+ -+ err = lower_bound(data, &key_in, &key_out, tree, -+ &elemaddr, &elemsize, &desc, 0); -+ -+ if (err) -+ { -+ grub_btrfs_unmount(data); -+ return err; -+ } -+ -+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF || elemaddr == 0) -+ { -+ r = next(data, &desc, &elemaddr, &elemsize, &key_out); -+ } -+ -+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) { -+ err = GRUB_ERR_FILE_NOT_FOUND; -+ grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root refs")); -+ goto out; -+ } -+ -+ do -+ { -+ struct grub_btrfs_root_ref *ref; -+ char *p = NULL; -+ -+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) -+ { -+ r = 0; -+ break; -+ } -+ -+ if (elemsize > allocated) -+ { -+ grub_size_t sz; -+ -+ grub_free(buf); -+ -+ if (grub_mul (elemsize, 2, &allocated) || -+ grub_add (allocated, 1, &sz)) -+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); -+ -+ buf = grub_malloc(sz); -+ if (!buf) -+ { -+ r = -grub_errno; -+ break; -+ } -+ } -+ ref = (struct grub_btrfs_root_ref *)buf; -+ -+ err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); -+ if (err) -+ { -+ r = -err; -+ break; -+ } -+ buf[elemsize] = 0; -+ -+ err = find_pathname(data, ref->dirid, fs_root, ref->name, &p); -+ if (err) -+ { -+ r = -err; -+ break; -+ } -+ -+ if (print) -+ { -+ if (num_only) -+ grub_printf("ID %"PRIuGRUB_UINT64_T"\n", key_out.offset); -+ else if (path_only) -+ grub_printf("%s\n", p); -+ else -+ grub_printf("ID %"PRIuGRUB_UINT64_T" path %s\n", key_out.offset, p); -+ } else { -+ char *old = output; -+ if (num_only) -+ output = grub_xasprintf("%s%"PRIuGRUB_UINT64_T"\n", -+ old ?: "", key_out.offset); -+ else if (path_only) -+ output = grub_xasprintf("%s%s\n", old ?: "", p); -+ else -+ output = grub_xasprintf("%sID %"PRIuGRUB_UINT64_T" path %s\n", -+ old ?: "", key_out.offset, p); -+ -+ if (old) -+ grub_free(old); -+ } -+ -+ r = next(data, &desc, &elemaddr, &elemsize, &key_out); -+ } while(r > 0); -+ -+ if (output) -+ grub_env_set(varname, output); -+ -+out: -+ free_iterator(&desc); -+ grub_btrfs_unmount(data); -+ -+ grub_device_close (dev); -+ -+ return 0; -+} -+ - static struct grub_fs grub_btrfs_fs = { - .name = "btrfs", - .fs_dir = grub_btrfs_dir, -@@ -2446,13 +2900,89 @@ - #endif - }; +@@ -3117,6 +3209,61 @@ + static grub_extcmd_t cmd_list_subvols; + static grub_extcmd_t cmd_get_default_subvol; -+static grub_command_t cmd_info; -+static grub_extcmd_t cmd_list_subvols; -+ +static char * +subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val) @@ -694,15 +261,10 @@ GRUB_MOD_INIT (btrfs) { grub_btrfs_fs.mod = mod; - grub_fs_register (&grub_btrfs_fs); -+ cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, -+ "DEVICE", -+ "Print BtrFS info about DEVICE."); -+ cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", -+ grub_cmd_btrfs_list_subvols, 0, -+ "[-p|-n] [-o var] DEVICE", -+ "Print list of BtrFS subvolumes on " -+ "DEVICE.", options); +@@ -3135,12 +3282,20 @@ + "[-p|-n] [-o var] DEVICE", + "Print default BtrFS subvolume on " + "DEVICE.", opt_default_subvol); + grub_register_variable_hook ("btrfs_subvol", subvol_get_env, + subvol_set_env); + grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, @@ -713,20 +275,11 @@ { + grub_register_variable_hook ("btrfs_subvol", NULL, NULL); + grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); -+ grub_unregister_command (cmd_info); -+ grub_unregister_extcmd (cmd_list_subvols); grub_fs_unregister (&grub_btrfs_fs); + grub_unregister_command (cmd_info); + grub_unregister_extcmd (cmd_list_subvols); + grub_unregister_extcmd (cmd_get_default_subvol); } + +// vim: si et sw=2: ---- a/include/grub/btrfs.h -+++ b/include/grub/btrfs.h -@@ -29,6 +29,7 @@ - GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84, - GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90, - GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8, -+ GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c, - GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4 - }; - ++++++ grub2-btrfs-06-subvol-mount.patch ++++++ --- /var/tmp/diff_new_pack.Pb5Vh4/_old 2026-08-31 15:56:36.582786224 +0200 +++ /var/tmp/diff_new_pack.Pb5Vh4/_new 2026-08-31 15:56:36.585786328 +0200 @@ -33,8 +33,8 @@ +find_mtab_subvol_tree (const char *path, char **path_in_subvol); static grub_err_t - read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) -@@ -1302,9 +1309,26 @@ + grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data, +@@ -1309,9 +1316,26 @@ grub_err_t err; grub_uint64_t tree = 0; grub_uint8_t type; @@ -61,7 +61,7 @@ if (err) return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); -@@ -2342,11 +2366,20 @@ +@@ -2354,11 +2378,20 @@ grub_uint8_t type; grub_size_t est_size = 0; grub_size_t sz; @@ -83,7 +83,7 @@ if (err) { grub_btrfs_unmount (data); -@@ -2480,11 +2513,21 @@ +@@ -2492,11 +2525,21 @@ struct grub_btrfs_inode inode; grub_uint8_t type; struct grub_btrfs_key key_in; @@ -106,8 +106,8 @@ if (err) { grub_btrfs_unmount (data); -@@ -2728,6 +2771,150 @@ - return 0; +@@ -2759,6 +2802,150 @@ + return GRUB_ERR_NONE; } +struct grub_btrfs_mtab @@ -257,24 +257,24 @@ static grub_err_t get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, grub_uint64_t objectid, grub_uint64_t offset, -@@ -2940,6 +3127,7 @@ +@@ -3250,6 +3437,7 @@ }; static grub_command_t cmd_info; +static grub_command_t cmd_mount_subvol; static grub_extcmd_t cmd_list_subvols; + static grub_extcmd_t cmd_get_default_subvol; - static char * -@@ -3004,6 +3192,9 @@ - cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, - "DEVICE", - "Print BtrFS info about DEVICE."); +@@ -3316,6 +3504,9 @@ + grub_cmd_btrfs_info, + "DEVICE", + "Print BtrFS info about DEVICE."); + cmd_mount_subvol = grub_register_command("btrfs-mount-subvol", grub_cmd_btrfs_mount_subvol, + "DEVICE DIRECTORY SUBVOL", + "Set btrfs DEVICE the DIRECTORY a mountpoint of SUBVOL."); cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", grub_cmd_btrfs_list_subvols, 0, - "[-p|-n] [-o var] DEVICE", + "[-p|-n] [-r] [-o var] DEVICE", --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -103,6 +103,14 @@ @@ -431,7 +431,7 @@ } } else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0) -@@ -1213,6 +1337,24 @@ +@@ -1223,6 +1347,24 @@ } #endif ++++++ grub2-btrfs-10-config-directory.patch ++++++ --- /var/tmp/diff_new_pack.Pb5Vh4/_old 2026-08-31 15:56:36.608787131 +0200 +++ /var/tmp/diff_new_pack.Pb5Vh4/_new 2026-08-31 15:56:36.612787270 +0200 @@ -8,110 +8,7 @@ --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c -@@ -3297,8 +3297,7 @@ - } - - static grub_err_t --grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, -- int argc, char **argv) -+grub_btrfs_get_default_subvol (const char *name, grub_uint64_t *ret_subvolid, char **ret_subvol) - { - char *devname; - grub_device_t dev; -@@ -3307,21 +3306,8 @@ - grub_uint64_t id; - char *subvol = NULL; - grub_uint64_t subvolid = 0; -- char *varname = NULL; -- char *output = NULL; -- int path_only = ctxt->state[1].set; -- int num_only = ctxt->state[2].set; -- -- if (ctxt->state[0].set) -- varname = ctxt->state[0].arg; -- -- if (argc < 1) -- return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); -- -- devname = grub_file_get_device_name(argv[0]); -- if (!devname) -- return grub_errno; - -+ devname = grub_file_get_device_name(name); - dev = grub_device_open (devname); - grub_free (devname); - if (!dev) -@@ -3332,8 +3318,7 @@ - { - grub_device_close (dev); - grub_dprintf ("btrfs", "failed to open fs\n"); -- grub_errno = GRUB_ERR_NONE; -- return 0; -+ return grub_errno; - } - - err = grub_btrfs_get_default_subvolume_id (data, &subvolid); -@@ -3362,12 +3347,47 @@ - return err; - } - -- if (subvol) -- grub_free (subvol); -+ grub_free (subvol); - subvol = path_out; - id = parent_id; - } - -+ if (ret_subvolid) -+ *ret_subvolid = subvolid; -+ if (ret_subvol) -+ *ret_subvol = subvol; -+ -+ grub_btrfs_unmount (data); -+ grub_device_close (dev); -+ -+ return GRUB_ERR_NONE; -+} -+ -+static grub_err_t -+grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, -+ int argc, char **argv) -+{ -+ grub_err_t err; -+ char *subvol = NULL; -+ grub_uint64_t subvolid = 0; -+ char *varname = NULL; -+ char *output = NULL; -+ int path_only = ctxt->state[1].set; -+ int num_only = ctxt->state[2].set; -+ -+ if (ctxt->state[0].set) -+ varname = ctxt->state[0].arg; -+ -+ if (argc < 1) -+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); -+ -+ if ((err = grub_btrfs_get_default_subvol (argv[0], &subvolid, &subvol)) != GRUB_ERR_NONE) -+ { -+ if (err == GRUB_ERR_BAD_FS) -+ err = grub_errno = GRUB_ERR_NONE; -+ return err; -+ } -+ - if (num_only && path_only) - output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol); - else if (num_only) -@@ -3383,9 +3403,6 @@ - grub_free (output); - grub_free (subvol); - -- grub_btrfs_unmount (data); -- grub_device_close (dev); -- - return GRUB_ERR_NONE; - } - -@@ -3464,6 +3481,122 @@ +@@ -3518,6 +3518,122 @@ return ""; } @@ -234,7 +131,7 @@ GRUB_MOD_INIT (btrfs) { grub_btrfs_fs.mod = mod; -@@ -3488,6 +3621,8 @@ +@@ -3543,6 +3659,8 @@ subvol_set_env); grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, subvolid_set_env); @@ -243,12 +140,12 @@ grub_env_export ("btrfs_subvol"); grub_env_export ("btrfs_subvolid"); grub_env_export ("btrfs_relative_path"); -@@ -3497,6 +3632,7 @@ +@@ -3552,6 +3670,7 @@ { grub_register_variable_hook ("btrfs_subvol", NULL, NULL); grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); + grub_register_variable_hook ("btrfs_relative_path", NULL, NULL); + grub_fs_unregister (&grub_btrfs_fs); grub_unregister_command (cmd_info); grub_unregister_extcmd (cmd_list_subvols); - grub_fs_unregister (&grub_btrfs_fs);
