From f04c72b44efdaa9c41a8d4011d070dbcee781328 Mon Sep 17 00:00:00 2001
From: Vladimir Serbinenko <phcoder@gmail.com>
Date: Mon, 28 Oct 2013 01:37:19 +0100
Subject: [PATCH 07/14] Make / in btrfs refer to real root, not the default
 volume. Modify mkrelpath to work even if device is mounted with subvolid
 option.

---
 ChangeLog            |  5 ++++
 grub-core/fs/btrfs.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/grub/btrfs.h | 71 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 159 insertions(+)
 create mode 100644 include/grub/btrfs.h

diff --git a/ChangeLog b/ChangeLog
index d098ce3..1f9193e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+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-25  Vladimir Serbinenko  <phcoder@gmail.com>
 
 	* conf/Makefile.common (CPPFLAGS_KERNEL): Add -DGRUB_KERNEL=1.
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index a993f07..d9f928a 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -28,6 +28,9 @@
 #include <grub/deflate.h>
 #include <minilzo.h>
 #include <grub/i18n.h>
+#ifdef GRUB_MACHINE_XEN
+#include <grub/btrfs.h>
+#endif
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -106,6 +109,7 @@ struct grub_btrfs_data
   struct grub_btrfs_extent_data *extent;
 };
 
+#ifndef GRUB_MACHINE_XEN
 enum
   {
     GRUB_BTRFS_ITEM_TYPE_INODE_ITEM = 0x01,
@@ -123,6 +127,7 @@ struct grub_btrfs_key
   grub_uint8_t type;
   grub_uint64_t offset;
 } __attribute__ ((packed));
+#endif
 
 struct grub_btrfs_chunk_item
 {
@@ -188,12 +193,14 @@ struct grub_btrfs_leaf_descriptor
   } *data;
 };
 
+#ifndef GRUB_MACHINE_XEN
 struct grub_btrfs_root_item
 {
   grub_uint8_t dummy[0xb0];
   grub_uint64_t tree;
   grub_uint64_t inode;
 };
+#endif
 
 struct grub_btrfs_time
 {
@@ -1181,6 +1188,42 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data,
   return pos - pos0;
 }
 
+#ifdef GRUB_MACHINE_XEN
+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;
+}
+#endif
+
 static grub_err_t
 find_path (struct grub_btrfs_data *data,
 	   const char *path, struct grub_btrfs_key *key,
@@ -1193,25 +1236,35 @@ 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;
+#ifndef GRUB_MACHINE_XEN
   int skip_default;
+#endif
   const char *ctoken;
   grub_size_t ctokenlen;
   char *path_alloc = NULL;
   char *origpath = NULL;
   unsigned symlinks_max = 32;
 
+#ifndef GRUB_MACHINE_XEN
   *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;
+#else
+  err = get_root (data, key, tree, type);
+  if (err)
+    return err;
+#endif
+
   origpath = grub_strdup (path);
   if (!origpath)
     return grub_errno;
 
   while (1)
     {
+#ifndef GRUB_MACHINE_XEN
       if (!skip_default)
 	{
 	  while (path[0] == '/')
@@ -1229,6 +1282,17 @@ find_path (struct grub_btrfs_data *data,
 	  ctoken = "default";
 	  ctokenlen = sizeof ("default") - 1;
 	}
+#else
+      while (path[0] == '/')
+	path++;
+      if (!path[0])
+	break;
+      slash = grub_strchr (path, '/');
+      if (!slash)
+	slash = path + grub_strlen (path);
+      ctoken = path;
+      ctokenlen = slash - path;
+#endif
 
       if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
 	{
@@ -1239,10 +1303,15 @@ find_path (struct grub_btrfs_data *data,
 
       if (ctokenlen == 1 && ctoken[0] == '.')
 	{
+#ifndef GRUB_MACHINE_XEN
 	  if (!skip_default)
 	    path = slash;
 	  skip_default = 0;
 	continue;
+#else
+	  path = slash;
+	  continue;
+#endif
 	}
       if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.')
 	{
@@ -1272,9 +1341,13 @@ find_path (struct grub_btrfs_data *data,
 	  *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
 	  key->object_id = key_out.offset;
 
+#ifndef GRUB_MACHINE_XEN
 	  if (!skip_default)
 	    path = slash;
 	  skip_default = 0;
+#else
+	  path = slash;
+#endif
 
 	  continue;
 	}
@@ -1344,9 +1417,13 @@ find_path (struct grub_btrfs_data *data,
 	  return err;
 	}
 
+#ifndef GRUB_MACHINE_XEN
       if (!skip_default)
 	path = slash;
       skip_default = 0;
+#else
+      path = slash;
+#endif
       if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
 	{
 	  struct grub_btrfs_inode inode;
@@ -1396,12 +1473,18 @@ find_path (struct grub_btrfs_data *data,
 	  path = path_alloc = tmp;
 	  if (path[0] == '/')
 	    {
+#ifndef GRUB_MACHINE_XEN
 	      *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;
+#else
+	      err = get_root (data, key, tree, type);
+	      if (err)
+		return err;
+#endif
 	    }
 	  continue;
 	}
diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h
new file mode 100644
index 0000000..77531d1
--- /dev/null
+++ b/include/grub/btrfs.h
@@ -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
-- 
1.8.1.4

