Blame SOURCES/0093-grub2-btrfs-06-subvol-mount.patch

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