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

Reply via email to