+++ /dev/null
-From 2f725e644d8ccf001a4dccddc8abb2c9479352a7 Mon Sep 17 00:00:00 2001
-From: Jan Kara <jack@suse.cz>
-Date: Wed, 11 Jun 2014 18:36:01 +0200
-Subject: [PATCH] xfs: V5 filesystem format support
-
-Signed-off-by: Jan Kara <jack@suse.cz>
----
- grub-core/fs/xfs.c | 245 +++++++++++++++++++++++++++++++++++++----------------
- 1 file changed, 173 insertions(+), 72 deletions(-)
-
-Index: grub-2.02~beta2/grub-core/fs/xfs.c
-===================================================================
---- grub-2.02~beta2.orig/grub-core/fs/xfs.c
-+++ grub-2.02~beta2/grub-core/fs/xfs.c
-@@ -34,6 +34,15 @@ GRUB_MOD_LICENSE ("GPLv3+");
- #define XFS_INODE_FORMAT_EXT 2
- #define XFS_INODE_FORMAT_BTREE 3
-
-+/* Superblock version field flags */
-+#define XFS_SB_VERSION_NUMBITS 0x000f
-+#define XFS_SB_VERSION_MOREBITSBIT 0x8000
-+
-+/* features2 field flags */
-+#define XFS_SB_VERSION2_FTYPE 0x00000200 /* inode type in dir */
-+
-+/* incompat feature flags */
-+#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
-
- struct grub_xfs_sblock
- {
-@@ -45,7 +54,9 @@ struct grub_xfs_sblock
- grub_uint64_t rootino;
- grub_uint8_t unused3[20];
- grub_uint32_t agsize;
-- grub_uint8_t unused4[20];
-+ grub_uint8_t unused4[12];
-+ grub_uint16_t version;
-+ grub_uint8_t unused5[6];
- grub_uint8_t label[12];
- grub_uint8_t log2_bsize;
- grub_uint8_t log2_sect;
-@@ -54,12 +65,19 @@ struct grub_xfs_sblock
- grub_uint8_t log2_agblk;
- grub_uint8_t unused6[67];
- grub_uint8_t log2_dirblk;
-+ grub_uint8_t unused7[7];
-+ grub_uint32_t features2;
-+ grub_uint8_t unused8[4];
-+ grub_uint32_t sb_features_compat;
-+ grub_uint32_t sb_features_ro_compat;
-+ grub_uint32_t sb_features_incompat;
-+ grub_uint32_t sb_features_log_incompat;
- } GRUB_PACKED;
-
- struct grub_xfs_dir_header
- {
- grub_uint8_t count;
-- grub_uint8_t smallino;
-+ grub_uint8_t largeino;
- union
- {
- grub_uint32_t i4;
-@@ -67,14 +85,16 @@ struct grub_xfs_dir_header
- } GRUB_PACKED parent;
- } GRUB_PACKED;
-
-+/* Structure for directory entry inlined in the inode */
- struct grub_xfs_dir_entry
- {
- grub_uint8_t len;
- grub_uint16_t offset;
- char name[1];
-- /* Inode number follows, 32 bits. */
-+ /* Inode number follows, 32 / 64 bits. */
- } GRUB_PACKED;
-
-+/* Structure for directory entry in a block */
- struct grub_xfs_dir2_entry
- {
- grub_uint64_t inode;
-@@ -90,7 +110,8 @@ struct grub_xfs_btree_node
- grub_uint16_t numrecs;
- grub_uint64_t left;
- grub_uint64_t right;
-- grub_uint64_t keys[1];
-+ /* In V5 here follow crc, uuid, etc. */
-+ /* Then follow keys and block pointers */
- } GRUB_PACKED;
-
- struct grub_xfs_btree_root
-@@ -123,17 +144,6 @@ struct grub_xfs_inode
- grub_uint16_t unused3;
- grub_uint8_t fork_offset;
- grub_uint8_t unused4[17];
-- union
-- {
-- char raw[156];
-- struct dir
-- {
-- struct grub_xfs_dir_header dirhead;
-- struct grub_xfs_dir_entry direntry[1];
-- } dir;
-- grub_xfs_extent extents[XFS_INODE_EXTENTS];
-- struct grub_xfs_btree_root btree;
-- } GRUB_PACKED data;
- } GRUB_PACKED;
-
- struct grub_xfs_dirblock_tail
-@@ -157,6 +167,8 @@ struct grub_xfs_data
- int pos;
- int bsize;
- grub_uint32_t agsize;
-+ unsigned int hasftype:1;
-+ unsigned int hascrc:1;
- struct grub_fshelp_node diropen;
- };
-
-@@ -164,6 +176,24 @@ static grub_dl_t my_mod;
-
- \f
-
-+static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
-+{
-+ return (grub_be_to_cpu16(data->sblock.version) & XFS_SB_VERSION_NUMBITS) == 5;
-+}
-+
-+static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
-+{
-+ grub_uint32_t version = grub_be_to_cpu16(data->sblock.version);
-+
-+ if ((version & XFS_SB_VERSION_NUMBITS) == 5 &&
-+ grub_be_to_cpu32(data->sblock.sb_features_incompat) & XFS_SB_FEAT_INCOMPAT_FTYPE)
-+ return 1;
-+ if (version & XFS_SB_VERSION_MOREBITSBIT &&
-+ grub_be_to_cpu32(data->sblock.features2) & XFS_SB_VERSION2_FTYPE)
-+ return 1;
-+ return 0;
-+}
-+
- /* Filetype information as used in inodes. */
- #define FILETYPE_INO_MASK 0170000
- #define FILETYPE_INO_REG 0100000
-@@ -219,18 +249,6 @@ GRUB_XFS_EXTENT_SIZE (grub_xfs_extent *e
- return (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 21) - 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);
--}
--
- \f
- static inline grub_uint64_t
- grub_xfs_inode_block (struct grub_xfs_data *data,
-@@ -261,6 +279,92 @@ grub_xfs_inode_size(struct grub_xfs_data
- return 1 << data->sblock.log2_inode;
- }
-
-+static void *
-+grub_xfs_inode_data(struct grub_xfs_inode *inode)
-+{
-+ if (inode->version <= 2)
-+ return ((char *)inode) + 100;
-+ return ((char *)inode) + 176;
-+}
-+
-+static struct grub_xfs_dir_entry *
-+grub_xfs_inline_de(struct grub_xfs_dir_header *head)
-+{
-+ /*
-+ * With small inode numbers the header is 4 bytes smaller because of
-+ * smaller parent pointer
-+ */
-+ return (void *)(((char *)head) + sizeof(struct grub_xfs_dir_header) -
-+ (head->largeino ? 0 : sizeof(grub_uint32_t)));
-+}
-+
-+static grub_uint8_t *
-+grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
-+ struct grub_xfs_dir_entry *de)
-+{
-+ return ((grub_uint8_t *)(de + 1)) + de->len - 1 +
-+ (data->hasftype ? 1 : 0);
-+}
-+
-+static struct grub_xfs_dir_entry *
-+grub_xfs_inline_next_de(struct grub_xfs_data *data,
-+ struct grub_xfs_dir_header *head,
-+ struct grub_xfs_dir_entry *de)
-+{
-+ char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
-+
-+ p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
-+ if (data->hasftype)
-+ p++;
-+
-+ return (struct grub_xfs_dir_entry *)p;
-+}
-+
-+static struct grub_xfs_dirblock_tail *
-+grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
-+{
-+ int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
-+
-+ return (struct grub_xfs_dirblock_tail *)
-+ ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
-+}
-+
-+static struct grub_xfs_dir2_entry *
-+grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
-+{
-+ if (data->hascrc)
-+ return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
-+ return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
-+}
-+
-+static inline int
-+grub_xfs_round_dirent_size (int len)
-+{
-+ return (len + 7) & ~7;
-+}
-+
-+static struct grub_xfs_dir2_entry *
-+grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
-+{
-+ int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
-+
-+ if (data->hasftype)
-+ size++; /* File type */
-+ return (struct grub_xfs_dir2_entry *)
-+ (((char *)de) + grub_xfs_round_dirent_size (size));
-+}
-+
-+static grub_uint64_t *
-+grub_xfs_btree_keys(struct grub_xfs_data *data,
-+ struct grub_xfs_btree_node *leaf)
-+{
-+ char *p = (char *)(leaf + 1);
-+
-+ if (data->hascrc)
-+ p += 48; /* crc, uuid, ... */
-+ return (grub_uint64_t *)(void*)p;
-+}
-+
- static grub_err_t
- grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
- struct grub_xfs_inode *inode)
-@@ -268,6 +372,9 @@ grub_xfs_read_inode (struct grub_xfs_dat
- grub_uint64_t block = grub_xfs_inode_block (data, ino);
- int offset = grub_xfs_inode_offset (data, ino);
-
-+ grub_dprintf("xfs", "Reading inode (%llu) - %llu, %d\n",
-+ (unsigned long long) ino,
-+ (unsigned long long) block, offset);
- /* Read the inode. */
- if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
- inode))
-@@ -290,6 +397,7 @@ grub_xfs_read_block (grub_fshelp_node_t
-
- if (node->inode.format == XFS_INODE_FORMAT_BTREE)
- {
-+ struct grub_xfs_btree_root *root;
- const grub_uint64_t *keys;
- int recoffset;
-
-@@ -297,15 +405,15 @@ grub_xfs_read_block (grub_fshelp_node_t
- if (leaf == 0)
- return 0;
-
-- nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs);
-- keys = &node->inode.data.btree.keys[0];
-+ root = grub_xfs_inode_data(&node->inode);
-+ nrec = grub_be_to_cpu16 (root->numrecs);
-+ keys = &root->keys[0];
- if (node->inode.fork_offset)
- recoffset = (node->inode.fork_offset - 1) / 2;
- else
- recoffset = (grub_xfs_inode_size(node->data)
-- - ((char *) &node->inode.data.btree.keys
-- - (char *) &node->inode))
-- / (2 * sizeof (grub_uint64_t));
-+ - ((char *) keys - (char *) &node->inode))
-+ / (2 * sizeof (grub_uint64_t));
- do
- {
- int i;
-@@ -327,7 +435,10 @@ grub_xfs_read_block (grub_fshelp_node_t
- 0, node->data->bsize, leaf))
- return 0;
-
-- if (grub_strncmp ((char *) leaf->magic, "BMAP", 4))
-+ if ((!node->data->hascrc &&
-+ grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
-+ (node->data->hascrc &&
-+ grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
- {
- grub_free (leaf);
- grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
-@@ -335,8 +446,8 @@ grub_xfs_read_block (grub_fshelp_node_t
- }
-
- nrec = grub_be_to_cpu16 (leaf->numrecs);
-- keys = &leaf->keys[0];
-- recoffset = ((node->data->bsize - ((char *) &leaf->keys
-+ keys = grub_xfs_btree_keys(node->data, leaf);
-+ recoffset = ((node->data->bsize - ((char *) keys
- - (char *) leaf))
- / (2 * sizeof (grub_uint64_t)));
- }
-@@ -346,7 +457,7 @@ grub_xfs_read_block (grub_fshelp_node_t
- else if (node->inode.format == XFS_INODE_FORMAT_EXT)
- {
- nrec = grub_be_to_cpu32 (node->inode.nextents);
-- exts = &node->inode.data.extents[0];
-+ exts = grub_xfs_inode_data(&node->inode);
- }
- else
- {
-@@ -404,7 +515,7 @@ grub_xfs_read_symlink (grub_fshelp_node_
- switch (node->inode.format)
- {
- case XFS_INODE_FORMAT_INO:
-- return grub_strndup (node->inode.data.raw, size);
-+ return grub_strndup (grub_xfs_inode_data(&node->inode), size);
-
- case XFS_INODE_FORMAT_EXT:
- {
-@@ -501,23 +612,18 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
- {
- case XFS_INODE_FORMAT_INO:
- {
-- struct grub_xfs_dir_entry *de = &diro->inode.data.dir.direntry[0];
-- int smallino = !diro->inode.data.dir.dirhead.smallino;
-+ struct grub_xfs_dir_header *head = grub_xfs_inode_data(&diro->inode);
-+ struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
-+ int smallino = !head->largeino;
- int i;
- grub_uint64_t parent;
-
- /* If small inode numbers are used to pack the direntry, the
- parent inode number is small too. */
- if (smallino)
-- {
-- parent = grub_be_to_cpu32 (diro->inode.data.dir.dirhead.parent.i4);
-- /* The header is a bit smaller than usual. */
-- de = (struct grub_xfs_dir_entry *) ((char *) de - 4);
-- }
-+ parent = grub_be_to_cpu32 (head->parent.i4);
- else
-- {
-- parent = grub_be_to_cpu64(diro->inode.data.dir.dirhead.parent.i8);
-- }
-+ parent = grub_be_to_cpu64 (head->parent.i8);
-
- /* Synthesize the direntries for `.' and `..'. */
- if (iterate_dir_call_hook (diro->ino, ".", &ctx))
-@@ -526,12 +632,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
- if (iterate_dir_call_hook (parent, "..", &ctx))
- return 1;
-
-- for (i = 0; i < diro->inode.data.dir.dirhead.count; i++)
-+ for (i = 0; i < head->count; i++)
- {
- grub_uint64_t ino;
-- grub_uint8_t *inopos = (((grub_uint8_t *) de)
-- + sizeof (struct grub_xfs_dir_entry)
-- + de->len - 1);
-+ grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
- grub_uint8_t c;
-
- /* inopos might be unaligned. */
-@@ -556,10 +660,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
- return 1;
- de->name[de->len] = c;
-
-- de = ((struct grub_xfs_dir_entry *)
-- (((char *) de)+ sizeof (struct grub_xfs_dir_entry) + de->len
-- + ((smallino ? sizeof (grub_uint32_t)
-- : sizeof (grub_uint64_t))) - 1));
-+ de = grub_xfs_inline_next_de(dir->data, head, de);
- }
- break;
- }
-@@ -586,15 +687,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
- >> dirblk_log2);
- blk++)
- {
-- /* The header is skipped, the first direntry is stored
-- from byte 16. */
-- int pos = 16;
-+ struct grub_xfs_dir2_entry *direntry =
-+ grub_xfs_first_de(dir->data, dirblock);
- int entries;
-- int tail_start = (dirblk_size
-- - sizeof (struct grub_xfs_dirblock_tail));
--
-- struct grub_xfs_dirblock_tail *tail;
-- tail = (struct grub_xfs_dirblock_tail *) &dirblock[tail_start];
-+ struct grub_xfs_dirblock_tail *tail =
-+ grub_xfs_dir_tail(dir->data, dirblock);
-
- numread = grub_xfs_read_file (dir, 0, 0,
- blk << dirblk_log2,
-@@ -606,13 +703,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
- - grub_be_to_cpu32 (tail->leaf_stale));
-
- /* Iterate over all entries within this block. */
-- while (pos < tail_start)
-+ while ((char *)direntry < (char *)tail)
- {
-- struct grub_xfs_dir2_entry *direntry;
- grub_uint8_t *freetag;
- char *filename;
-
-- direntry = (struct grub_xfs_dir2_entry *) &dirblock[pos];
- freetag = (grub_uint8_t *) direntry;
-
- if (grub_get_unaligned16 (freetag) == 0XFFFF)
-@@ -620,14 +715,16 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
- grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
-
- /* This entry is not used, go to the next one. */
-- pos += grub_be_to_cpu16 (grub_get_unaligned16 (skip));
-+ direntry = (struct grub_xfs_dir2_entry *)
-+ (((char *)direntry) +
-+ grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
-
- continue;
- }
-
-- filename = &dirblock[pos + sizeof (*direntry)];
-- /* The byte after the filename is for the tag, which
-- is not used by GRUB. So it can be overwritten. */
-+ filename = (char *)(direntry + 1);
-+ /* The byte after the filename is for the filetype, padding, or
-+ tag, which is not used by GRUB. So it can be overwritten. */
- filename[direntry->len] = '\0';
-
- if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode),
-@@ -644,8 +741,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
- break;
-
- /* Select the next directory entry. */
-- pos = GRUB_XFS_NEXT_DIRENT (pos, direntry->len);
-- pos = GRUB_XFS_ROUND_TO_DIRENT (pos);
-+ direntry = grub_xfs_next_de(dir->data, direntry);
- }
- }
- grub_free (dirblock);
-@@ -670,6 +766,7 @@ grub_xfs_mount (grub_disk_t disk)
- if (!data)
- return 0;
-
-+ grub_dprintf("xfs", "Reading sb\n");
- /* Read the superblock. */
- if (grub_disk_read (disk, 0, 0,
- sizeof (struct grub_xfs_sblock), &data->sblock))
-@@ -697,9 +794,13 @@ grub_xfs_mount (grub_disk_t disk)
- data->diropen.inode_read = 1;
- data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
- data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
-+ data->hasftype = grub_xfs_sb_hasftype(data);
-+ data->hascrc = grub_xfs_sb_hascrc(data);
-
- data->disk = disk;
- data->pos = 0;
-+ grub_dprintf("xfs", "Reading root ino %llu\n",
-+ (unsigned long long) grub_cpu_to_be64(data->sblock.rootino));
-
- grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
-