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

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