Blame SOURCES/0170-xfs-V5-filesystem-format-support.patch

f731ee
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
f731ee
From: Jan Kara <jack@suse.cz>
f731ee
Date: Mon, 1 Jun 2015 14:28:46 +0200
f731ee
Subject: [PATCH] xfs: V5 filesystem format support
f731ee
f731ee
Add support for new XFS on disk format. We have to handle optional
f731ee
filetype fields in directory entries, additional CRC, LSN, UUID entries
f731ee
in some structures, etc.
f731ee
f731ee
Signed-off-by: Jan Kara <jack@suse.cz>
f731ee
---
f731ee
 grub-core/fs/xfs.c | 332 ++++++++++++++++++++++++++++++++++++++++-------------
f731ee
 1 file changed, 252 insertions(+), 80 deletions(-)
f731ee
f731ee
diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c
f731ee
index 26d8147a92e..f00e43e7dc3 100644
f731ee
--- a/grub-core/fs/xfs.c
f731ee
+++ b/grub-core/fs/xfs.c
f731ee
@@ -34,6 +34,50 @@ GRUB_MOD_LICENSE ("GPLv3+");
f731ee
 #define XFS_INODE_FORMAT_EXT	2
f731ee
 #define XFS_INODE_FORMAT_BTREE	3
f731ee
 
f731ee
+/* Superblock version field flags */
f731ee
+#define XFS_SB_VERSION_NUMBITS		0x000f
f731ee
+#define	XFS_SB_VERSION_ATTRBIT		0x0010
f731ee
+#define	XFS_SB_VERSION_NLINKBIT		0x0020
f731ee
+#define	XFS_SB_VERSION_QUOTABIT		0x0040
f731ee
+#define	XFS_SB_VERSION_ALIGNBIT		0x0080
f731ee
+#define	XFS_SB_VERSION_DALIGNBIT	0x0100
f731ee
+#define	XFS_SB_VERSION_LOGV2BIT		0x0400
f731ee
+#define	XFS_SB_VERSION_SECTORBIT	0x0800
f731ee
+#define	XFS_SB_VERSION_EXTFLGBIT	0x1000
f731ee
+#define	XFS_SB_VERSION_DIRV2BIT		0x2000
f731ee
+#define XFS_SB_VERSION_MOREBITSBIT	0x8000
f731ee
+#define XFS_SB_VERSION_BITS_SUPPORTED \
f731ee
+	(XFS_SB_VERSION_NUMBITS | \
f731ee
+	 XFS_SB_VERSION_ATTRBIT | \
f731ee
+	 XFS_SB_VERSION_NLINKBIT | \
f731ee
+	 XFS_SB_VERSION_QUOTABIT | \
f731ee
+	 XFS_SB_VERSION_ALIGNBIT | \
f731ee
+	 XFS_SB_VERSION_DALIGNBIT | \
f731ee
+	 XFS_SB_VERSION_LOGV2BIT | \
f731ee
+	 XFS_SB_VERSION_SECTORBIT | \
f731ee
+	 XFS_SB_VERSION_EXTFLGBIT | \
f731ee
+	 XFS_SB_VERSION_DIRV2BIT | \
f731ee
+	 XFS_SB_VERSION_MOREBITSBIT)
f731ee
+
f731ee
+/* Recognized xfs format versions */
f731ee
+#define XFS_SB_VERSION_4		4	/* Good old XFS filesystem */
f731ee
+#define XFS_SB_VERSION_5		5	/* CRC enabled filesystem */
f731ee
+
f731ee
+/* features2 field flags */
f731ee
+#define XFS_SB_VERSION2_LAZYSBCOUNTBIT	0x00000002	/* Superblk counters */
f731ee
+#define XFS_SB_VERSION2_ATTR2BIT	0x00000008	/* Inline attr rework */
f731ee
+#define XFS_SB_VERSION2_PROJID32BIT	0x00000080	/* 32-bit project ids */
f731ee
+#define XFS_SB_VERSION2_FTYPE		0x00000200	/* inode type in dir */
f731ee
+#define XFS_SB_VERSION2_BITS_SUPPORTED \
f731ee
+	(XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
f731ee
+	 XFS_SB_VERSION2_ATTR2BIT | \
f731ee
+	 XFS_SB_VERSION2_PROJID32BIT | \
f731ee
+	 XFS_SB_VERSION2_FTYPE)
f731ee
+
f731ee
+/* incompat feature flags */
f731ee
+#define XFS_SB_FEAT_INCOMPAT_FTYPE      (1 << 0)        /* filetype in dirent */
f731ee
+#define XFS_SB_FEAT_INCOMPAT_SUPPORTED \
f731ee
+	(XFS_SB_FEAT_INCOMPAT_FTYPE)
f731ee
 
