]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Make / in btrfs refer to real root, not the default volume.
authorVladimir Serbinenko <phcoder@gmail.com>
Mon, 28 Oct 2013 00:37:19 +0000 (01:37 +0100)
committerVladimir Serbinenko <phcoder@gmail.com>
Mon, 28 Oct 2013 00:37:19 +0000 (01:37 +0100)
Modify mkrelpath to work even if device is mounted with subvolid option.

ChangeLog
grub-core/fs/btrfs.c
grub-core/osdep/linux/getroot.c
grub-core/osdep/unix/relpath.c
include/grub/btrfs.h [new file with mode: 0644]
include/grub/emu/getroot.h

index d697f20af25d6317b624eb250548388dd71c20bf..625385181b0fb8bb85b1df627099d563192f0f2a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,11 @@
-2013-10-14  Andrey Borzenkov <arvidjaar@gmail.com>
+2013-10-28  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Make / in btrfs refer to real root, not the default volume.
+       Modify mkrelpath to work even if device is mounted with subvolid option.
+
+2013-10-28  Andrey Borzenkov <arvidjaar@gmail.com>
 
-       *  Makefile.util.def: Add grub-core/kern/disk_common.c to library
+       * Makefile.util.def: Add grub-core/kern/disk_common.c to library
        extra_dist.
        * grub-core/Makefile.core.def: Add kern/disk_common.c to disk module
        extra_dist.
index 196f3017c744c385d35cfef370b101b4b7cf4f60..49f11cc3c1b337df399a384f09fee643abdd25b6 100644 (file)
@@ -1,7 +1,7 @@
 /* btrfs.c - B-tree file system.  */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2010  Free Software Foundation, Inc.
+ *  Copyright (C) 2010,2011,2012,2013  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
 #include <grub/deflate.h>
 #include <minilzo.h>
 #include <grub/i18n.h>
+#include <grub/btrfs.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -106,24 +107,6 @@ struct grub_btrfs_data
   struct grub_btrfs_extent_data *extent;
 };
 
-enum
-  {
-    GRUB_BTRFS_ITEM_TYPE_INODE_ITEM = 0x01,
-    GRUB_BTRFS_ITEM_TYPE_INODE_REF = 0x0c,
-    GRUB_BTRFS_ITEM_TYPE_DIR_ITEM = 0x54,
-    GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM = 0x6c,
-    GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84,
-    GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8,
-    GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4
-  };
-
-struct grub_btrfs_key
-{
-  grub_uint64_t object_id;
-  grub_uint8_t type;
-  grub_uint64_t offset;
-} __attribute__ ((packed));
-
 struct grub_btrfs_chunk_item
 {
   grub_uint64_t size;
@@ -188,13 +171,6 @@ struct grub_btrfs_leaf_descriptor
   } *data;
 };
 
-struct grub_btrfs_root_item
-{
-  grub_uint8_t dummy[0xb0];
-  grub_uint64_t tree;
-  grub_uint64_t inode;
-};
-
 struct grub_btrfs_time
 {
   grub_int64_t sec;
@@ -1196,6 +1172,40 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data,
   return pos - pos0;
 }
 
