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