grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] grub2/btrfs: Add ability to boot from subvolumes


From: Michael Chang
Subject: Re: [PATCH] grub2/btrfs: Add ability to boot from subvolumes
Date: Mon, 13 Jul 2020 16:50:57 +0800
User-agent: Mutt/1.10.1 (2018-07-13)

Besides Jeff's patch, we had been using patch to have grub booting from
default subvolume with the path relative to it, rather than booting from
real root and using absoute path [1].

[1] 
http://git.savannah.gnu.org/cgit/grub.git/commit/?id=82591fa6e7941efe2723a23cb1d924dfe0641974

IMHO grub should view btrfs and it's subvolumes as a collection of
bootable systems and snapshots, hence the default one to boot is
selectable via `btrfs set-default`. Having it always refer to real root
could make things simpler as everything could work the same way like
tractional filesystems, but is also rendering the btrfs
subvolume/snapshot booting feature very hard to come by.

Could we please consider to reverting the commit ?

Thanks,
Michael

On Mon, Jul 13, 2020 at 09:14:30AM +0200, Thomas Schneider wrote:
> Hello,
> 
> in order to answer questions why this feature is needed I'll re-post my
> original feature request.
> 
> Grub is booting the default subvolume in BTRFS w/o any manual
> modification in Grub configuration.
> 
> Current situation:
> Booting a snapshot created with Snapper
> <https://wiki.archlinux.org/index.php/Snapper> is not possible w/o
> modifying the Grub entries.
> This is related to the Snapper functionality that creates a ro snapshot
> that must be booted and then creates another rw snapshot of the
> currently booted ro snapshot. Then Grub configuration must be recreated
> to reflect the newly created rw snapshot.
> 
> Available solution:
> In OpenSuse Grub is always booting the default snapshot and hereby Grub
> configuration must not be adjusted.
> To my understanding OpenSuse uses a patched Grub.
> 
> Regards
> Thomas
> 
> Am 12.07.2020 um 16:43 schrieb Jeff Mahoney:
> > On 7/11/20 7:09 AM, Vladimir 'phcoder' Serbinenko wrote:
> >> Why is this patch needed? Can't subvolumes be reached from real root?
> >> Isn't autogenerated grub.cfg use the names based on real root
> > We use it to boot from snapshots, which include the grub configs (though
> > not grub itself).  It allows all pathnames to be relative to the root of
> > the subvolume so we can set btrfs_subvol to the snapshot and load the
> > grub config from the snapshot without having to modify it at any point.
> >
> > -Jeff
> >
> >
> >> On Sat, Jul 11, 2020, 05:52 <jeffm@suse.com <mailto:jeffm@suse.com>> wrote:
> >>
> >>     From: Jeff Mahoney <jeffm@suse.com <mailto:jeffm@suse.com>>
> >>
> >>     This patch adds the ability to specify a different root on a btrfs
> >>     filesystem too boot from other than the default one.
> >>
> >>     btrfs-list-snapshots <dev> will list the subvolumes available on the
> >>     filesystem.
> >>
> >>     set btrfs_subvol=<path> and set btrfs_subvolid=<subvolid> will specify
> >>     which subvolume to use and any pathnames provided with either of those
> >>     variables set will start using that root. If the subvolume or
> >>     subvolume id
> >>     doesn't exist, then an error case will result.
> >>
> >>     It is possible to boot into a separate GRUB instance by exporting the
> >>     variable and loading the config file from the subvolume.
> >>
> >>     Signed-off-by: Jeff Mahoney <jeffm@suse.com <mailto:jeffm@suse.com>>
> >>     ---
> >>      grub-core/fs/btrfs.c | 548 +++++++++++++++++++++++++++++++++++++++++--
> >>      include/grub/btrfs.h |   1 +
> >>      2 files changed, 531 insertions(+), 18 deletions(-)
> >>
> >>     diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
> >>     index 63f9657a6..fda4302b4 100644
> >>     --- a/grub-core/fs/btrfs.c
> >>     +++ b/grub-core/fs/btrfs.c
> >>     @@ -40,6 +40,9 @@
> >>      #include <grub/btrfs.h>
> >>      #include <grub/crypto.h>
> >>      #include <grub/diskfilter.h>
> >>     +#include <grub/command.h>
> >>     +#include <grub/env.h>
> >>     +#include <grub/extcmd.h>
> >>
> >>      GRUB_MOD_LICENSE ("GPLv3+");
> >>
> >>     @@ -78,9 +81,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];
> >>     @@ -120,6 +125,7 @@ struct grub_btrfs_data
> >>        grub_uint64_t exttree;
> >>        grub_size_t extsize;
> >>        struct grub_btrfs_extent_data *extent;
> >>     +  grub_uint64_t fs_tree;
> >>      };
> >>
> >>      struct grub_btrfs_chunk_item
> >>     @@ -190,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];
> >>     +} __attribute__ ((packed));
> >>     +
> >>      struct grub_btrfs_time
> >>      {
> >>        grub_int64_t sec;
> >>     @@ -235,6 +249,14 @@ struct grub_btrfs_extent_data
> >>
> >>      #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
> >>     +
> >>     +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
> >>      };
> >>     @@ -1160,6 +1182,62 @@ grub_btrfs_read_logical (struct
> >>     grub_btrfs_data *data, grub_disk_addr_t addr,
> >>        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);
> >>     +
> >>     +static grub_err_t
> >>     +lookup_root_by_id(struct grub_btrfs_data *data, grub_uint64_t id)
> >>     +{
> >>     +  grub_err_t err;
> >>     +  grub_uint64_t tree;
> >>     +
> >>     +  err = get_fs_root(data, data->sblock.root_tree, id, -1, &tree);
> >>     +  if (!err)
> >>     +    data->fs_tree = tree;
> >>     +  return err;
> >>     +}
> >>     +
> >>     +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);
> >>     +
> >>     +static grub_err_t
> >>     +lookup_root_by_name(struct grub_btrfs_data *data, const char *path)
> >>     +{
> >>     +  grub_err_t err;
> >>     +  grub_uint64_t tree = 0;
> >>     +  grub_uint8_t type;
> >>     +  struct grub_btrfs_key key;
> >>     +
> >>     +  err = find_path (data, path, &key, &tree, &type);
> >>     +  if (err)
> >>     +      return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate
> >>     %s\n", path);
> >>     +
> >>     +  if (key.object_id != grub_cpu_to_le64_compile_time
> >>     (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0)
> >>     +    return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a
> >>     subvolume\n", path);
> >>     +
> >>     +  data->fs_tree = tree;
> >>     +  return GRUB_ERR_NONE;
> >>     +}
> >>     +
> >>     +static grub_err_t
> >>     +btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__
> >>     ((unused)))
> >>     +{
> >>     +  if (btrfs_default_subvol)
> >>     +    return lookup_root_by_name(data, btrfs_default_subvol);
> >>     +
> >>     +  if (btrfs_default_subvolid)
> >>     +    return lookup_root_by_id(data, btrfs_default_subvolid);
> >>     +
> >>     +  data->fs_tree = 0;
> >>     +
> >>     +  return GRUB_ERR_NONE;
> >>     +}
> >>     +
> >>     +
> >>      static struct grub_btrfs_data *
> >>      grub_btrfs_mount (grub_device_t dev)
> >>      {
> >>     @@ -1195,6 +1273,13 @@ grub_btrfs_mount (grub_device_t dev)
> >>        data->devices_attached[0].dev = dev;
> >>        data->devices_attached[0].id = data->sblock.this_device.device_id;
> >>
> >>     +  err = btrfs_handle_subvol (data);
> >>     +  if (err)
> >>     +    {
> >>     +      grub_free (data);
> >>     +      return NULL;
> >>     +    }
> >>     +
> >>        return data;
> >>      }
> >>
> >>     @@ -1660,6 +1745,91 @@ 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 = 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;
> >>     +
> >>     +    inode_ref = grub_malloc(elemsize + 1);
> >>     +    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);
> >>     +
> >>     +    alloc += grub_le_to_cpu16 (inode_ref->n) + 2;
> >>     +    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,
> >>     @@ -1678,14 +1848,26 @@ find_path (struct grub_btrfs_data *data,
> >>        char *origpath = NULL;
> >>        unsigned symlinks_max = 32;
> >>
> >>     -  err = get_root (data, key, tree, type);
> >>     -  if (err)
> >>     -    return err;
> >>     -
> >>        origpath = grub_strdup (path);
> >>        if (!origpath)
> >>          return grub_errno;
> >>
> >>     +  if (data->fs_tree)
> >>     +    {
> >>     +      *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
> >>     +      *tree = data->fs_tree;
> >>     +      /* This is a tree root, so everything starts at objectid 256 */
> >>     +      key->object_id = grub_cpu_to_le64_compile_time
> >>     (GRUB_BTRFS_OBJECT_ID_CHUNK);
> >>     +      key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
> >>     +      key->offset = 0;
> >>     +    }
> >>     +  else
> >>     +    {
> >>     +      err = get_root (data, key, tree, type);
> >>     +      if (err)
> >>     +       return err;
> >>     +    }
> >>     +
> >>        while (1)
> >>          {
> >>            while (path[0] == '/')
> >>     @@ -1858,9 +2040,21 @@ find_path (struct grub_btrfs_data *data,
> >>               path = path_alloc = tmp;
> >>               if (path[0] == '/')
> >>                 {
> >>     -             err = get_root (data, key, tree, type);
> >>     -             if (err)
> >>     -               return err;
> >>     +             if (data->fs_tree)
> >>     +               {
> >>     +                 *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
> >>     +                 *tree = data->fs_tree;
> >>     +                 /* This is a tree root, so everything starts at
> >>     objectid 256 */
> >>     +                 key->object_id = grub_cpu_to_le64_compile_time
> >>     (GRUB_BTRFS_OBJECT_ID_CHUNK);
> >>     +                 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
> >>     +                 key->offset = 0;
> >>     +               }
> >>     +             else
> >>     +               {
> >>     +                 err = get_root (data, key, tree, type);
> >>     +                 if (err)
> >>     +                   return err;
> >>     +               }
> >>                 }
> >>               continue;
> >>             }
> >>     @@ -2101,6 +2295,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)
> >>      {
> >>     @@ -2112,15 +2320,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);
> >>
> >>     @@ -2177,6 +2377,242 @@ 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)
> >>     +    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_free(buf);
> >>     +          allocated = 2 * elemsize;
> >>     +          buf = grub_malloc(allocated + 1);
> >>     +          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;
> >>     +
> >>     +      find_pathname(data, ref->dirid, fs_root, ref->name, &p);
> >>     +
> >>     +      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,
> >>     @@ -2192,12 +2628,88 @@ static struct grub_fs grub_btrfs_fs = {
> >>      #endif
> >>      };
> >>
> >>     +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)
> >>     +{
> >>     +  unsigned long long result = 0;
> >>     +
> >>     +  grub_errno = GRUB_ERR_NONE;
> >>     +  if (*val)
> >>     +    {
> >>     +      result = grub_strtoull(val, NULL, 10);
> >>     +      if (grub_errno)
> >>     +        return NULL;
> >>     +    }
> >>     +
> >>     +  grub_free (btrfs_default_subvol);
> >>     +  btrfs_default_subvol = NULL;
> >>     +  btrfs_default_subvolid = result;
> >>     +  return grub_strdup(val);
> >>     +}
> >>     +
> >>     +static const char *
> >>     +subvolid_get_env (struct grub_env_var *var __attribute__ ((unused)),
> >>     +                  const char *val __attribute__ ((unused)))
> >>     +{
> >>     +  if (btrfs_default_subvol)
> >>     +    return grub_xasprintf("subvol:%s", btrfs_default_subvol);
> >>     +  else if (btrfs_default_subvolid)
> >>     +    return grub_xasprintf("%"PRIuGRUB_UINT64_T,
> >>     btrfs_default_subvolid);
> >>     +  else
> >>     +    return "";
> >>     +}
> >>     +
> >>     +static char *
> >>     +subvol_set_env (struct grub_env_var *var __attribute__ ((unused)),
> >>     +                const char *val)
> >>     +{
> >>     +  grub_free (btrfs_default_subvol);
> >>     +  btrfs_default_subvol = grub_strdup (val);
> >>     +  btrfs_default_subvolid = 0;
> >>     +  return grub_strdup(val);
> >>     +}
> >>     +
> >>     +static const char *
> >>     +subvol_get_env (struct grub_env_var *var __attribute__ ((unused)),
> >>     +                const char *val __attribute__ ((unused)))
> >>     +{
> >>     +  if (btrfs_default_subvol)
> >>     +    return btrfs_default_subvol;
> >>     +  else if (btrfs_default_subvolid)
> >>     +    return grub_xasprintf("subvolid:%" PRIuGRUB_UINT64_T,
> >>     +                          btrfs_default_subvolid);
> >>     +  else
> >>     +    return "";
> >>     +}
> >>     +
> >>      GRUB_MOD_INIT (btrfs)
> >>      {
> >>        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);
> >>     +  grub_register_variable_hook ("btrfs_subvol", subvol_get_env,
> >>     +                               subvol_set_env);
> >>     +  grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env,
> >>     +                               subvolid_set_env);
> >>      }
> >>
> >>      GRUB_MOD_FINI (btrfs)
> >>      {
> >>     +  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);
> >>      }
> >>     +
> >>     +// vim: si et sw=2:
> >>     diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h
> >>     index 9d93fb6c1..234ad9767 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
> >>        };
> >>
> >>     -- 
> >>     2.26.2
> >>
> >>
> >>     _______________________________________________
> >>     Grub-devel mailing list
> >>     Grub-devel@gnu.org <mailto:Grub-devel@gnu.org>
> >>     https://lists.gnu.org/mailman/listinfo/grub-devel
> >>
> >>
> >> _______________________________________________
> >> Grub-devel mailing list
> >> Grub-devel@gnu.org
> >> https://lists.gnu.org/mailman/listinfo/grub-devel
> >>
> >
> >
> > _______________________________________________
> > Grub-devel mailing list
> > Grub-devel@gnu.org
> > https://lists.gnu.org/mailman/listinfo/grub-devel
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel




reply via email to

[Prev in Thread] Current Thread [Next in Thread]