Description: Rewrite XFS btree parsing; fixes invalid BMAP
Author: Vladimir Serbinenko <phcoder@gmail.com>
Bug: http://savannah.gnu.org/bugs/?34213
Bug-Debian: http://bugs.debian.org/657776
Applied-Upstream: http://bazaar.launchpad.net/~vcs-imports/grub/grub2-bzr/revision/3443
Last-Update: 2012-01-29

Index: b/grub-core/fs/xfs.c
===================================================================
--- a/grub-core/fs/xfs.c
+++ b/grub-core/fs/xfs.c
@@ -111,7 +111,9 @@
   grub_uint64_t nblocks;
   grub_uint32_t extsize;
   grub_uint32_t nextents;
-  grub_uint8_t unused3[20];
+  grub_uint16_t unused3;
+  grub_uint8_t fork_offset;
+  grub_uint8_t unused4[17];
   union
   {
     char raw[156];
@@ -145,7 +147,7 @@
   grub_disk_t disk;
   int pos;
   int bsize;
-  int agsize;
+  grub_uint32_t agsize;
   struct grub_fshelp_node diropen;
 };
 
@@ -159,33 +161,67 @@
 #define FILETYPE_INO_DIRECTORY	0040000
 #define FILETYPE_INO_SYMLINK	0120000
 