f731ee
 struct grub_xfs_sblock
f731ee
 {
f731ee
@@ -45,7 +89,9 @@ struct grub_xfs_sblock
f731ee
   grub_uint64_t rootino;
f731ee
   grub_uint8_t unused3[20];
f731ee
   grub_uint32_t agsize;
f731ee
-  grub_uint8_t unused4[20];
f731ee
+  grub_uint8_t unused4[12];
f731ee
+  grub_uint16_t version;
f731ee
+  grub_uint8_t unused5[6];
f731ee
   grub_uint8_t label[12];
f731ee
   grub_uint8_t log2_bsize;
f731ee
   grub_uint8_t log2_sect;
f731ee
@@ -54,12 +100,19 @@ struct grub_xfs_sblock
f731ee
   grub_uint8_t log2_agblk;
f731ee
   grub_uint8_t unused6[67];
f731ee
   grub_uint8_t log2_dirblk;
f731ee
+  grub_uint8_t unused7[7];
f731ee
+  grub_uint32_t features2;
f731ee
+  grub_uint8_t unused8[4];
f731ee
+  grub_uint32_t sb_features_compat;
f731ee
+  grub_uint32_t sb_features_ro_compat;
f731ee
+  grub_uint32_t sb_features_incompat;
f731ee
+  grub_uint32_t sb_features_log_incompat;
f731ee
 } GRUB_PACKED;
f731ee
 
f731ee
 struct grub_xfs_dir_header
f731ee
 {
f731ee
   grub_uint8_t count;
f731ee
-  grub_uint8_t smallino;
f731ee
+  grub_uint8_t largeino;
f731ee
   union
f731ee
   {
f731ee
     grub_uint32_t i4;
f731ee
@@ -67,14 +120,16 @@ struct grub_xfs_dir_header
f731ee
   } GRUB_PACKED parent;
f731ee
 } GRUB_PACKED;
f731ee
 
f731ee
+/* Structure for directory entry inlined in the inode */
f731ee
 struct grub_xfs_dir_entry
f731ee
 {
f731ee
   grub_uint8_t len;
f731ee
   grub_uint16_t offset;
f731ee
   char name[1];
f731ee
-  /* Inode number follows, 32 bits.  */
f731ee
+  /* Inode number follows, 32 / 64 bits.  */
f731ee
 } GRUB_PACKED;
f731ee
 
f731ee
+/* Structure for directory entry in a block */
f731ee
 struct grub_xfs_dir2_entry
f731ee
 {
f731ee
   grub_uint64_t inode;
f731ee
@@ -90,7 +145,8 @@ struct grub_xfs_btree_node
f731ee
   grub_uint16_t numrecs;
f731ee
   grub_uint64_t left;
f731ee
   grub_uint64_t right;
f731ee
-  grub_uint64_t keys[1];
f731ee
+  /* In V5 here follow crc, uuid, etc. */
f731ee
+  /* Then follow keys and block pointers */
f731ee
 }  GRUB_PACKED;
f731ee
 
f731ee
 struct grub_xfs_btree_root
f731ee
@@ -123,19 +179,11 @@ struct grub_xfs_inode
f731ee
   grub_uint16_t unused3;
f731ee
   grub_uint8_t fork_offset;
f731ee
   grub_uint8_t unused4[17];
f731ee
-  union
f731ee
-  {
f731ee
-    char raw[156];
f731ee
-    struct dir
f731ee
-    {
f731ee
-      struct grub_xfs_dir_header dirhead;
f731ee
-      struct grub_xfs_dir_entry direntry[1];
f731ee
-    } dir;
f731ee
-    grub_xfs_extent extents[XFS_INODE_EXTENTS];
f731ee
-    struct grub_xfs_btree_root btree;
f731ee
-  } GRUB_PACKED data;
f731ee
 } GRUB_PACKED;
