|
|
d9d99f |
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
d9d99f |
From: Michael Chang <mchang@suse.com>
|
|
|
d9d99f |
Date: Thu, 11 May 2017 08:56:57 +0000
|
|
|
d9d99f |
Subject: [PATCH] Grub not working correctly with btrfs snapshots (bsc#1026511)
|
|
|
d9d99f |
|
|
|
d9d99f |
---
|
|
|
d9d99f |
grub-core/fs/btrfs.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
d9d99f |
1 file changed, 238 insertions(+)
|
|
|
d9d99f |
|
|
|
d9d99f |
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
|
|
|
b71686 |
index 4a31d39ee..7002ad81b 100644
|
|
|
d9d99f |
--- a/grub-core/fs/btrfs.c
|
|
|
d9d99f |
+++ b/grub-core/fs/btrfs.c
|
|
|
d9d99f |
@@ -2446,6 +2446,238 @@ out:
|
|
|
d9d99f |
return 0;
|
|
|
d9d99f |
}
|
|
|
d9d99f |
|
|
|
d9d99f |
+static grub_err_t
|
|
|
d9d99f |
+grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data,
|
|
|
d9d99f |
+ grub_uint64_t child_id,
|
|
|
d9d99f |
+ const char *child_path,
|
|
|
d9d99f |
+ grub_uint64_t *parent_id,
|
|
|
d9d99f |
+ char **path_out)
|
|
|
d9d99f |
+{
|
|
|
d9d99f |
+ grub_uint64_t fs_root = 0;
|
|
|
d9d99f |
+ struct grub_btrfs_key key_in = {
|
|
|
d9d99f |
+ .object_id = child_id,
|
|
|
d9d99f |
+ .type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF,
|
|
|
d9d99f |
+ .offset = 0,
|
|
|
d9d99f |
+ }, key_out;
|
|
|
d9d99f |
+ struct grub_btrfs_root_ref *ref;
|
|
|
d9d99f |
+ char *buf;
|
|
|
d9d99f |
+ struct grub_btrfs_leaf_descriptor desc;
|
|
|
d9d99f |
+ grub_size_t elemsize;
|
|
|
d9d99f |
+ grub_disk_addr_t elemaddr;
|
|
|
d9d99f |
+ grub_err_t err;
|
|
|
d9d99f |
+ char *parent_path;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ *parent_id = 0;
|
|
|
d9d99f |
+ *path_out = 0;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ err = lower_bound(data, &key_in, &key_out, data->sblock.root_tree,
|
|
|
d9d99f |
+ &elemaddr, &elemsize, &desc, 0);
|
|
|
d9d99f |
+ if (err)
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF || elemaddr == 0)
|
|
|
d9d99f |
+ next(data, &desc, &elemaddr, &elemsize, &key_out);
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ free_iterator(&desc);
|
|
|
d9d99f |
+ return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root backrefs"));
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ buf = grub_malloc(elemsize + 1);
|
|
|
d9d99f |
+ if (!buf)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ free_iterator(&desc);
|
|
|
d9d99f |
+ return grub_errno;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0);
|
|
|
d9d99f |
+ if (err)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_free(buf);
|
|
|
d9d99f |
+ free_iterator(&desc);
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ buf[elemsize] = 0;
|
|
|
d9d99f |
+ ref = (struct grub_btrfs_root_ref *)buf;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ err = get_fs_root(data, data->sblock.root_tree, grub_le_to_cpu64 (key_out.offset),
|
|
|
d9d99f |
+ 0, &fs_root);
|
|
|
d9d99f |
+ if (err)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_free(buf);
|
|
|
d9d99f |
+ free_iterator(&desc);
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path);
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (child_path)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ *path_out = grub_xasprintf ("%s/%s", parent_path, child_path);
|
|
|
d9d99f |
+ grub_free (parent_path);
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+ else
|
|
|
d9d99f |
+ *path_out = parent_path;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ *parent_id = grub_le_to_cpu64 (key_out.offset);
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ grub_free(buf);
|
|
|
d9d99f |
+ free_iterator(&desc);
|
|
|
d9d99f |
+ return GRUB_ERR_NONE;
|
|
|
d9d99f |
+}
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+static grub_err_t
|
|
|
d9d99f |
+grub_btrfs_get_default_subvolume_id (struct grub_btrfs_data *data, grub_uint64_t *id)
|
|
|
d9d99f |
+{
|
|
|
d9d99f |
+ grub_err_t err;
|
|
|
d9d99f |
+ grub_disk_addr_t elemaddr;
|
|
|
d9d99f |
+ grub_size_t elemsize;
|
|
|
d9d99f |
+ struct grub_btrfs_key key, key_out;
|
|
|
d9d99f |
+ struct grub_btrfs_dir_item *direl = NULL;
|
|
|
d9d99f |
+ const char *ctoken = "default";
|
|
|
d9d99f |
+ grub_size_t ctokenlen = sizeof ("default") - 1;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ *id = 0;
|
|
|
d9d99f |
+ key.object_id = data->sblock.root_dir_objectid;
|
|
|
d9d99f |
+ key.type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
|
|
|
d9d99f |
+ key.offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen));
|
|
|
d9d99f |
+ err = lower_bound (data, &key, &key_out, data->sblock.root_tree, &elemaddr, &elemsize,
|
|
|
d9d99f |
+ NULL, 0);
|
|
|
d9d99f |
+ if (err)
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (key_cmp (&key, &key_out) != 0)
|
|
|
d9d99f |
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found"));
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ struct grub_btrfs_dir_item *cdirel;
|
|
|
d9d99f |
+ direl = grub_malloc (elemsize + 1);
|
|
|
d9d99f |
+ err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0);
|
|
|
d9d99f |
+ if (err)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_free (direl);
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+ for (cdirel = direl;
|
|
|
d9d99f |
+ (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
|
|
|
d9d99f |
+ < (grub_ssize_t) elemsize;
|
|
|
d9d99f |
+ cdirel = (void *) ((grub_uint8_t *) (direl + 1)
|
|
|
d9d99f |
+ + grub_le_to_cpu16 (cdirel->n)
|
|
|
d9d99f |
+ + grub_le_to_cpu16 (cdirel->m)))
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ if (ctokenlen == grub_le_to_cpu16 (cdirel->n)
|
|
|
d9d99f |
+ && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0)
|
|
|
d9d99f |
+ break;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+ if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl
|
|
|
d9d99f |
+ >= (grub_ssize_t) elemsize)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_free (direl);
|
|
|
d9d99f |
+ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found"));
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (cdirel->key.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_free (direl);
|
|
|
d9d99f |
+ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found"));
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ *id = grub_le_to_cpu64 (cdirel->key.object_id);
|
|
|
d9d99f |
+ return GRUB_ERR_NONE;
|
|
|
d9d99f |
+}
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+static grub_err_t
|
|
|
d9d99f |
+grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt,
|
|
|
d9d99f |
+ int argc, char **argv)
|
|
|
d9d99f |
+{
|
|
|
d9d99f |
+ char *devname;
|
|
|
d9d99f |
+ grub_device_t dev;
|
|
|
d9d99f |
+ struct grub_btrfs_data *data;
|
|
|
d9d99f |
+ grub_err_t err;
|
|
|
d9d99f |
+ grub_uint64_t id;
|
|
|
d9d99f |
+ char *subvol = NULL;
|
|
|
d9d99f |
+ grub_uint64_t subvolid = 0;
|
|
|
d9d99f |
+ char *varname = NULL;
|
|
|
d9d99f |
+ char *output = NULL;
|
|
|
d9d99f |
+ int path_only = ctxt->state[1].set;
|
|
|
d9d99f |
+ int num_only = ctxt->state[2].set;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (ctxt->state[0].set)
|
|
|
d9d99f |
+ varname = ctxt->state[0].arg;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (argc < 1)
|
|
|
d9d99f |
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ devname = grub_file_get_device_name(argv[0]);
|
|
|
d9d99f |
+ if (!devname)
|
|
|
d9d99f |
+ return grub_errno;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ dev = grub_device_open (devname);
|
|
|
d9d99f |
+ grub_free (devname);
|
|
|
d9d99f |
+ if (!dev)
|
|
|
d9d99f |
+ return grub_errno;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ data = grub_btrfs_mount(dev);
|
|
|
d9d99f |
+ if (!data)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_device_close (dev);
|
|
|
d9d99f |
+ grub_dprintf ("btrfs", "failed to open fs\n");
|
|
|
d9d99f |
+ grub_errno = GRUB_ERR_NONE;
|
|
|
d9d99f |
+ return 0;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ err = grub_btrfs_get_default_subvolume_id (data, &subvolid);
|
|
|
d9d99f |
+ if (err)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_btrfs_unmount (data);
|
|
|
d9d99f |
+ grub_device_close (dev);
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ id = subvolid;
|
|
|
d9d99f |
+ while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_uint64_t parent_id;
|
|
|
d9d99f |
+ char *path_out;
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ err = grub_btrfs_get_parent_subvol_path (data, grub_cpu_to_le64 (id), subvol, &parent_id, &path_out);
|
|
|
d9d99f |
+ if (err)
|
|
|
d9d99f |
+ {
|
|
|
d9d99f |
+ grub_btrfs_unmount (data);
|
|
|
d9d99f |
+ grub_device_close (dev);
|
|
|
d9d99f |
+ return err;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (subvol)
|
|
|
d9d99f |
+ grub_free (subvol);
|
|
|
d9d99f |
+ subvol = path_out;
|
|
|
d9d99f |
+ id = parent_id;
|
|
|
d9d99f |
+ }
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (num_only && path_only)
|
|
|
d9d99f |
+ output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol);
|
|
|
d9d99f |
+ else if (num_only)
|
|
|
d9d99f |
+ output = grub_xasprintf ("%"PRIuGRUB_UINT64_T, subvolid);
|
|
|
d9d99f |
+ else
|
|
|
d9d99f |
+ output = grub_xasprintf ("/%s", subvol);
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ if (varname)
|
|
|
d9d99f |
+ grub_env_set(varname, output);
|
|
|
d9d99f |
+ else
|
|
|
d9d99f |
+ grub_printf ("%s\n", output);
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ grub_free (output);
|
|
|
d9d99f |
+ grub_free (subvol);
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ grub_btrfs_unmount (data);
|
|
|
d9d99f |
+ grub_device_close (dev);
|
|
|
d9d99f |
+
|
|
|
d9d99f |
+ return GRUB_ERR_NONE;
|
|
|
d9d99f |
+}
|
|
|
d9d99f |
+
|
|
|
d9d99f |
static struct grub_fs grub_btrfs_fs = {
|
|
|
d9d99f |
.name = "btrfs",
|
|
|
d9d99f |
.dir = grub_btrfs_dir,
|
|
|
d9d99f |
@@ -2464,6 +2696,7 @@ static struct grub_fs grub_btrfs_fs = {
|
|
|
d9d99f |
static grub_command_t cmd_info;
|
|
|
d9d99f |
static grub_command_t cmd_mount_subvol;
|
|
|
d9d99f |
static grub_extcmd_t cmd_list_subvols;
|
|
|
d9d99f |
+static grub_extcmd_t cmd_get_default_subvol;
|
|
|
d9d99f |
|
|
|
d9d99f |
static char *
|
|
|
d9d99f |
subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)),
|
|
|
d9d99f |
@@ -2534,6 +2767,11 @@ GRUB_MOD_INIT (btrfs)
|
|
|
d9d99f |
"[-p|-n] [-o var] DEVICE",
|
|
|
d9d99f |
"Print list of BtrFS subvolumes on "
|
|
|
d9d99f |
"DEVICE.", options);
|
|
|
d9d99f |
+ cmd_get_default_subvol = grub_register_extcmd("btrfs-get-default-subvol",
|
|
|
d9d99f |
+ grub_cmd_btrfs_get_default_subvol, 0,
|
|
|
d9d99f |
+ "[-p|-n] [-o var] DEVICE",
|
|
|
d9d99f |
+ "Print default BtrFS subvolume on "
|
|
|
d9d99f |
+ "DEVICE.", options);
|
|
|
d9d99f |
grub_register_variable_hook ("btrfs_subvol", subvol_get_env,
|
|
|
d9d99f |
subvol_set_env);
|
|
|
d9d99f |
grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env,
|