grub2/0079-grub2-btrfs-06-subvol-mount.patch
Javier Martinez Canillas e1531466e1
Update to grub 2.04
This change updates grub to the 2.04 release. The new release changed how
grub is built, so the bootstrap and bootstrap.conf files have to be added
to the dist-git. Also, the gitignore file changed so it has to be updated.

Since the patches have been forward ported to 2.04, there's no need for a
logic to maintain a patch with the delta between the release and the grub
master branch. So the release-to-master.patch is dropped and no longer is
updated by the do-rebase script.

Also since gnulib isn't part of the grub repository anymore and cloned by
the boostrap tool, a gnulib tarball is included as other source file and
copied before calling the bootstrap tool. That way grub can be built even
in builders that only have access to the sources lookaside cache.

Resolves: rhbz#1727279

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
2019-08-15 08:04:53 +02:00

537 lines
13 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang <mchang@suse.com>
Date: Tue, 9 Jul 2019 13:56:16 +0200
Subject: [PATCH] grub2-btrfs-06-subvol-mount
---
grub-core/fs/btrfs.c | 195 +++++++++++++++++++++++++++++++++++++++-
grub-core/osdep/linux/getroot.c | 148 +++++++++++++++++++++++++++++-
util/grub-install.c | 49 ++++++++++
include/grub/emu/getroot.h | 5 ++
4 files changed, 392 insertions(+), 5 deletions(-)
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 2db89f71ea5..162723423ca 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -41,6 +41,7 @@
#include <grub/command.h>
#include <grub/env.h>
#include <grub/extcmd.h>
+#include <grub/list.h>
#include <grub/crypto.h>
#include <grub/diskfilter.h>
@@ -263,6 +264,12 @@ static grub_err_t
grub_btrfs_read_logical (struct grub_btrfs_data *data,
grub_disk_addr_t addr, void *buf, grub_size_t size,
int recursion_depth);
+static grub_err_t
+get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key,
+ grub_uint64_t *tree, grub_uint8_t *type);
+
+grub_uint64_t
+find_mtab_subvol_tree (const char *path, char **path_in_subvol);
static grub_err_t
read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb)
@@ -1203,9 +1210,26 @@ 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;
+ grub_uint64_t saved_tree;
struct grub_btrfs_key key;
+ if (path[0] == '\0')
+ {
+ data->fs_tree = 0;
+ return GRUB_ERR_NONE;
+ }
+
+ err = get_root (data, &key, &tree, &type);
+ if (err)
+ return err;
+
+ saved_tree = data->fs_tree;
+ data->fs_tree = tree;
+
err = find_path (data, path, &key, &tree, &type);
+
+ data->fs_tree = saved_tree;
+
if (err)
return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path);
@@ -2179,11 +2203,20 @@ grub_btrfs_dir (grub_device_t device, const char *path,
int r = 0;
grub_uint64_t tree;
grub_uint8_t type;
+ char *new_path = NULL;
if (!data)
return grub_errno;
- err = find_path (data, path, &key_in, &tree, &type);
+ tree = find_mtab_subvol_tree (path, &new_path);
+
+ if (tree)
+ data->fs_tree = tree;
+
+ err = find_path (data, new_path ? new_path : path, &key_in, &tree, &type);
+ if (new_path)
+ grub_free (new_path);
+
if (err)
{
grub_btrfs_unmount (data);
@@ -2285,11 +2318,21 @@ grub_btrfs_open (struct grub_file *file, const char *name)
struct grub_btrfs_inode inode;
grub_uint8_t type;
struct grub_btrfs_key key_in;
+ grub_uint64_t tree;
+ char *new_path = NULL;
if (!data)
return grub_errno;
- err = find_path (data, name, &key_in, &data->tree, &type);
+ tree = find_mtab_subvol_tree (name, &new_path);
+
+ if (tree)
+ data->fs_tree = tree;
+
+ err = find_path (data, new_path ? new_path : name, &key_in, &data->tree, &type);
+ if (new_path)
+ grub_free (new_path);
+
if (err)
{
grub_btrfs_unmount (data);
@@ -2460,6 +2503,150 @@ grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc,
return 0;
}
+struct grub_btrfs_mtab
+{
+ struct grub_btrfs_mtab *next;
+ struct grub_btrfs_mtab **prev;
+ char *path;
+ char *subvol;
+ grub_uint64_t tree;
+};
+
+typedef struct grub_btrfs_mtab* grub_btrfs_mtab_t;
+
+static struct grub_btrfs_mtab *btrfs_mtab;
+
+#define FOR_GRUB_MTAB(var) FOR_LIST_ELEMENTS (var, btrfs_mtab)
+#define FOR_GRUB_MTAB_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE((var), (next), btrfs_mtab)
+
+static void
+add_mountpoint (const char *path, const char *subvol, grub_uint64_t tree)
+{
+ grub_btrfs_mtab_t m = grub_malloc (sizeof (*m));
+
+ m->path = grub_strdup (path);
+ m->subvol = grub_strdup (subvol);
+ m->tree = tree;
+ grub_list_push (GRUB_AS_LIST_P (&btrfs_mtab), GRUB_AS_LIST (m));
+}
+
+static grub_err_t
+grub_cmd_btrfs_mount_subvol (grub_command_t cmd __attribute__ ((unused)), int argc,
+ char **argv)
+{
+ char *devname, *dirname, *subvol;
+ struct grub_btrfs_key key_in;
+ grub_uint8_t type;
+ grub_uint64_t tree;
+ grub_uint64_t saved_tree;
+ grub_err_t err;
+ struct grub_btrfs_data *data = NULL;
+ grub_device_t dev = NULL;
+
+ if (argc < 3)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "required <dev> <dir> and <subvol>");
+
+ devname = grub_file_get_device_name(argv[0]);
+ dev = grub_device_open (devname);
+ grub_free (devname);
+
+ if (!dev)
+ {
+ err = grub_errno;
+ goto err_out;
+ }
+
+ dirname = argv[1];
+ subvol = argv[2];
+
+ data = grub_btrfs_mount (dev);
+ if (!data)
+ {
+ err = grub_errno;
+ goto err_out;
+ }
+
+ err = find_path (data, dirname, &key_in, &tree, &type);
+ if (err)
+ goto err_out;
+
+ if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
+ goto err_out;
+ }
+
+ err = get_root (data, &key_in, &tree, &type);
+
+ if (err)
+ goto err_out;
+
+ saved_tree = data->fs_tree;
+ data->fs_tree = tree;
+ err = find_path (data, subvol, &key_in, &tree, &type);
+ data->fs_tree = saved_tree;
+
+ if (err)
+ goto err_out;
+
+ if (key_in.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", subvol);
+ goto err_out;
+ }
+
+ grub_btrfs_unmount (data);
+ grub_device_close (dev);
+ add_mountpoint (dirname, subvol, tree);
+
+ return GRUB_ERR_NONE;
+
+err_out:
+
+ if (data)
+ grub_btrfs_unmount (data);
+
+ if (dev)
+ grub_device_close (dev);
+
+ return err;
+}
+
+grub_uint64_t
+find_mtab_subvol_tree (const char *path, char **path_in_subvol)
+{
+ grub_btrfs_mtab_t m, cm;
+ grub_uint64_t tree;
+
+ if (!path || !path_in_subvol)
+ return 0;
+
+ *path_in_subvol = NULL;
+ tree = 0;
+ cm = NULL;
+
+ FOR_GRUB_MTAB (m)
+ {
+ if (grub_strncmp (path, m->path, grub_strlen (m->path)) == 0)
+ {
+ if (!cm)
+ cm = m;
+ else
+ if (grub_strcmp (m->path, cm->path) > 0)
+ cm = m;
+ }
+ }
+
+ if (cm)
+ {
+ const char *s = path + grub_strlen (cm->path);
+ *path_in_subvol = (s[0] == '\0') ? grub_strdup ("/") : grub_strdup (s);
+ tree = cm->tree;
+ }
+
+ return tree;
+}
+
static grub_err_t
get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree,
grub_uint64_t objectid, grub_uint64_t offset,
@@ -2666,6 +2853,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 char *
@@ -2729,6 +2917,9 @@ GRUB_MOD_INIT (btrfs)
cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info,
"DEVICE",
"Print BtrFS info about DEVICE.");
+ cmd_mount_subvol = grub_register_command("btrfs-mount-subvol", grub_cmd_btrfs_mount_subvol,
+ "DEVICE DIRECTORY SUBVOL",
+ "Set btrfs DEVICE the DIRECTORY a mountpoint of SUBVOL.");
cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols",
grub_cmd_btrfs_list_subvols, 0,
"[-p|-n] [-o var] DEVICE",
diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
index 5d50dd6f8dc..4c5a13022dc 100644
--- a/grub-core/osdep/linux/getroot.c
+++ b/grub-core/osdep/linux/getroot.c
@@ -107,6 +107,14 @@ struct btrfs_ioctl_search_key
grub_uint32_t unused[9];
};
+struct btrfs_ioctl_search_header {
+ grub_uint64_t transid;
+ grub_uint64_t objectid;
+ grub_uint64_t offset;
+ grub_uint32_t type;
+ grub_uint32_t len;
+};
+
struct btrfs_ioctl_search_args {
struct btrfs_ioctl_search_key key;
grub_uint64_t buf[(4096 - sizeof(struct btrfs_ioctl_search_key))
@@ -378,6 +386,109 @@ get_btrfs_fs_prefix (const char *mount_path)
int use_relative_path_on_btrfs = 0;
+static char *
+get_btrfs_subvol (const char *path)
+{
+ struct btrfs_ioctl_ino_lookup_args args;
+ grub_uint64_t tree_id;
+ int fd = -1;
+ char *ret = NULL;
+
+ fd = open (path, O_RDONLY);
+
+ if (fd < 0)
+ return NULL;
+
+ memset (&args, 0, sizeof(args));
+ args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID;
+
+ if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
+ goto error;
+
+ tree_id = args.treeid;
+
+ while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID)
+ {
+ struct btrfs_ioctl_search_args sargs;
+ struct grub_btrfs_root_backref *br;
+ struct btrfs_ioctl_search_header *search_header;
+ char *old;
+ grub_uint16_t len;
+ grub_uint64_t inode_id;
+
+ memset (&sargs, 0, sizeof(sargs));
+
+ sargs.key.tree_id = 1;
+ sargs.key.min_objectid = tree_id;
+ sargs.key.max_objectid = tree_id;
+
+ sargs.key.min_offset = 0;
+ sargs.key.max_offset = ~0ULL;
+ sargs.key.min_transid = 0;
+ sargs.key.max_transid = ~0ULL;
+ sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
+ sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
+
+ sargs.key.nr_items = 1;
+
+ if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
+ goto error;
+
+ if (sargs.key.nr_items == 0)
+ goto error;
+
+ search_header = (struct btrfs_ioctl_search_header *)sargs.buf;
+ br = (struct grub_btrfs_root_backref *) (search_header + 1);
+
+ len = grub_le_to_cpu16 (br->n);
+ inode_id = grub_le_to_cpu64 (br->inode_id);
+ tree_id = search_header->offset;
+
+ old = ret;
+ ret = malloc (len + 1);
+ memcpy (ret, br->name, len);
+ ret[len] = '\0';
+
+ if (inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID)
+ {
+ char *s;
+
+ memset(&args, 0, sizeof(args));
+ args.treeid = search_header->offset;
+ args.objectid = inode_id;
+
+ if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
+ goto error;
+
+ s = xasprintf ("%s%s", args.name, ret);
+ free (ret);
+ ret = s;
+ }
+
+ if (old)
+ {
+ char *s = xasprintf ("%s/%s", ret, old);
+ free (ret);
+ free (old);
+ ret = s;
+ }
+ }
+
+ close (fd);
+ return ret;
+
+error:
+
+ if (fd >= 0)
+ close (fd);
+ if (ret)
+ free (ret);
+
+ return NULL;
+}
+
+void (*grub_find_root_btrfs_mount_path_hook)(const char *mount_path);
+
char **
grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
{
@@ -519,12 +630,15 @@ again:
else if (grub_strcmp (entries[i].fstype, "btrfs") == 0)
{
ret = grub_find_root_devices_from_btrfs (dir);
- fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
if (use_relative_path_on_btrfs)
{
- if (fs_prefix)
- free (fs_prefix);
fs_prefix = xstrdup ("/");
+ if (grub_find_root_btrfs_mount_path_hook)
+ grub_find_root_btrfs_mount_path_hook (entries[i].enc_path);
+ }
+ else
+ {
+ fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
}
}
else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0)
@@ -1150,6 +1264,34 @@ grub_util_get_grub_dev_os (const char *os_dev)
return grub_dev;
}
+
+char *
+grub_util_get_btrfs_subvol (const char *path, char **mount_path)
+{
+ char *mp = NULL;
+
+ if (mount_path)
+ *mount_path = NULL;
+
+ auto void
+ mount_path_hook (const char *m)
+ {
+ mp = strdup (m);
+ }
+
+ grub_find_root_btrfs_mount_path_hook = mount_path_hook;
+ grub_free (grub_find_root_devices_from_mountinfo (path, NULL));
+ grub_find_root_btrfs_mount_path_hook = NULL;
+
+ if (!mp)
+ return NULL;
+
+ if (mount_path)
+ *mount_path = mp;
+
+ return get_btrfs_subvol (mp);
+}
+
char *
grub_make_system_path_relative_to_its_root_os (const char *path)
{
diff --git a/util/grub-install.c b/util/grub-install.c
index 0e807b09c36..3e718b9e3fb 100644
--- a/util/grub-install.c
+++ b/util/grub-install.c
@@ -1561,6 +1561,55 @@ main (int argc, char *argv[])
prefix_drive = xasprintf ("(%s)", grub_drives[0]);
}
+#ifdef __linux__
+
+ if (config.is_suse_btrfs_snapshot_enabled
+ && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0)
+ {
+ char *subvol = NULL;
+ char *mount_path = NULL;
+ char **rootdir_devices = NULL;
+ char *rootdir_path = grub_util_path_concat (2, "/", rootdir);
+
+ if (grub_util_is_directory (rootdir_path))
+ rootdir_devices = grub_guess_root_devices (rootdir_path);
+
+ free (rootdir_path);
+
+ if (rootdir_devices && rootdir_devices[0])
+ if (grub_strcmp (rootdir_devices[0], grub_devices[0]) == 0)
+ subvol = grub_util_get_btrfs_subvol (platdir, &mount_path);
+
+ if (subvol && mount_path)
+ {
+ char *def_subvol;
+
+ def_subvol = grub_util_get_btrfs_subvol ("/", NULL);
+
+ if (def_subvol)
+ {
+ if (!load_cfg_f)
+ load_cfg_f = grub_util_fopen (load_cfg, "wb");
+ have_load_cfg = 1;
+
+ if (grub_strcmp (subvol, def_subvol) != 0)
+ fprintf (load_cfg_f, "btrfs-mount-subvol ($root) %s %s\n", mount_path, subvol);
+ free (def_subvol);
+ }
+ }
+
+ for (curdev = rootdir_devices; *curdev; curdev++)
+ free (*curdev);
+ if (rootdir_devices)
+ free (rootdir_devices);
+ if (subvol)
+ free (subvol);
+ if (mount_path)
+ free (mount_path);
+ }
+
+#endif
+
char mkimage_target[200];
const char *core_name = NULL;
diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h
index 73fa2d34abb..9c642ae3fe3 100644
--- a/include/grub/emu/getroot.h
+++ b/include/grub/emu/getroot.h
@@ -53,6 +53,11 @@ char **
grub_find_root_devices_from_mountinfo (const char *dir, char **relroot);
#endif
+#ifdef __linux__
+char *
+grub_util_get_btrfs_subvol (const char *path, char **mount_path);
+#endif
+
/* Devmapper functions provided by getroot_devmapper.c. */
void
grub_util_pull_devmapper (const char *os_dev);