f731ee
 
f731ee
+#define XFS_V2_INODE_SIZE sizeof(struct grub_xfs_inode)
f731ee
+#define XFS_V3_INODE_SIZE (XFS_V2_INODE_SIZE + 76)
f731ee
+
f731ee
 struct grub_xfs_dirblock_tail
f731ee
 {
f731ee
   grub_uint32_t leaf_count;
f731ee
@@ -157,6 +205,8 @@ struct grub_xfs_data
f731ee
   int pos;
f731ee
   int bsize;
f731ee
   grub_uint32_t agsize;
f731ee
+  unsigned int hasftype:1;
f731ee
+  unsigned int hascrc:1;
f731ee
   struct grub_fshelp_node diropen;
f731ee
 };
f731ee
 
f731ee
@@ -164,6 +214,71 @@ static grub_dl_t my_mod;
f731ee
 
f731ee
 
f731ee
 
f731ee
+static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
f731ee
+{
f731ee
+  return (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
f731ee
+	  grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5);
f731ee
+}
f731ee
+
f731ee
+static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
f731ee
+{
f731ee
+  if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
f731ee
+	grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5) &&
f731ee
+      data->sblock.sb_features_incompat & grub_cpu_to_be32_compile_time(XFS_SB_FEAT_INCOMPAT_FTYPE))
f731ee
+    return 1;
f731ee
+  if (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
f731ee
+      data->sblock.features2 & grub_cpu_to_be32_compile_time(XFS_SB_VERSION2_FTYPE))
f731ee
+    return 1;
f731ee
+  return 0;
f731ee
+}
f731ee
+
f731ee
+static int grub_xfs_sb_valid(struct grub_xfs_data *data)
f731ee
+{
f731ee
+  grub_dprintf("xfs", "Validating superblock\n");
f731ee
+  if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)
f731ee
+      || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS
f731ee
+      || ((int) data->sblock.log2_bsize
f731ee
+	  + (int) data->sblock.log2_dirblk) >= 27)
f731ee
+    {
f731ee
+      grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
f731ee
+      return 0;
f731ee
+    }
f731ee
+  if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
f731ee
+       grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5))
f731ee
+    {
f731ee
+      grub_dprintf("xfs", "XFS v5 superblock detected\n");
f731ee
+      if (data->sblock.sb_features_incompat &
f731ee
+          grub_cpu_to_be32_compile_time(~XFS_SB_FEAT_INCOMPAT_SUPPORTED))
f731ee
+        {
f731ee
+	  grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported "
f731ee
+		      "incompatible features");
f731ee
+	  return 0;
f731ee
+        }
f731ee
+      return 1;
f731ee
+    }
f731ee
+  else if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
f731ee
+	   grub_cpu_to_be16_compile_time(XFS_SB_VERSION_4))
f731ee
+    {
f731ee
+      grub_dprintf("xfs", "XFS v4 superblock detected\n");
f731ee
+      if (!(data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT)))
f731ee
+	{
f731ee
+	  grub_error (GRUB_ERR_BAD_FS, "XFS filesystem without V2 directories "
f731ee
+		      "is unsupported");
f731ee
+	  return 0;
f731ee
+	}
f731ee
+      if (data->sblock.version & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION_BITS_SUPPORTED) ||
f731ee
+	  (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
f731ee
+	   data->sblock.features2 & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION2_BITS_SUPPORTED)))
f731ee
+	{
f731ee
+	  grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported version "
f731ee
+		      "bits");
f731ee
+	  return 0;
f731ee
+	}
f731ee
+      return 1;
f731ee
+    }
f731ee
+  return 0;
f731ee
+}
f731ee
+
f731ee
 /* Filetype information as used in inodes.  */
f731ee
 #define FILETYPE_INO_MASK	0170000
f731ee
 #define FILETYPE_INO_REG	0100000
