1 From 2f725e644d8ccf001a4dccddc8abb2c9479352a7 Mon Sep 17 00:00:00 2001
2 From: Jan Kara <jack@suse.cz>
3 Date: Wed, 11 Jun 2014 18:36:01 +0200
4 Subject: [PATCH] xfs: V5 filesystem format support
6 Signed-off-by: Jan Kara <jack@suse.cz>
8 grub-core/fs/xfs.c | 245 +++++++++++++++++++++++++++++++++++++----------------
9 1 file changed, 173 insertions(+), 72 deletions(-)
11 Index: grub-2.02~beta2/grub-core/fs/xfs.c
12 ===================================================================
13 --- grub-2.02~beta2.orig/grub-core/fs/xfs.c
14 +++ grub-2.02~beta2/grub-core/fs/xfs.c
15 @@ -34,6 +34,15 @@ GRUB_MOD_LICENSE ("GPLv3+");
16 #define XFS_INODE_FORMAT_EXT 2
17 #define XFS_INODE_FORMAT_BTREE 3
19 +/* Superblock version field flags */
20 +#define XFS_SB_VERSION_NUMBITS 0x000f
21 +#define XFS_SB_VERSION_MOREBITSBIT 0x8000
23 +/* features2 field flags */
24 +#define XFS_SB_VERSION2_FTYPE 0x00000200 /* inode type in dir */
26 +/* incompat feature flags */
27 +#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
29 struct grub_xfs_sblock
31 @@ -45,7 +54,9 @@ struct grub_xfs_sblock
32 grub_uint64_t rootino;
33 grub_uint8_t unused3[20];
35 - grub_uint8_t unused4[20];
36 + grub_uint8_t unused4[12];
37 + grub_uint16_t version;
38 + grub_uint8_t unused5[6];
39 grub_uint8_t label[12];
40 grub_uint8_t log2_bsize;
41 grub_uint8_t log2_sect;
42 @@ -54,12 +65,19 @@ struct grub_xfs_sblock
43 grub_uint8_t log2_agblk;
44 grub_uint8_t unused6[67];
45 grub_uint8_t log2_dirblk;
46 + grub_uint8_t unused7[7];
47 + grub_uint32_t features2;
48 + grub_uint8_t unused8[4];
49 + grub_uint32_t sb_features_compat;
50 + grub_uint32_t sb_features_ro_compat;
51 + grub_uint32_t sb_features_incompat;
52 + grub_uint32_t sb_features_log_incompat;
55 struct grub_xfs_dir_header
58 - grub_uint8_t smallino;
59 + grub_uint8_t largeino;
63 @@ -67,14 +85,16 @@ struct grub_xfs_dir_header
67 +/* Structure for directory entry inlined in the inode */
68 struct grub_xfs_dir_entry
73 - /* Inode number follows, 32 bits. */
74 + /* Inode number follows, 32 / 64 bits. */
77 +/* Structure for directory entry in a block */
78 struct grub_xfs_dir2_entry
81 @@ -90,7 +110,8 @@ struct grub_xfs_btree_node
82 grub_uint16_t numrecs;
85 - grub_uint64_t keys[1];
86 + /* In V5 here follow crc, uuid, etc. */
87 + /* Then follow keys and block pointers */
90 struct grub_xfs_btree_root
91 @@ -123,17 +144,6 @@ struct grub_xfs_inode
92 grub_uint16_t unused3;
93 grub_uint8_t fork_offset;
94 grub_uint8_t unused4[17];
100 - struct grub_xfs_dir_header dirhead;
101 - struct grub_xfs_dir_entry direntry[1];
103 - grub_xfs_extent extents[XFS_INODE_EXTENTS];
104 - struct grub_xfs_btree_root btree;
105 - } GRUB_PACKED data;
108 struct grub_xfs_dirblock_tail
109 @@ -157,6 +167,8 @@ struct grub_xfs_data
112 grub_uint32_t agsize;
113 + unsigned int hasftype:1;
114 + unsigned int hascrc:1;
115 struct grub_fshelp_node diropen;
118 @@ -164,6 +176,24 @@ static grub_dl_t my_mod;
122 +static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
124 + return (grub_be_to_cpu16(data->sblock.version) & XFS_SB_VERSION_NUMBITS) == 5;
127 +static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
129 + grub_uint32_t version = grub_be_to_cpu16(data->sblock.version);
131 + if ((version & XFS_SB_VERSION_NUMBITS) == 5 &&
132 + grub_be_to_cpu32(data->sblock.sb_features_incompat) & XFS_SB_FEAT_INCOMPAT_FTYPE)
134 + if (version & XFS_SB_VERSION_MOREBITSBIT &&
135 + grub_be_to_cpu32(data->sblock.features2) & XFS_SB_VERSION2_FTYPE)
140 /* Filetype information as used in inodes. */
141 #define FILETYPE_INO_MASK 0170000
142 #define FILETYPE_INO_REG 0100000
143 @@ -219,18 +249,6 @@ GRUB_XFS_EXTENT_SIZE (grub_xfs_extent *e
144 return (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 21) - 1));
148 -GRUB_XFS_ROUND_TO_DIRENT (int pos)
150 - return ((((pos) + 8 - 1) / 8) * 8);
154 -GRUB_XFS_NEXT_DIRENT (int pos, int len)
156 - return (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2);
160 static inline grub_uint64_t
161 grub_xfs_inode_block (struct grub_xfs_data *data,
162 @@ -261,6 +279,92 @@ grub_xfs_inode_size(struct grub_xfs_data
163 return 1 << data->sblock.log2_inode;
167 +grub_xfs_inode_data(struct grub_xfs_inode *inode)
169 + if (inode->version <= 2)
170 + return ((char *)inode) + 100;
171 + return ((char *)inode) + 176;
174 +static struct grub_xfs_dir_entry *
175 +grub_xfs_inline_de(struct grub_xfs_dir_header *head)
178 + * With small inode numbers the header is 4 bytes smaller because of
179 + * smaller parent pointer
181 + return (void *)(((char *)head) + sizeof(struct grub_xfs_dir_header) -
182 + (head->largeino ? 0 : sizeof(grub_uint32_t)));
185 +static grub_uint8_t *
186 +grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
187 + struct grub_xfs_dir_entry *de)
189 + return ((grub_uint8_t *)(de + 1)) + de->len - 1 +
190 + (data->hasftype ? 1 : 0);
193 +static struct grub_xfs_dir_entry *
194 +grub_xfs_inline_next_de(struct grub_xfs_data *data,
195 + struct grub_xfs_dir_header *head,
196 + struct grub_xfs_dir_entry *de)
198 + char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
200 + p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
201 + if (data->hasftype)
204 + return (struct grub_xfs_dir_entry *)p;
207 +static struct grub_xfs_dirblock_tail *
208 +grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
210 + int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
212 + return (struct grub_xfs_dirblock_tail *)
213 + ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
216 +static struct grub_xfs_dir2_entry *
217 +grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
220 + return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
221 + return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
225 +grub_xfs_round_dirent_size (int len)
227 + return (len + 7) & ~7;
230 +static struct grub_xfs_dir2_entry *
231 +grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
233 + int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
235 + if (data->hasftype)
236 + size++; /* File type */
237 + return (struct grub_xfs_dir2_entry *)
238 + (((char *)de) + grub_xfs_round_dirent_size (size));
241 +static grub_uint64_t *
242 +grub_xfs_btree_keys(struct grub_xfs_data *data,
243 + struct grub_xfs_btree_node *leaf)
245 + char *p = (char *)(leaf + 1);
248 + p += 48; /* crc, uuid, ... */
249 + return (grub_uint64_t *)(void*)p;
253 grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
254 struct grub_xfs_inode *inode)
255 @@ -268,6 +372,9 @@ grub_xfs_read_inode (struct grub_xfs_dat
256 grub_uint64_t block = grub_xfs_inode_block (data, ino);
257 int offset = grub_xfs_inode_offset (data, ino);
259 + grub_dprintf("xfs", "Reading inode (%llu) - %llu, %d\n",
260 + (unsigned long long) ino,
261 + (unsigned long long) block, offset);
262 /* Read the inode. */
263 if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
265 @@ -290,6 +397,7 @@ grub_xfs_read_block (grub_fshelp_node_t
267 if (node->inode.format == XFS_INODE_FORMAT_BTREE)
269 + struct grub_xfs_btree_root *root;
270 const grub_uint64_t *keys;
273 @@ -297,15 +405,15 @@ grub_xfs_read_block (grub_fshelp_node_t
277 - nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs);
278 - keys = &node->inode.data.btree.keys[0];
279 + root = grub_xfs_inode_data(&node->inode);
280 + nrec = grub_be_to_cpu16 (root->numrecs);
281 + keys = &root->keys[0];
282 if (node->inode.fork_offset)
283 recoffset = (node->inode.fork_offset - 1) / 2;
285 recoffset = (grub_xfs_inode_size(node->data)
286 - - ((char *) &node->inode.data.btree.keys
287 - - (char *) &node->inode))
288 - / (2 * sizeof (grub_uint64_t));
289 + - ((char *) keys - (char *) &node->inode))
290 + / (2 * sizeof (grub_uint64_t));
294 @@ -327,7 +435,10 @@ grub_xfs_read_block (grub_fshelp_node_t
295 0, node->data->bsize, leaf))
298 - if (grub_strncmp ((char *) leaf->magic, "BMAP", 4))
299 + if ((!node->data->hascrc &&
300 + grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
301 + (node->data->hascrc &&
302 + grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
305 grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
306 @@ -335,8 +446,8 @@ grub_xfs_read_block (grub_fshelp_node_t
309 nrec = grub_be_to_cpu16 (leaf->numrecs);
310 - keys = &leaf->keys[0];
311 - recoffset = ((node->data->bsize - ((char *) &leaf->keys
312 + keys = grub_xfs_btree_keys(node->data, leaf);
313 + recoffset = ((node->data->bsize - ((char *) keys
315 / (2 * sizeof (grub_uint64_t)));
317 @@ -346,7 +457,7 @@ grub_xfs_read_block (grub_fshelp_node_t
318 else if (node->inode.format == XFS_INODE_FORMAT_EXT)
320 nrec = grub_be_to_cpu32 (node->inode.nextents);
321 - exts = &node->inode.data.extents[0];
322 + exts = grub_xfs_inode_data(&node->inode);
326 @@ -404,7 +515,7 @@ grub_xfs_read_symlink (grub_fshelp_node_
327 switch (node->inode.format)
329 case XFS_INODE_FORMAT_INO:
330 - return grub_strndup (node->inode.data.raw, size);
331 + return grub_strndup (grub_xfs_inode_data(&node->inode), size);
333 case XFS_INODE_FORMAT_EXT:
335 @@ -501,23 +612,18 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
337 case XFS_INODE_FORMAT_INO:
339 - struct grub_xfs_dir_entry *de = &diro->inode.data.dir.direntry[0];
340 - int smallino = !diro->inode.data.dir.dirhead.smallino;
341 + struct grub_xfs_dir_header *head = grub_xfs_inode_data(&diro->inode);
342 + struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
343 + int smallino = !head->largeino;
345 grub_uint64_t parent;
347 /* If small inode numbers are used to pack the direntry, the
348 parent inode number is small too. */
351 - parent = grub_be_to_cpu32 (diro->inode.data.dir.dirhead.parent.i4);
352 - /* The header is a bit smaller than usual. */
353 - de = (struct grub_xfs_dir_entry *) ((char *) de - 4);
355 + parent = grub_be_to_cpu32 (head->parent.i4);
358 - parent = grub_be_to_cpu64(diro->inode.data.dir.dirhead.parent.i8);
360 + parent = grub_be_to_cpu64 (head->parent.i8);
362 /* Synthesize the direntries for `.' and `..'. */
363 if (iterate_dir_call_hook (diro->ino, ".", &ctx))
364 @@ -526,12 +632,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
365 if (iterate_dir_call_hook (parent, "..", &ctx))
368 - for (i = 0; i < diro->inode.data.dir.dirhead.count; i++)
369 + for (i = 0; i < head->count; i++)
372 - grub_uint8_t *inopos = (((grub_uint8_t *) de)
373 - + sizeof (struct grub_xfs_dir_entry)
375 + grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
378 /* inopos might be unaligned. */
379 @@ -556,10 +660,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
381 de->name[de->len] = c;
383 - de = ((struct grub_xfs_dir_entry *)
384 - (((char *) de)+ sizeof (struct grub_xfs_dir_entry) + de->len
385 - + ((smallino ? sizeof (grub_uint32_t)
386 - : sizeof (grub_uint64_t))) - 1));
387 + de = grub_xfs_inline_next_de(dir->data, head, de);
391 @@ -586,15 +687,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
395 - /* The header is skipped, the first direntry is stored
398 + struct grub_xfs_dir2_entry *direntry =
399 + grub_xfs_first_de(dir->data, dirblock);
401 - int tail_start = (dirblk_size
402 - - sizeof (struct grub_xfs_dirblock_tail));
404 - struct grub_xfs_dirblock_tail *tail;
405 - tail = (struct grub_xfs_dirblock_tail *) &dirblock[tail_start];
406 + struct grub_xfs_dirblock_tail *tail =
407 + grub_xfs_dir_tail(dir->data, dirblock);
409 numread = grub_xfs_read_file (dir, 0, 0,
411 @@ -606,13 +703,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
412 - grub_be_to_cpu32 (tail->leaf_stale));
414 /* Iterate over all entries within this block. */
415 - while (pos < tail_start)
416 + while ((char *)direntry < (char *)tail)
418 - struct grub_xfs_dir2_entry *direntry;
419 grub_uint8_t *freetag;
422 - direntry = (struct grub_xfs_dir2_entry *) &dirblock[pos];
423 freetag = (grub_uint8_t *) direntry;
425 if (grub_get_unaligned16 (freetag) == 0XFFFF)
426 @@ -620,14 +715,16 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
427 grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
429 /* This entry is not used, go to the next one. */
430 - pos += grub_be_to_cpu16 (grub_get_unaligned16 (skip));
431 + direntry = (struct grub_xfs_dir2_entry *)
432 + (((char *)direntry) +
433 + grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
438 - filename = &dirblock[pos + sizeof (*direntry)];
439 - /* The byte after the filename is for the tag, which
440 - is not used by GRUB. So it can be overwritten. */
441 + filename = (char *)(direntry + 1);
442 + /* The byte after the filename is for the filetype, padding, or
443 + tag, which is not used by GRUB. So it can be overwritten. */
444 filename[direntry->len] = '\0';
446 if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode),
447 @@ -644,8 +741,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t
450 /* Select the next directory entry. */
451 - pos = GRUB_XFS_NEXT_DIRENT (pos, direntry->len);
452 - pos = GRUB_XFS_ROUND_TO_DIRENT (pos);
453 + direntry = grub_xfs_next_de(dir->data, direntry);
456 grub_free (dirblock);
457 @@ -670,6 +766,7 @@ grub_xfs_mount (grub_disk_t disk)
461 + grub_dprintf("xfs", "Reading sb\n");
462 /* Read the superblock. */
463 if (grub_disk_read (disk, 0, 0,
464 sizeof (struct grub_xfs_sblock), &data->sblock))
465 @@ -697,9 +794,13 @@ grub_xfs_mount (grub_disk_t disk)
466 data->diropen.inode_read = 1;
467 data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
468 data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
469 + data->hasftype = grub_xfs_sb_hasftype(data);
470 + data->hascrc = grub_xfs_sb_hascrc(data);
474 + grub_dprintf("xfs", "Reading root ino %llu\n",
475 + (unsigned long long) grub_cpu_to_be64(data->sblock.rootino));
477 grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);