-#define GRUB_XFS_INO_AGBITS(data)		\
-  ((data)->sblock.log2_agblk + (data)->sblock.log2_inop)
-#define GRUB_XFS_INO_INOINAG(data, ino)		\
-  (grub_be_to_cpu64 (ino) & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1))
-#define GRUB_XFS_INO_AG(data,ino)		\
-  (grub_be_to_cpu64 (ino) >> GRUB_XFS_INO_AGBITS (data))
-
-#define GRUB_XFS_FSB_TO_BLOCK(data, fsb) \
-  (((fsb) >> (data)->sblock.log2_agblk) * (data)->agsize \
- + ((fsb) & ((1LL << (data)->sblock.log2_agblk) - 1)))
-
-#define GRUB_XFS_EXTENT_OFFSET(exts,ex) \
-	((grub_be_to_cpu32 (exts[ex][0]) & ~(1 << 31)) << 23 \
-	| grub_be_to_cpu32 (exts[ex][1]) >> 9)
-
-#define GRUB_XFS_EXTENT_BLOCK(exts,ex)		\
-  ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex][1]) \
-		  & (0x1ff)) << 43 \
-   | (grub_uint64_t) grub_be_to_cpu32 (exts[ex][2]) << 11 \
-   | grub_be_to_cpu32 (exts[ex][3]) >> 21)
-
-#define GRUB_XFS_EXTENT_SIZE(exts,ex)		\
-  (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 20) - 1))
-
-#define GRUB_XFS_ROUND_TO_DIRENT(pos)	((((pos) + 8 - 1) / 8) * 8)
-#define GRUB_XFS_NEXT_DIRENT(pos,len)		\
-  (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2)
+static inline int
+GRUB_XFS_INO_AGBITS(struct grub_xfs_data *data)
+{
+  return ((data)->sblock.log2_agblk + (data)->sblock.log2_inop);
+}
+
+static inline grub_uint64_t
+GRUB_XFS_INO_INOINAG (struct grub_xfs_data *data,
+		      grub_uint64_t ino)
+{
+  return (grub_be_to_cpu64 (ino) & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1));
+}
+
+static inline grub_uint64_t
+GRUB_XFS_INO_AG (struct grub_xfs_data *data,
+		 grub_uint64_t ino)
+{
+  return (grub_be_to_cpu64 (ino) >> GRUB_XFS_INO_AGBITS (data));
+}
+
+static inline grub_disk_addr_t
+GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data *data, grub_disk_addr_t fsb)
+{
+  return ((fsb >> data->sblock.log2_agblk) * data->agsize
+	  + (fsb & ((1LL << data->sblock.log2_agblk) - 1)));
+}
+
+static inline grub_uint64_t
+GRUB_XFS_EXTENT_OFFSET (grub_xfs_extent *exts, int ex)
+{
+  return ((grub_be_to_cpu32 (exts[ex][0]) & ~(1 << 31)) << 23
+	  | grub_be_to_cpu32 (exts[ex][1]) >> 9);
+}
+
+static inline grub_uint64_t
+GRUB_XFS_EXTENT_BLOCK (grub_xfs_extent *exts, int ex)
+{
+  return ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex][1])
+			   & (0x1ff)) << 43
+	  | (grub_uint64_t) grub_be_to_cpu32 (exts[ex][2]) << 11
+	  | grub_be_to_cpu32 (exts[ex][3]) >> 21);
+}
+
+static inline grub_uint64_t
+GRUB_XFS_EXTENT_SIZE (grub_xfs_extent *exts, int ex)
+{
+  return (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 20) - 1));
+}
+
+static inline int
+GRUB_XFS_ROUND_TO_DIRENT (int pos)
+{
+  return ((((pos) + 8 - 1) / 8) * 8);
+}
+
+static inline int
+GRUB_XFS_NEXT_DIRENT (int pos, int len)
+{
+  return (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2);
+}
+
 
 static inline grub_uint64_t
 grub_xfs_inode_block (struct grub_xfs_data *data,
@@ -241,13 +277,23 @@
   if (node->inode.format == XFS_INODE_FORMAT_BTREE)
     {
       grub_uint64_t *keys;
+      int recoffset;
 
-      leaf = grub_malloc (node->data->sblock.bsize);
+      leaf = grub_malloc (node->data->bsize);
       if (leaf == 0)
         return 0;
 
       nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs);
       keys = &node->inode.data.btree.keys[0];
+      if (node->inode.fork_offset)
+	recoffset = (node->inode.fork_offset
+		     - ((char *) &node->inode.data.btree.keys - (char *) &node->inode))
+	  / (2 * sizeof (grub_uint64_t));
+      else
+	recoffset = ((1 << node->data->sblock.log2_inode)
+		     - ((char *) &node->inode.data.btree.keys
+			- (char *) &node->inode))
+	  / (2 * sizeof (grub_uint64_t));
       do
         {
           int i;
@@ -264,12 +310,9 @@
               grub_free (leaf);
               return 0;
             }
-
           if (grub_disk_read (node->data->disk,
-                              grub_be_to_cpu64 (keys[i - 1 + nrec])
-                              << (node->data->sblock.log2_bsize
-                                  - GRUB_DISK_SECTOR_BITS),
-                              0, node->data->sblock.bsize, leaf))
+                              GRUB_XFS_FSB_TO_BLOCK (node->data, grub_be_to_cpu64 (keys[i - 1 + recoffset])) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS),
+                              0, node->data->bsize, leaf))
             return 0;
 
           if (grub_strncmp ((char *) leaf->magic, "BMAP", 4))
@@ -281,7 +324,11 @@
 
           nrec = grub_be_to_cpu16 (leaf->numrecs);
           keys = &leaf->keys[0];
-        } while (leaf->level);
+	  recoffset = ((node->data->bsize - ((char *) &leaf->keys
+					     - (char *) leaf))
+		       / (2 * sizeof (grub_uint64_t)));
+	}
+      while (leaf->level);
       exts = (grub_xfs_extent *) keys;
     }
   else if (node->inode.format == XFS_INODE_FORMAT_EXT)
@@ -328,7 +375,7 @@
 grub_xfs_read_file (grub_fshelp_node_t node,
 		     void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
 					unsigned offset, unsigned length),
-		     int pos, grub_size_t len, char *buf)
+		     grub_off_t pos, grub_size_t len, char *buf)
 {
   return grub_fshelp_read_file (node->data->disk, node, read_hook,
 				pos, len, buf, grub_xfs_read_block,