f731ee
@@ -219,18 +334,6 @@ GRUB_XFS_EXTENT_SIZE (grub_xfs_extent *exts, int ex)
f731ee
   return (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 21) - 1));
f731ee
 }
f731ee
 
f731ee
-static inline int
f731ee
-GRUB_XFS_ROUND_TO_DIRENT (int pos)
f731ee
-{
f731ee
-  return ((((pos) + 8 - 1) / 8) * 8);
f731ee
-}
f731ee
-
f731ee
-static inline int
f731ee
-GRUB_XFS_NEXT_DIRENT (int pos, int len)
f731ee
-{
f731ee
-  return (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2);
f731ee
-}
f731ee
-
f731ee
 
f731ee
 static inline grub_uint64_t
f731ee
 grub_xfs_inode_block (struct grub_xfs_data *data,
f731ee
@@ -274,6 +377,85 @@ grub_xfs_fshelp_size(struct grub_xfs_data *data)
f731ee
 	       + grub_xfs_inode_size(data);
f731ee
 }
f731ee
 
f731ee
+static void *
f731ee
+grub_xfs_inode_data(struct grub_xfs_inode *inode)
f731ee
+{
f731ee
+	if (inode->version <= 2)
f731ee
+		return ((char *)inode) + XFS_V2_INODE_SIZE;
f731ee
+	return ((char *)inode) + XFS_V3_INODE_SIZE;
f731ee
+}
f731ee
+
f731ee
+static struct grub_xfs_dir_entry *
f731ee
+grub_xfs_inline_de(struct grub_xfs_dir_header *head)
f731ee
+{
f731ee
+	/*
f731ee
+	 * With small inode numbers the header is 4 bytes smaller because of
f731ee
+	 * smaller parent pointer
f731ee
+	 */
f731ee
+	return (void *)(((char *)head) + sizeof(struct grub_xfs_dir_header) -
f731ee
+		(head->largeino ? 0 : sizeof(grub_uint32_t)));
f731ee
+}
f731ee
+
f731ee
+static grub_uint8_t *
f731ee
+grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
f731ee
+			  struct grub_xfs_dir_entry *de)
f731ee
+{
f731ee
+	return ((grub_uint8_t *)(de + 1)) + de->len - 1 +
f731ee
+		 (data->hasftype ? 1 : 0);
f731ee
+}
f731ee
+
f731ee
+static struct grub_xfs_dir_entry *
f731ee
+grub_xfs_inline_next_de(struct grub_xfs_data *data,
f731ee
+			struct grub_xfs_dir_header *head,
f731ee
+			struct grub_xfs_dir_entry *de)
f731ee
+{
f731ee
+  char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
f731ee
+
f731ee
+  p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
f731ee
+  if (data->hasftype)
f731ee
+    p++;
f731ee
+
f731ee
+  return (struct grub_xfs_dir_entry *)p;
f731ee
+}
f731ee
+
f731ee
+static struct grub_xfs_dirblock_tail *
f731ee
+grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
f731ee
+{
f731ee
+  int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
f731ee
+
f731ee
+  return (struct grub_xfs_dirblock_tail *)
f731ee
+    ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
f731ee
+}
f731ee
+
f731ee
+static struct grub_xfs_dir2_entry *
f731ee
+grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
f731ee
+{
f731ee
+  if (data->hascrc)
f731ee
+    return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
f731ee
+  return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
f731ee
+}
f731ee
+
f731ee
+static struct grub_xfs_dir2_entry *
f731ee
+grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
f731ee
+{
f731ee
+  int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
f731ee
+
f731ee
+  if (data->hasftype)
f731ee
+    size++;		/* File type */
f731ee
+  return (struct grub_xfs_dir2_entry *)(((char *)de) + ALIGN_UP(size, 8));
f731ee
+}
f731ee
+
f731ee
+static grub_uint64_t *
f731ee
+grub_xfs_btree_keys(struct grub_xfs_data *data,
f731ee
+		    struct grub_xfs_btree_node *leaf)
f731ee
+{
f731ee
+  grub_uint64_t *keys = (grub_uint64_t *)(leaf + 1);
f731ee
+
f731ee
+  if (data->hascrc)
f731ee
+    keys += 6;	/* skip crc, uuid, ... */
f731ee
+  return keys;
f731ee
+}
f731ee
+
f731ee
 static grub_err_t