+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_err_t err;
+  grub_disk_addr_t elemaddr;
+  grub_size_t elemsize;
+  struct grub_btrfs_key key_out, key_in;
+  struct grub_btrfs_root_item ri;
+
+  key_in.object_id = GRUB_BTRFS_ROOT_VOL_OBJECTID;
+  key_in.offset = 0;
+  key_in.type = GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM;
+  err = lower_bound (data, &key_in, &key_out,
+                    data->sblock.root_tree,
+                    &elemaddr, &elemsize, NULL, 0);
+  if (err)
+    return err;
+  if (key_in.object_id != key_out.object_id
+      || key_in.type != key_out.type
+      || key_in.offset != key_out.offset)
+    return grub_error (GRUB_ERR_BAD_FS, "no root");
+  err = grub_btrfs_read_logical (data, elemaddr, &ri,
+                                sizeof (ri), 0);
+  if (err)
+    return err;
+  key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
+  key->offset = 0;
+  key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
+  *tree = ri.tree;
+  *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 find_path (struct grub_btrfs_data *data,
           const char *path, struct grub_btrfs_key *key,
@@ -1208,42 +1218,31 @@ find_path (struct grub_btrfs_data *data,
   grub_size_t allocated = 0;
   struct grub_btrfs_dir_item *direl = NULL;
   struct grub_btrfs_key key_out;
-  int skip_default;
   const char *ctoken;
   grub_size_t ctokenlen;
   char *path_alloc = NULL;
   char *origpath = NULL;
   unsigned symlinks_max = 32;
 
-  *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
-  *tree = data->sblock.root_tree;
-  key->object_id = data->sblock.root_dir_objectid;
-  key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
-  key->offset = 0;
-  skip_default = 1;
+  err = get_root (data, key, tree, type);
+  if (err)
+    return err;
+
   origpath = grub_strdup (path);
   if (!origpath)
     return grub_errno;
 
   while (1)
     {
-      if (!skip_default)
-       {
-         while (path[0] == '/')
-           path++;
-         if (!path[0])
-           break;
-         slash = grub_strchr (path, '/');
-         if (!slash)
-           slash = path + grub_strlen (path);
-         ctoken = path;
-         ctokenlen = slash - path;
-       }
-      else
-       {
-         ctoken = "default";
-         ctokenlen = sizeof ("default") - 1;
-       }
+      while (path[0] == '/')
+       path++;
+      if (!path[0])
+       break;
+      slash = grub_strchr (path, '/');
+      if (!slash)
+       slash = path + grub_strlen (path);
+      ctoken = path;
+      ctokenlen = slash - path;
 
       if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
        {
@@ -1254,10 +1253,8 @@ find_path (struct grub_btrfs_data *data,
 
       if (ctokenlen == 1 && ctoken[0] == '.')
        {
-         if (!skip_default)
-           path = slash;
-         skip_default = 0;
-       continue;
+         path = slash;
+         continue;
        }
       if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.')
        {
@@ -1287,9 +1284,7 @@ find_path (struct grub_btrfs_data *data,
          *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
          key->object_id = key_out.offset;
 
-         if (!skip_default)
-           path = slash;
-         skip_default = 0;
+         path = slash;
 
          continue;
        }
@@ -1359,9 +1354,7 @@ find_path (struct grub_btrfs_data *data,
          return err;
        }
 
-      if (!skip_default)
-       path = slash;
-      skip_default = 0;
+      path = slash;
       if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
        {
          struct grub_btrfs_inode inode;
@@ -1411,12 +1404,9 @@ find_path (struct grub_btrfs_data *data,
          path = path_alloc = tmp;
          if (path[0] == '/')
            {
-             *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
-             *tree = data->sblock.root_tree;
-             key->object_id = data->sblock.root_dir_objectid;
-             key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
-             key->offset = 0;
-             skip_default = 1;
+             err = get_root (data, key, tree, type);
+             if (err)
+               return err;
            }
          continue;
        }
index 77d80838efd4e88ac097293d108693da8fd25f7a..9bf841fa5500f15826da3603b9690329e954c9a6 100644 (file)
 
 #include <sys/wait.h>
 
+#include <btrfs/ioctl.h>
 #include <linux/types.h>
 #include <linux/major.h>
 #include <linux/raid/md_p.h>
 #include <linux/raid/md_u.h>
 #include <grub/i18n.h>
 #include <grub/emu/exec.h>
+#include <grub/btrfs.h>
 
 #define LVM_DEV_MAPPER_STRING "/dev/mapper/"
 
+#if 0
 /* Defines taken from btrfs/ioctl.h.  */
 
 struct btrfs_ioctl_dev_info_args
@@ -82,7 +85,7 @@ struct btrfs_ioctl_fs_info_args
                                  struct btrfs_ioctl_dev_info_args)
 #define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \
                                struct btrfs_ioctl_fs_info_args)
-
+#endif
 static int
 grub_util_is_imsm (const char *os_dev);
 
@@ -224,6 +227,117 @@ grub_find_root_devices_from_btrfs (const char *dir)
   return ret;
 }
 
+static char *
+get_btrfs_fs_prefix (const char *mount_path)
+{
+  struct btrfs_ioctl_ino_lookup_args args;
+  struct stat st;
+  int fd;
+  grub_uint64_t tree_id, inode_id;
+  char *ret = NULL;
+
+  fd = open (mount_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)
+    return NULL;
+  tree_id = args.treeid;
+
+  if (fstat (fd, &st) < 0)
+    return NULL;
+  inode_id = st.st_ino;
+
+  while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID
+        || inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID)
+    {
+      grub_uint64_t *nid;
+      const char *name;
+      size_t namelen;
+      struct btrfs_ioctl_search_args sargs;
+      char *old;
+
+      memset (&sargs, 0, sizeof(sargs));
+
+      if (inode_id == GRUB_BTRFS_TREE_ROOT_OBJECTID)
+       {
+         struct grub_btrfs_root_backref *br;
+
+         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)
+           return NULL;
+
+         if (sargs.key.nr_items == 0)
+           return NULL;
+      
+         nid = (grub_uint64_t *) (sargs.buf + 16);
+         tree_id = *nid;
+         br = (struct grub_btrfs_root_backref *) (sargs.buf + 32);
+         inode_id = br->inode_id;
+         name = br->name;
+         namelen = br->n;
+       }
+      else
+       {
+         struct grub_btrfs_inode_ref *ir;
+
+         sargs.key.tree_id = tree_id;
+         sargs.key.min_objectid = inode_id;
+         sargs.key.max_objectid = inode_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_INODE_REF;
+         sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF;
+
+         if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
+           return NULL;
+
+         if (sargs.key.nr_items == 0)
+           return NULL;
+
+         nid = (grub_uint64_t *) (sargs.buf + 16);
+         inode_id = *nid;
+
+         ir = (struct grub_btrfs_inode_ref *) (sargs.buf + 32);
+         name = ir->name;
+         namelen = ir->n;
+       }
+      old = ret;
+      ret = xmalloc (namelen + (old ? strlen (old) : 0) + 2);
+      ret[0] = '/';
+      memcpy (ret + 1, name, namelen);
+      if (old)
+       {
+         strcpy (ret + 1 + namelen, old);
+         free (old);
+       }
+      else
+       ret[1+namelen] = '\0';
+    }
+  if (!ret)
+    return xstrdup ("/");
+  return ret;
+}
+
+
 char **
 grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
 {
@@ -332,6 +446,7 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
   for (i = entry_len - 1; i >= 0; i--)
     {
       char **ret = NULL;
+      char *fs_prefix = NULL;
       if (!*entries[i].device)
        continue;
 
@@ -348,45 +463,52 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
          if (relroot)
            {
              if (!slash)
-               *relroot = xasprintf ("/@%s", entries[i].enc_root);
+               fs_prefix = xasprintf ("/@%s", entries[i].enc_root);
              else if (strchr (slash + 1, '@'))
-               *relroot = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
+               fs_prefix = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
              else
-               *relroot = xasprintf ("/%s@%s", slash + 1, entries[i].enc_root);
+               fs_prefix = xasprintf ("/%s@%s", slash + 1,
+                                      entries[i].enc_root);
            }
        }
       else if (grub_strcmp (entries[i].fstype, "btrfs") == 0)
        {
          ret = grub_find_root_devices_from_btrfs (dir);
-         if (relroot)
-           {
-             char *ptr;
-             *relroot = xmalloc (strlen (entries[i].enc_root) +
-                                 2 + strlen (dir));
-             ptr = grub_stpcpy (*relroot, entries[i].enc_root);
-             if (strlen (dir) > strlen (entries[i].enc_path))
-               {
-                 while (ptr > *relroot && *(ptr - 1) == '/')
-                   ptr--;
-                 if (dir[strlen (entries[i].enc_path)] != '/')
-                   *ptr++ = '/';
-                 ptr = grub_stpcpy (ptr, dir + strlen (entries[i].enc_path));
-               }
-             *ptr = 0;
-           }
+         fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
        }
       if (!ret)
        {
          ret = xmalloc (2 * sizeof (ret[0]));
          ret[0] = strdup (entries[i].device);
          ret[1] = 0;
-         if (relroot)
-           *relroot = strdup (entries[i].enc_root);
        }
-       free (buf);
-       free (entries);
-       fclose (fp);
-       return ret;
+      if (!fs_prefix)
+       fs_prefix = entries[i].enc_root;
+      if (relroot)
+       {
+         char *ptr;
+         grub_size_t enc_root_len = strlen (fs_prefix);
+         grub_size_t enc_path_len = strlen (entries[i].enc_path);
+         grub_size_t dir_strlen = strlen (dir);
+         *relroot = xmalloc (enc_root_len +
+                             2 + dir_strlen);
+         ptr = grub_stpcpy (*relroot, fs_prefix);
+         if (dir_strlen > enc_path_len)
+           {
+             while (ptr > *relroot && *(ptr - 1) == '/')
+               ptr--;
+             if (dir[enc_path_len] != '/')
+               *ptr++ = '/';
+             ptr = grub_stpcpy (ptr, dir + enc_path_len);
+           }
+         *ptr = 0;
+       }
+      if (fs_prefix != entries[i].enc_root)
+       free (fs_prefix);
+      free (buf);
+      free (entries);
+      fclose (fp);
+      return ret;
     }
 
   free (buf);
@@ -913,3 +1035,23 @@ grub_util_get_grub_dev_os (const char *os_dev)
 
   return grub_dev;
 }
+
+char *
+grub_make_system_path_relative_to_its_root_os (const char *path)
+{
+  char *bind = NULL;
+  grub_size_t len;
+  grub_free (grub_find_root_devices_from_mountinfo (path, &bind));
+  if (bind && bind[0])
+    {
+      len = strlen (bind);
+      while (len > 0 && bind[len - 1] == '/')
+       {
+         bind[len - 1] = '\0';
+         len--;
+       }
+      return bind;
+    }
+  grub_free (bind);
+  return NULL;
+}
index 56d3923a8584228d9fc32551c7cffbfb6594699d..71c19d86728ada6f207a59489bdc351acd01fb77 100644 (file)
@@ -48,7 +48,13 @@ grub_make_system_path_relative_to_its_root (const char *path)
   if (p == NULL)
     grub_util_error (_("failed to get canonical path of `%s'"), path);
 
-  /* For ZFS sub-pool filesystems, could be extended to others (btrfs?).  */
+#ifdef __linux__
+  ret = grub_make_system_path_relative_to_its_root_os (p);
+  if (ret)
+    return ret;
+#endif
+
+  /* For ZFS sub-pool filesystems.  */
 #ifndef __HAIKU__
   {
     char *dummy;
@@ -98,18 +104,6 @@ grub_make_system_path_relative_to_its_root (const char *path)
          if (offset == 0)
            {
              free (buf);
-#ifdef __linux__
-             {
-               char *bind;
-               grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind));
-               if (bind && bind[0] && bind[1])
-                 {
-                   buf3 = bind;
-                   goto parsedir;
-                 }
-               grub_free (bind);
-             }
-#endif
              free (buf2);
              if (poolfs)
                return xasprintf ("/%s/@", poolfs);
@@ -131,25 +125,9 @@ grub_make_system_path_relative_to_its_root (const char *path)
   free (buf);
   buf3 = xstrdup (buf2 + offset);
   buf2[offset] = 0;
-#ifdef __linux__
-  {
-    char *bind;
-    grub_free (grub_find_root_devices_from_mountinfo (buf2, &bind));
-    if (bind && bind[0] && bind[1])
-      {
-       char *temp = buf3;
-       buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3);
-       grub_free (temp);
-      }
-    grub_free (bind);
-  }
-#endif
   
   free (buf2);
 
