7e98da058f
This change reorganizes and cleanups our patches to reduce the patch number from 314 patches to 187. That's achieved by dropping patches that are later reverted and squashing fixes for earlier patches that introduced features. There are no code changes and the diff with upstream is the same before and after the cleanup. Having fewer patches makes easier to manage the patchset and also will ease to rebase them on top of the latest grub-2.04 release. Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
273 lines
7.3 KiB
Diff
273 lines
7.3 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Michael Chang <mchang@suse.com>
|
|
Date: Thu, 11 May 2017 08:56:57 +0000
|
|
Subject: [PATCH] Grub not working correctly with btrfs snapshots (bsc#1026511)
|
|
|
|
---
|
|
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 4a31d39ee74..7002ad81b7e 100644
|
|
--- a/grub-core/fs/btrfs.c
|
|
+++ b/grub-core/fs/btrfs.c
|
|
@@ -2446,6 +2446,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",
|
|
.dir = grub_btrfs_dir,
|
|
@@ -2464,6 +2696,7 @@ static struct grub_fs grub_btrfs_fs = {
|
|
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 *
|
|
subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)),
|
|
@@ -2534,6 +2767,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,
|