f731ee
 grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
f731ee
 		     struct grub_xfs_inode *inode)
f731ee
@@ -281,6 +463,8 @@ grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
f731ee
   grub_uint64_t block = grub_xfs_inode_block (data, ino);
f731ee
   int offset = grub_xfs_inode_offset (data, ino);
f731ee
 
f731ee
+  grub_dprintf("xfs", "Reading inode (%"PRIuGRUB_UINT64_T") - %"PRIuGRUB_UINT64_T", %d\n",
f731ee
+	       ino, block, offset);
f731ee
   /* Read the inode.  */
f731ee
   if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
f731ee
 		      inode))
f731ee
@@ -303,6 +487,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
f731ee
 
f731ee
   if (node->inode.format == XFS_INODE_FORMAT_BTREE)
f731ee
     {
f731ee
+      struct grub_xfs_btree_root *root;
f731ee
       const grub_uint64_t *keys;
f731ee
       int recoffset;
f731ee
 
f731ee
@@ -310,15 +495,15 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
f731ee
       if (leaf == 0)
f731ee
         return 0;
f731ee
 
f731ee
-      nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs);
f731ee
-      keys = &node->inode.data.btree.keys[0];
f731ee
+      root = grub_xfs_inode_data(&node->inode);
f731ee
+      nrec = grub_be_to_cpu16 (root->numrecs);
f731ee
+      keys = &root->keys[0];
f731ee
       if (node->inode.fork_offset)
f731ee
 	recoffset = (node->inode.fork_offset - 1) / 2;
f731ee
       else
f731ee
 	recoffset = (grub_xfs_inode_size(node->data)
f731ee
-		     - ((char *) &node->inode.data.btree.keys
f731ee
-			- (char *) &node->inode))
f731ee
-	  / (2 * sizeof (grub_uint64_t));
f731ee
+		     - ((char *) keys - (char *) &node->inode))
f731ee
+				/ (2 * sizeof (grub_uint64_t));
f731ee
       do
f731ee
         {
f731ee
           int i;
f731ee
@@ -340,7 +525,10 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
f731ee
                               0, node->data->bsize, leaf))
f731ee
             return 0;
f731ee
 
f731ee
-          if (grub_strncmp ((char *) leaf->magic, "BMAP", 4))
f731ee
+	  if ((!node->data->hascrc &&
f731ee
+	       grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
f731ee
+	      (node->data->hascrc &&
f731ee
+	       grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
f731ee
             {
f731ee
               grub_free (leaf);
f731ee
               grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
f731ee
@@ -348,8 +536,8 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
f731ee
             }
f731ee
 
f731ee
           nrec = grub_be_to_cpu16 (leaf->numrecs);
f731ee
-          keys = &leaf->keys[0];
f731ee
-	  recoffset = ((node->data->bsize - ((char *) &leaf->keys
f731ee
+          keys = grub_xfs_btree_keys(node->data, leaf);
f731ee
+	  recoffset = ((node->data->bsize - ((char *) keys
f731ee
 					     - (char *) leaf))
f731ee
 		       / (2 * sizeof (grub_uint64_t)));
f731ee
 	}
f731ee
@@ -359,7 +547,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
f731ee
   else if (node->inode.format == XFS_INODE_FORMAT_EXT)
f731ee
     {
f731ee
       nrec = grub_be_to_cpu32 (node->inode.nextents);
f731ee
-      exts = &node->inode.data.extents[0];
f731ee
+      exts = grub_xfs_inode_data(&node->inode);
f731ee
     }
f731ee
   else
f731ee
     {
f731ee
@@ -417,7 +605,7 @@ grub_xfs_read_symlink (grub_fshelp_node_t node)
f731ee
   switch (node->inode.format)
f731ee
     {
f731ee
     case XFS_INODE_FORMAT_INO:
f731ee
-      return grub_strndup (node->inode.data.raw, size);
f731ee
+      return grub_strndup (grub_xfs_inode_data(&node->inode), size);
f731ee
 
f731ee
     case XFS_INODE_FORMAT_EXT:
f731ee
       {
f731ee
@@ -512,23 +700,18 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
f731ee
     {
f731ee
     case XFS_INODE_FORMAT_INO:
f731ee
       {
f731ee
-	struct grub_xfs_dir_entry *de = &diro->inode.data.dir.direntry[0];
f731ee
-	int smallino = !diro->inode.data.dir.dirhead.smallino;
f731ee
+	struct grub_xfs_dir_header *head = grub_xfs_inode_data(&diro->inode);
f731ee
+	struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
f731ee
+	int smallino = !head->largeino;
f731ee
 	int i;
f731ee
 	grub_uint64_t parent;
f731ee
 
f731ee
 	/* If small inode numbers are used to pack the direntry, the
f731ee
 	   parent inode number is small too.  */
f731ee
 	if (smallino)
f731ee
-	  {
f731ee
-	    parent = grub_be_to_cpu32 (diro->inode.data.dir.dirhead.parent.i4);
f731ee
-	    /* The header is a bit smaller than usual.  */
f731ee
-	    de = (struct grub_xfs_dir_entry *) ((char *) de - 4);
f731ee
-	  }
f731ee
+	  parent = grub_be_to_cpu32 (head->parent.i4);
f731ee
 	else
f731ee
-	  {
f731ee
-	    parent = grub_be_to_cpu64(diro->inode.data.dir.dirhead.parent.i8);
f731ee
-	  }
f731ee
+	  parent = grub_be_to_cpu64 (head->parent.i8);
f731ee
 
f731ee
 	/* Synthesize the direntries for `.' and `..'.  */
f731ee
 	if (iterate_dir_call_hook (diro->ino, ".", &ctx))
f731ee
@@ -537,12 +720,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
f731ee
 	if (iterate_dir_call_hook (parent, "..", &ctx))
f731ee
 	  return 1;
f731ee
 
f731ee
-	for (i = 0; i < diro->inode.data.dir.dirhead.count; i++)
f731ee
+	for (i = 0; i < head->count; i++)
f731ee
 	  {
f731ee
 	    grub_uint64_t ino;
f731ee
-	    grub_uint8_t *inopos = (((grub_uint8_t *) de)
f731ee
-			    + sizeof (struct grub_xfs_dir_entry)
f731ee
-			    + de->len - 1);
f731ee
+	    grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
f731ee
 	    grub_uint8_t c;
f731ee
 
f731ee
 	    /* inopos might be unaligned.  */
f731ee
@@ -567,10 +748,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
f731ee
 	      return 1;
f731ee
 	    de->name[de->len] = c;
f731ee
 
f731ee
-	    de = ((struct grub_xfs_dir_entry *)
f731ee
-		  (((char *) de)+ sizeof (struct grub_xfs_dir_entry) + de->len
f731ee
-		   + ((smallino ? sizeof (grub_uint32_t)
f731ee
-		       : sizeof (grub_uint64_t))) - 1));
f731ee
+	    de = grub_xfs_inline_next_de(dir->data, head, de);
f731ee
 	  }
f731ee
 	break;
f731ee
       }
f731ee
@@ -597,15 +775,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
f731ee
 		    >> dirblk_log2);
f731ee
 	     blk++)
f731ee
 	  {
f731ee
-	    /* The header is skipped, the first direntry is stored
f731ee
-	       from byte 16.  */
f731ee
-	    int pos = 16;
f731ee
+	    struct grub_xfs_dir2_entry *direntry =
f731ee
+					grub_xfs_first_de(dir->data, dirblock);
f731ee
 	    int entries;
f731ee
-	    int tail_start = (dirblk_size
f731ee
-			      - sizeof (struct grub_xfs_dirblock_tail));
f731ee
-
f731ee
-	    struct grub_xfs_dirblock_tail *tail;
f731ee
-	    tail = (struct grub_xfs_dirblock_tail *) &dirblock[tail_start];
f731ee
+	    struct grub_xfs_dirblock_tail *tail =
f731ee
+					grub_xfs_dir_tail(dir->data, dirblock);
f731ee
 
f731ee
 	    numread = grub_xfs_read_file (dir, 0, 0,
f731ee
 					  blk << dirblk_log2,
f731ee
@@ -617,13 +791,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
f731ee
 		       - grub_be_to_cpu32 (tail->leaf_stale));
f731ee
 
f731ee
 	    /* Iterate over all entries within this block.  */
f731ee
-	    while (pos < tail_start)
f731ee
+	    while ((char *)direntry < (char *)tail)
f731ee
 	      {
f731ee
-		struct grub_xfs_dir2_entry *direntry;
f731ee
 		grub_uint8_t *freetag;
f731ee
 		char *filename;
f731ee
 
f731ee
-		direntry = (struct grub_xfs_dir2_entry *) &dirblock[pos];
f731ee
 		freetag = (grub_uint8_t *) direntry;
f731ee
 
f731ee
 		if (grub_get_unaligned16 (freetag) == 0XFFFF)
f731ee
@@ -631,14 +803,16 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
f731ee
 		    grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
f731ee
 
f731ee
 		    /* This entry is not used, go to the next one.  */
f731ee
-		    pos += grub_be_to_cpu16 (grub_get_unaligned16 (skip));
f731ee
+		    direntry = (struct grub_xfs_dir2_entry *)
f731ee
+				(((char *)direntry) +
f731ee
+				grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
f731ee
 
f731ee
 		    continue;
f731ee
 		  }
f731ee
 
f731ee
-		filename = &dirblock[pos + sizeof (*direntry)];
f731ee
-		/* The byte after the filename is for the tag, which
f731ee
-		   is not used by GRUB.  So it can be overwritten.  */
f731ee
+		filename = (char *)(direntry + 1);
f731ee
+		/* The byte after the filename is for the filetype, padding, or
f731ee
+		   tag, which is not used by GRUB.  So it can be overwritten. */
f731ee
 		filename[direntry->len] = '\0';
f731ee
 
f731ee
 		if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode), 
f731ee
@@ -655,8 +829,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
f731ee
 		  break;
f731ee
 
f731ee
 		/* Select the next directory entry.  */
f731ee
-		pos = GRUB_XFS_NEXT_DIRENT (pos, direntry->len);
f731ee
-		pos = GRUB_XFS_ROUND_TO_DIRENT (pos);
f731ee
+		direntry = grub_xfs_next_de(dir->data, direntry);
f731ee
 	      }
f731ee
 	  }
f731ee
 	grub_free (dirblock);
f731ee
@@ -681,19 +854,14 @@ grub_xfs_mount (grub_disk_t disk)
f731ee
   if (!data)
f731ee
     return 0;
f731ee
 
f731ee
+  grub_dprintf("xfs", "Reading sb\n");
f731ee
   /* Read the superblock.  */
f731ee
   if (grub_disk_read (disk, 0, 0,
f731ee
 		      sizeof (struct grub_xfs_sblock), &data->sblock))
f731ee
     goto fail;
f731ee
 
f731ee
-  if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)
f731ee
-      || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS
f731ee
-      || ((int) data->sblock.log2_bsize
f731ee
-	  + (int) data->sblock.log2_dirblk) >= 27)
f731ee
-    {
f731ee
-      grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
f731ee
-      goto fail;
f731ee
-    }
f731ee
+  if (!grub_xfs_sb_valid(data))
f731ee
+    goto fail;
f731ee
 
f731ee
   data = grub_realloc (data,
f731ee
 		       sizeof (struct grub_xfs_data)
f731ee
@@ -708,9 +876,13 @@ grub_xfs_mount (grub_disk_t disk)
f731ee
   data->diropen.inode_read = 1;
f731ee
   data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
f731ee
   data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
f731ee
+  data->hasftype = grub_xfs_sb_hasftype(data);
f731ee
+  data->hascrc = grub_xfs_sb_hascrc(data);
f731ee
 
f731ee
   data->disk = disk;
f731ee
   data->pos = 0;
f731ee
+  grub_dprintf("xfs", "Reading root ino %"PRIuGRUB_UINT64_T"\n",
f731ee
+	       grub_cpu_to_be64(data->sblock.rootino));
f731ee
 
f731ee
   grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
f731ee