X-Git-Url: https://git.tld-linux.org/?p=packages%2Fgrub2.git;a=blobdiff_plain;f=grub2-xfs-V5-filesystem-format-support.patch;fp=grub2-xfs-V5-filesystem-format-support.patch;h=56318d400270da6ad08711317c7084dd652920ac;hp=0000000000000000000000000000000000000000;hb=9d17de0887ee48f66be4e47be43f8fc6a4ea7799;hpb=f70448640b83ae5ebec0579e4b9ecee8c405bb22 diff --git a/grub2-xfs-V5-filesystem-format-support.patch b/grub2-xfs-V5-filesystem-format-support.patch new file mode 100644 index 0000000..56318d4 --- /dev/null +++ b/grub2-xfs-V5-filesystem-format-support.patch @@ -0,0 +1,478 @@ +From 2f725e644d8ccf001a4dccddc8abb2c9479352a7 Mon Sep 17 00:00:00 2001 +From: Jan Kara +Date: Wed, 11 Jun 2014 18:36:01 +0200 +Subject: [PATCH] xfs: V5 filesystem format support + +Signed-off-by: Jan Kara +--- + 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; + + + ++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); +-} +- + + 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); +