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

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