On Mon, Oct 14, 2024 at 1:10 PM Leo Sandoval <lsand...@redhat.com> wrote: > > From: Michael Chang <mch...@suse.com> > > Introduce the "btrfs-get-default-subvol" command to output the default > subvolume's name or id to the console, or to a variable specified via "-o". > > Some example usage: > > 1. Output the default subvolume's name or path: > btrfs-get-default-subvol -p ($root) > 2. Output the default subvolume's ID or number: > btrfs-get-default-subvol -n ($root) > 3. Output the default subvolume's path to a variable "btrfs_prefix": > btrfs-get-default-subvol -p -o btrfs_prefix ($root) > > This is useful in a prebuilt image like `grub.xen`, which is built at a time > when the guest configuration cannot be determined. In such a setup, trying to > enable "btrfs_relative_path" in the load configuration may lead to problems if > the guest image did not enable btrfs snapshots in its configuration. > > As a result, when attempting to locate grub.cfg in a guest, it may be > necessary > to specify the default subvolume in advance where the grub.cfg resides. The > script would look like this: > > btrfs-get-default-subvol -p -o btrfs_prefix ($root) > if [ -n "${btrfs_prefix}" ]; then > configfile "${btrfs_prefix}/boot/grub/grub.cfg" > else > configfile "/boot/grub/grub.cfg" > fi > > Signed-off-by: Michael Chang <mch...@suse.com> > Signed-off-by: Robbie Harwood <rharw...@redhat.com> > --- > grub-core/fs/btrfs.c | 238 +++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 238 insertions(+) > > diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c > index d47f9ab03..d93e9615a 100644 > --- a/grub-core/fs/btrfs.c > +++ b/grub-core/fs/btrfs.c > @@ -2876,6 +2876,238 @@ out: > return 0; > } > > +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 = child_id, > + .type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF, > + .offset = 0, > + }, 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; > + > + *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")); > + } > + > + buf = grub_malloc(elemsize + 1); > + 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), > + 0, &fs_root); > + if (err) > + { > + grub_free(buf); > + free_iterator(&desc); > + return err; > + } > + > + find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, > &parent_path); > + > + 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 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; > + > + *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; > + direl = grub_malloc (elemsize + 1); > + 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); > + return GRUB_ERR_NONE; > +} > + > +static grub_err_t > +grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, > + int argc, char **argv) > +{ > + 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; > + 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; > + > + 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"); > + grub_errno = GRUB_ERR_NONE; > + return 0; > + } > + > + err = grub_btrfs_get_default_subvolume_id (data, &subvolid); > + if (err) > + { > + grub_btrfs_unmount (data); > + grub_device_close (dev); > + return err; > + } > + > + id = subvolid; > + while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID) > + { > + grub_uint64_t parent_id; > + char *path_out; > + > + err = grub_btrfs_get_parent_subvol_path (data, grub_cpu_to_le64 (id), > subvol, &parent_id, &path_out); > + if (err) > + { > + grub_btrfs_unmount (data); > + grub_device_close (dev); > + return err; > + } > + > + if (subvol) > + grub_free (subvol); > + subvol = path_out; > + id = parent_id; > + } > + > + 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); > + > + 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, > @@ -2893,6 +3125,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; > > static char * > subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), > @@ -2960,6 +3193,11 @@ GRUB_MOD_INIT (btrfs) > "[-p|-n] [-o var] DEVICE", > "Print list of BtrFS subvolumes on " > "DEVICE.", options); > + 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.", options); > grub_register_variable_hook ("btrfs_subvol", subvol_get_env, > subvol_set_env); > grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, > -- > 2.46.2 >
LGTM. :) Reviewed-by: Neal Gompa <ngomp...@gmail.com> -- 真実はいつも一つ!/ Always, there's only one truth! _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel