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

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