-#ifdef __linux__
- parsedir:
-#endif
   /* Remove trailing slashes, return empty string if root directory.  */
   len = strlen (buf3);
   while (len > 0 && buf3[len - 1] == '/')
diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h
new file mode 100644 (file)
index 0000000..77531d1
--- /dev/null
@@ -0,0 +1,71 @@
+/* btrfs.c - B-tree file system.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2010,2011,2012,2013  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_BTRFS_H
+#define GRUB_BTRFS_H   1
+
+enum
+  {
+    GRUB_BTRFS_ITEM_TYPE_INODE_ITEM = 0x01,
+    GRUB_BTRFS_ITEM_TYPE_INODE_REF = 0x0c,
+    GRUB_BTRFS_ITEM_TYPE_DIR_ITEM = 0x54,
+    GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM = 0x6c,
+    GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84,
+    GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90,
+    GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8,
+    GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4
+  };
+
+enum
+  {
+    GRUB_BTRFS_ROOT_VOL_OBJECTID = 5,
+    GRUB_BTRFS_TREE_ROOT_OBJECTID = 0x100,
+  };
+
+struct grub_btrfs_root_item
+{
+  grub_uint8_t dummy[0xb0];
+  grub_uint64_t tree;
+  grub_uint64_t inode;
+};
+
+struct grub_btrfs_key
+{
+  grub_uint64_t object_id;
+  grub_uint8_t type;
+  grub_uint64_t offset;
+} __attribute__ ((packed));
+
+
+struct grub_btrfs_root_backref
+{
+  grub_uint64_t inode_id;
+  grub_uint64_t seqnr;
+  grub_uint16_t n;
+  char name[0];
+};
+
+struct grub_btrfs_inode_ref
+{
+  grub_uint64_t idxid;
+  grub_uint16_t n;
+  char name[0];
+};
+
+#endif
index 6684e372a5a6d26c5f43ffe947e8c413697fb8fd..675cf78be0b15c91ed641801f2d175a36ec4e030 100644 (file)
@@ -36,6 +36,8 @@ void grub_util_pull_device (const char *osname);
 char **grub_guess_root_devices (const char *dir);
 int grub_util_get_dev_abstraction (const char *os_dev);
 char *grub_make_system_path_relative_to_its_root (const char *path);
+char *
+grub_make_system_path_relative_to_its_root_os (const char *path);
 char *grub_util_get_grub_dev (const char *os_dev);
 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
 void grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out,