]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/fs/xfs.c
1. change some directory structure for the build script
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / fs / xfs.c
1 /* xfs.c - XFS. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/err.h>
21 #include <grub/file.h>
22 #include <grub/mm.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
25 #include <grub/dl.h>
26 #include <grub/types.h>
27 #include <grub/fshelp.h>
28
29 GRUB_MOD_LICENSE ("GPLv3+");
30
31 #define XFS_INODE_EXTENTS 9
32
33 #define XFS_INODE_FORMAT_INO 1
34 #define XFS_INODE_FORMAT_EXT 2
35 #define XFS_INODE_FORMAT_BTREE 3
36
37 /* Superblock version field flags */
38 #define XFS_SB_VERSION_NUMBITS 0x000f
39 #define XFS_SB_VERSION_ATTRBIT 0x0010
40 #define XFS_SB_VERSION_NLINKBIT 0x0020
41 #define XFS_SB_VERSION_QUOTABIT 0x0040
42 #define XFS_SB_VERSION_ALIGNBIT 0x0080
43 #define XFS_SB_VERSION_DALIGNBIT 0x0100
44 #define XFS_SB_VERSION_LOGV2BIT 0x0400
45 #define XFS_SB_VERSION_SECTORBIT 0x0800
46 #define XFS_SB_VERSION_EXTFLGBIT 0x1000
47 #define XFS_SB_VERSION_DIRV2BIT 0x2000
48 #define XFS_SB_VERSION_MOREBITSBIT 0x8000
49 #define XFS_SB_VERSION_BITS_SUPPORTED \
50 (XFS_SB_VERSION_NUMBITS | \
51 XFS_SB_VERSION_ATTRBIT | \
52 XFS_SB_VERSION_NLINKBIT | \
53 XFS_SB_VERSION_QUOTABIT | \
54 XFS_SB_VERSION_ALIGNBIT | \
55 XFS_SB_VERSION_DALIGNBIT | \
56 XFS_SB_VERSION_LOGV2BIT | \
57 XFS_SB_VERSION_SECTORBIT | \
58 XFS_SB_VERSION_EXTFLGBIT | \
59 XFS_SB_VERSION_DIRV2BIT | \
60 XFS_SB_VERSION_MOREBITSBIT)
61
62 /* Recognized xfs format versions */
63 #define XFS_SB_VERSION_4 4 /* Good old XFS filesystem */
64 #define XFS_SB_VERSION_5 5 /* CRC enabled filesystem */
65
66 /* features2 field flags */
67 #define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
68 #define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
69 #define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32-bit project ids */
70 #define XFS_SB_VERSION2_FTYPE 0x00000200 /* inode type in dir */
71 #define XFS_SB_VERSION2_BITS_SUPPORTED \
72 (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
73 XFS_SB_VERSION2_ATTR2BIT | \
74 XFS_SB_VERSION2_PROJID32BIT | \
75 XFS_SB_VERSION2_FTYPE)
76
77 /* incompat feature flags */
78 #define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
79 #define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */
80 #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */
81
82 /*
83 * Directory entries with ftype are explicitly handled by GRUB code.
84 *
85 * We do not currently read the inode btrees, so it is safe to read filesystems
86 * with the XFS_SB_FEAT_INCOMPAT_SPINODES feature.
87 *
88 * We do not currently verify metadata UUID, so it is safe to read filesystems
89 * with the XFS_SB_FEAT_INCOMPAT_META_UUID feature.
90 */
91 #define XFS_SB_FEAT_INCOMPAT_SUPPORTED \
92 (XFS_SB_FEAT_INCOMPAT_FTYPE | \
93 XFS_SB_FEAT_INCOMPAT_SPINODES | \
94 XFS_SB_FEAT_INCOMPAT_META_UUID)
95
96 struct grub_xfs_sblock
97 {
98 grub_uint8_t magic[4];
99 grub_uint32_t bsize;
100 grub_uint8_t unused1[24];
101 grub_uint16_t uuid[8];
102 grub_uint8_t unused2[8];
103 grub_uint64_t rootino;
104 grub_uint8_t unused3[20];
105 grub_uint32_t agsize;
106 grub_uint8_t unused4[12];
107 grub_uint16_t version;
108 grub_uint8_t unused5[6];
109 grub_uint8_t label[12];
110 grub_uint8_t log2_bsize;
111 grub_uint8_t log2_sect;
112 grub_uint8_t log2_inode;
113 grub_uint8_t log2_inop;
114 grub_uint8_t log2_agblk;
115 grub_uint8_t unused6[67];
116 grub_uint8_t log2_dirblk;
117 grub_uint8_t unused7[7];
118 grub_uint32_t features2;
119 grub_uint8_t unused8[4];
120 grub_uint32_t sb_features_compat;
121 grub_uint32_t sb_features_ro_compat;
122 grub_uint32_t sb_features_incompat;
123 grub_uint32_t sb_features_log_incompat;
124 } GRUB_PACKED;
125
126 struct grub_xfs_dir_header
127 {
128 grub_uint8_t count;
129 grub_uint8_t largeino;
130 union
131 {
132 grub_uint32_t i4;
133 grub_uint64_t i8;
134 } GRUB_PACKED parent;
135 } GRUB_PACKED;
136
137 /* Structure for directory entry inlined in the inode */
138 struct grub_xfs_dir_entry
139 {
140 grub_uint8_t len;
141 grub_uint16_t offset;
142 char name[1];
143 /* Inode number follows, 32 / 64 bits. */
144 } GRUB_PACKED;
145
146 /* Structure for directory entry in a block */
147 struct grub_xfs_dir2_entry
148 {
149 grub_uint64_t inode;
150 grub_uint8_t len;
151 } GRUB_PACKED;
152
153 struct grub_xfs_extent
154 {
155 /* This should be a bitfield but bietfields are unportable, so just have
156 a raw array and functions extracting useful info from it.
157 */
158 grub_uint32_t raw[4];
159 } GRUB_PACKED;
160
161 struct grub_xfs_btree_node
162 {
163 grub_uint8_t magic[4];
164 grub_uint16_t level;
165 grub_uint16_t numrecs;
166 grub_uint64_t left;
167 grub_uint64_t right;
168 /* In V5 here follow crc, uuid, etc. */
169 /* Then follow keys and block pointers */
170 } GRUB_PACKED;
171
172 struct grub_xfs_btree_root
173 {
174 grub_uint16_t level;
175 grub_uint16_t numrecs;
176 grub_uint64_t keys[1];
177 } GRUB_PACKED;
178
179 struct grub_xfs_time
180 {
181 grub_uint32_t sec;
182 grub_uint32_t nanosec;
183 } GRUB_PACKED;
184
185 struct grub_xfs_inode
186 {
187 grub_uint8_t magic[2];
188 grub_uint16_t mode;
189 grub_uint8_t version;
190 grub_uint8_t format;
191 grub_uint8_t unused2[26];
192 struct grub_xfs_time atime;
193 struct grub_xfs_time mtime;
194 struct grub_xfs_time ctime;
195 grub_uint64_t size;
196 grub_uint64_t nblocks;
197 grub_uint32_t extsize;
198 grub_uint32_t nextents;
199 grub_uint16_t unused3;
200 grub_uint8_t fork_offset;
201 grub_uint8_t unused4[17];
202 } GRUB_PACKED;
203
204 #define XFS_V2_INODE_SIZE sizeof(struct grub_xfs_inode)
205 #define XFS_V3_INODE_SIZE (XFS_V2_INODE_SIZE + 76)
206
207 struct grub_xfs_dirblock_tail
208 {
209 grub_uint32_t leaf_count;
210 grub_uint32_t leaf_stale;
211 } GRUB_PACKED;
212
213 struct grub_fshelp_node
214 {
215 struct grub_xfs_data *data;
216 grub_uint64_t ino;
217 int inode_read;
218 struct grub_xfs_inode inode;
219 };
220
221 struct grub_xfs_data
222 {
223 struct grub_xfs_sblock sblock;
224 grub_disk_t disk;
225 int pos;
226 int bsize;
227 grub_uint32_t agsize;
228 unsigned int hasftype:1;
229 unsigned int hascrc:1;
230 struct grub_fshelp_node diropen;
231 };
232
233 static grub_dl_t my_mod;
234
235 \f
236
237 static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
238 {
239 return (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
240 grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5);
241 }
242
243 static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
244 {
245 if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
246 grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5) &&
247 data->sblock.sb_features_incompat & grub_cpu_to_be32_compile_time(XFS_SB_FEAT_INCOMPAT_FTYPE))
248 return 1;
249 if (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
250 data->sblock.features2 & grub_cpu_to_be32_compile_time(XFS_SB_VERSION2_FTYPE))
251 return 1;
252 return 0;
253 }
254
255 static int grub_xfs_sb_valid(struct grub_xfs_data *data)
256 {
257 grub_dprintf("xfs", "Validating superblock\n");
258 if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)
259 || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS
260 || ((int) data->sblock.log2_bsize
261 + (int) data->sblock.log2_dirblk) >= 27)
262 {
263 grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
264 return 0;
265 }
266 if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
267 grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5))
268 {
269 grub_dprintf("xfs", "XFS v5 superblock detected\n");
270 if (data->sblock.sb_features_incompat &
271 grub_cpu_to_be32_compile_time(~XFS_SB_FEAT_INCOMPAT_SUPPORTED))
272 {
273 grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported "
274 "incompatible features");
275 return 0;
276 }
277 return 1;
278 }
279 else if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
280 grub_cpu_to_be16_compile_time(XFS_SB_VERSION_4))
281 {
282 grub_dprintf("xfs", "XFS v4 superblock detected\n");
283 if (!(data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT)))
284 {
285 grub_error (GRUB_ERR_BAD_FS, "XFS filesystem without V2 directories "
286 "is unsupported");
287 return 0;
288 }
289 if (data->sblock.version & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION_BITS_SUPPORTED) ||
290 (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) &&
291 data->sblock.features2 & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION2_BITS_SUPPORTED)))
292 {
293 grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported version "
294 "bits");
295 return 0;
296 }
297 return 1;
298 }
299 return 0;
300 }
301
302 /* Filetype information as used in inodes. */
303 #define FILETYPE_INO_MASK 0170000
304 #define FILETYPE_INO_REG 0100000
305 #define FILETYPE_INO_DIRECTORY 0040000
306 #define FILETYPE_INO_SYMLINK 0120000
307
308 static inline int
309 GRUB_XFS_INO_AGBITS(struct grub_xfs_data *data)
310 {
311 return ((data)->sblock.log2_agblk + (data)->sblock.log2_inop);
312 }
313
314 static inline grub_uint64_t
315 GRUB_XFS_INO_INOINAG (struct grub_xfs_data *data,
316 grub_uint64_t ino)
317 {
318 return (ino & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1));
319 }
320
321 static inline grub_uint64_t
322 GRUB_XFS_INO_AG (struct grub_xfs_data *data,
323 grub_uint64_t ino)
324 {
325 return (ino >> GRUB_XFS_INO_AGBITS (data));
326 }
327
328 static inline grub_disk_addr_t
329 GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data *data, grub_disk_addr_t fsb)
330 {
331 return ((fsb >> data->sblock.log2_agblk) * data->agsize
332 + (fsb & ((1LL << data->sblock.log2_agblk) - 1)));
333 }
334
335 static inline grub_uint64_t
336 GRUB_XFS_EXTENT_OFFSET (struct grub_xfs_extent *exts, int ex)
337 {
338 return ((grub_be_to_cpu32 (exts[ex].raw[0]) & ~(1 << 31)) << 23
339 | grub_be_to_cpu32 (exts[ex].raw[1]) >> 9);
340 }
341
342 static inline grub_uint64_t
343 GRUB_XFS_EXTENT_BLOCK (struct grub_xfs_extent *exts, int ex)
344 {
345 return ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex].raw[1])
346 & (0x1ff)) << 43
347 | (grub_uint64_t) grub_be_to_cpu32 (exts[ex].raw[2]) << 11
348 | grub_be_to_cpu32 (exts[ex].raw[3]) >> 21);
349 }
350
351 static inline grub_uint64_t
352 GRUB_XFS_EXTENT_SIZE (struct grub_xfs_extent *exts, int ex)
353 {
354 return (grub_be_to_cpu32 (exts[ex].raw[3]) & ((1 << 21) - 1));
355 }
356
357 \f
358 static inline grub_uint64_t
359 grub_xfs_inode_block (struct grub_xfs_data *data,
360 grub_uint64_t ino)
361 {
362 long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino);
363 long long ag = GRUB_XFS_INO_AG (data, ino);
364 long long block;
365
366 block = (inoinag >> data->sblock.log2_inop) + ag * data->agsize;
367 block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS);
368 return block;
369 }
370
371
372 static inline int
373 grub_xfs_inode_offset (struct grub_xfs_data *data,
374 grub_uint64_t ino)
375 {
376 int inoag = GRUB_XFS_INO_INOINAG (data, ino);
377 return ((inoag & ((1 << data->sblock.log2_inop) - 1)) <<
378 data->sblock.log2_inode);
379 }
380
381 static inline grub_size_t
382 grub_xfs_inode_size(struct grub_xfs_data *data)
383 {
384 return (grub_size_t)1 << data->sblock.log2_inode;
385 }
386
387 /*
388 * Returns size occupied by XFS inode stored in memory - we store struct
389 * grub_fshelp_node there but on disk inode size may be actually larger than
390 * struct grub_xfs_inode so we need to account for that so that we can read
391 * from disk directly into in-memory structure.
392 */
393 static inline grub_size_t
394 grub_xfs_fshelp_size(struct grub_xfs_data *data)
395 {
396 return sizeof (struct grub_fshelp_node) - sizeof (struct grub_xfs_inode)
397 + grub_xfs_inode_size(data);
398 }
399
400 /* This should return void * but XFS code is error-prone with alignment, so
401 return char to retain cast-align.
402 */
403 static char *
404 grub_xfs_inode_data(struct grub_xfs_inode *inode)
405 {
406 if (inode->version <= 2)
407 return ((char *)inode) + XFS_V2_INODE_SIZE;
408 return ((char *)inode) + XFS_V3_INODE_SIZE;
409 }
410
411 static struct grub_xfs_dir_entry *
412 grub_xfs_inline_de(struct grub_xfs_dir_header *head)
413 {
414 /*
415 With small inode numbers the header is 4 bytes smaller because of
416 smaller parent pointer
417 */
418 return (struct grub_xfs_dir_entry *)
419 (((char *) head) + sizeof(struct grub_xfs_dir_header) -
420 (head->largeino ? 0 : sizeof(grub_uint32_t)));
421 }
422
423 static grub_uint8_t *
424 grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
425 struct grub_xfs_dir_entry *de)
426 {
427 return ((grub_uint8_t *)(de + 1)) + de->len - 1 + (data->hasftype ? 1 : 0);
428 }
429
430 static struct grub_xfs_dir_entry *
431 grub_xfs_inline_next_de(struct grub_xfs_data *data,
432 struct grub_xfs_dir_header *head,
433 struct grub_xfs_dir_entry *de)
434 {
435 char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
436
437 p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
438 if (data->hasftype)
439 p++;
440
441 return (struct grub_xfs_dir_entry *)p;
442 }
443
444 static struct grub_xfs_dirblock_tail *
445 grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
446 {
447 int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
448
449 return (struct grub_xfs_dirblock_tail *)
450 ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
451 }
452
453 static struct grub_xfs_dir2_entry *
454 grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
455 {
456 if (data->hascrc)
457 return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
458 return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
459 }
460
461 static struct grub_xfs_dir2_entry *
462 grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
463 {
464 int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
465
466 if (data->hasftype)
467 size++; /* File type */
468 return (struct grub_xfs_dir2_entry *)(((char *)de) + ALIGN_UP(size, 8));
469 }
470
471 /* This should return void * but XFS code is error-prone with alignment, so
472 return char to retain cast-align.
473 */
474 static char *
475 grub_xfs_btree_keys(struct grub_xfs_data *data,
476 struct grub_xfs_btree_node *leaf)
477 {
478 char *keys = (char *)(leaf + 1);
479
480 if (data->hascrc)
481 keys += 48; /* skip crc, uuid, ... */
482 return keys;
483 }
484
485 static grub_err_t
486 grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
487 struct grub_xfs_inode *inode)
488 {
489 grub_uint64_t block = grub_xfs_inode_block (data, ino);
490 int offset = grub_xfs_inode_offset (data, ino);
491
492 grub_dprintf("xfs", "Reading inode (%"PRIuGRUB_UINT64_T") - %"PRIuGRUB_UINT64_T", %d\n",
493 ino, block, offset);
494 /* Read the inode. */
495 if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
496 inode))
497 return grub_errno;
498
499 if (grub_strncmp ((char *) inode->magic, "IN", 2))
500 return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode");
501
502 return 0;
503 }
504
505 static grub_uint64_t
506 get_fsb (const void *keys, int idx)
507 {
508 const char *p = (const char *) keys + sizeof(grub_uint64_t) * idx;
509 return grub_be_to_cpu64 (grub_get_unaligned64 (p));
510 }
511
512 static grub_disk_addr_t
513 grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
514 {
515 struct grub_xfs_btree_node *leaf = 0;
516 int ex, nrec;
517 struct grub_xfs_extent *exts;
518 grub_uint64_t ret = 0;
519
520 if (node->inode.format == XFS_INODE_FORMAT_BTREE)
521 {
522 struct grub_xfs_btree_root *root;
523 const char *keys;
524 int recoffset;
525
526 leaf = grub_malloc (node->data->bsize);
527 if (leaf == 0)
528 return 0;
529
530 root = (struct grub_xfs_btree_root *) grub_xfs_inode_data(&node->inode);
531 nrec = grub_be_to_cpu16 (root->numrecs);
532 keys = (char *) &root->keys[0];
533 if (node->inode.fork_offset)
534 recoffset = (node->inode.fork_offset - 1) / 2;
535 else
536 recoffset = (grub_xfs_inode_size(node->data)
537 - ((char *) keys - (char *) &node->inode))
538 / (2 * sizeof (grub_uint64_t));
539 do
540 {
541 int i;
542
543 for (i = 0; i < nrec; i++)
544 {
545 if (fileblock < get_fsb(keys, i))
546 break;
547 }
548
549 /* Sparse block. */
550 if (i == 0)
551 {
552 grub_free (leaf);
553 return 0;
554 }
555
556 if (grub_disk_read (node->data->disk,
557 GRUB_XFS_FSB_TO_BLOCK (node->data, get_fsb (keys, i - 1 + recoffset)) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS),
558 0, node->data->bsize, leaf))
559 return 0;
560
561 if ((!node->data->hascrc &&
562 grub_strncmp ((char *) leaf->magic, "BMAP", 4)) ||
563 (node->data->hascrc &&
564 grub_strncmp ((char *) leaf->magic, "BMA3", 4)))
565 {
566 grub_free (leaf);
567 grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
568 return 0;
569 }
570
571 nrec = grub_be_to_cpu16 (leaf->numrecs);
572 keys = grub_xfs_btree_keys(node->data, leaf);
573 recoffset = ((node->data->bsize - ((char *) keys
574 - (char *) leaf))
575 / (2 * sizeof (grub_uint64_t)));
576 }
577 while (leaf->level);
578 exts = (struct grub_xfs_extent *) keys;
579 }
580 else if (node->inode.format == XFS_INODE_FORMAT_EXT)
581 {
582 nrec = grub_be_to_cpu32 (node->inode.nextents);
583 exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode);
584 }
585 else
586 {
587 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
588 "XFS does not support inode format %d yet",
589 node->inode.format);
590 return 0;
591 }
592
593 /* Iterate over each extent to figure out which extent has
594 the block we are looking for. */
595 for (ex = 0; ex < nrec; ex++)
596 {
597 grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex);
598 grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex);
599 grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex);
600
601 /* Sparse block. */
602 if (fileblock < offset)
603 break;
604 else if (fileblock < offset + size)
605 {
606 ret = (fileblock - offset + start);
607 break;
608 }
609 }
610
611 grub_free (leaf);
612
613 return GRUB_XFS_FSB_TO_BLOCK(node->data, ret);
614 }
615
616
617 /* Read LEN bytes from the file described by DATA starting with byte
618 POS. Return the amount of read bytes in READ. */
619 static grub_ssize_t
620 grub_xfs_read_file (grub_fshelp_node_t node,
621 grub_disk_read_hook_t read_hook, void *read_hook_data,
622 grub_off_t pos, grub_size_t len, char *buf, grub_uint32_t header_size)
623 {
624 return grub_fshelp_read_file (node->data->disk, node,
625 read_hook, read_hook_data,
626 pos, len, buf, grub_xfs_read_block,
627 grub_be_to_cpu64 (node->inode.size) + header_size,
628 node->data->sblock.log2_bsize
629 - GRUB_DISK_SECTOR_BITS, 0);
630 }
631
632
633 static char *
634 grub_xfs_read_symlink (grub_fshelp_node_t node)
635 {
636 grub_ssize_t size = grub_be_to_cpu64 (node->inode.size);
637
638 if (size < 0)
639 {
640 grub_error (GRUB_ERR_BAD_FS, "invalid symlink");
641 return 0;
642 }
643
644 switch (node->inode.format)
645 {
646 case XFS_INODE_FORMAT_INO:
647 return grub_strndup (grub_xfs_inode_data(&node->inode), size);
648
649 case XFS_INODE_FORMAT_EXT:
650 {
651 char *symlink;
652 grub_ssize_t numread;
653 int off = 0;
654
655 if (node->data->hascrc)
656 off = 56;
657
658 symlink = grub_malloc (size + 1);
659 if (!symlink)
660 return 0;
661
662 node->inode.size = grub_be_to_cpu64 (size + off);
663 numread = grub_xfs_read_file (node, 0, 0, off, size, symlink, off);
664 if (numread != size)
665 {
666 grub_free (symlink);
667 return 0;
668 }
669 symlink[size] = '\0';
670 return symlink;
671 }
672 }
673
674 return 0;
675 }
676
677
678 static enum grub_fshelp_filetype
679 grub_xfs_mode_to_filetype (grub_uint16_t mode)
680 {
681 if ((grub_be_to_cpu16 (mode)
682 & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
683 return GRUB_FSHELP_DIR;
684 else if ((grub_be_to_cpu16 (mode)
685 & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
686 return GRUB_FSHELP_SYMLINK;
687 else if ((grub_be_to_cpu16 (mode)
688 & FILETYPE_INO_MASK) == FILETYPE_INO_REG)
689 return GRUB_FSHELP_REG;
690 return GRUB_FSHELP_UNKNOWN;
691 }
692
693
694 /* Context for grub_xfs_iterate_dir. */
695 struct grub_xfs_iterate_dir_ctx
696 {
697 grub_fshelp_iterate_dir_hook_t hook;
698 void *hook_data;
699 struct grub_fshelp_node *diro;
700 };
701
702 /* Helper for grub_xfs_iterate_dir. */
703 static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename,
704 struct grub_xfs_iterate_dir_ctx *ctx)
705 {
706 struct grub_fshelp_node *fdiro;
707 grub_err_t err;
708
709 fdiro = grub_malloc (grub_xfs_fshelp_size(ctx->diro->data) + 1);
710 if (!fdiro)
711 {
712 grub_print_error ();
713 return 0;
714 }
715
716 /* The inode should be read, otherwise the filetype can
717 not be determined. */
718 fdiro->ino = ino;
719 fdiro->inode_read = 1;
720 fdiro->data = ctx->diro->data;
721 err = grub_xfs_read_inode (ctx->diro->data, ino, &fdiro->inode);
722 if (err)
723 {
724 grub_print_error ();
725 return 0;
726 }
727
728 return ctx->hook (filename, grub_xfs_mode_to_filetype (fdiro->inode.mode),
729 fdiro, ctx->hook_data);
730 }
731
732 static int
733 grub_xfs_iterate_dir (grub_fshelp_node_t dir,
734 grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
735 {
736 struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
737 struct grub_xfs_iterate_dir_ctx ctx = {
738 .hook = hook,
739 .hook_data = hook_data,
740 .diro = diro
741 };
742
743 switch (diro->inode.format)
744 {
745 case XFS_INODE_FORMAT_INO:
746 {
747 struct grub_xfs_dir_header *head = (struct grub_xfs_dir_header *) grub_xfs_inode_data(&diro->inode);
748 struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head);
749 int smallino = !head->largeino;
750 int i;
751 grub_uint64_t parent;
752
753 /* If small inode numbers are used to pack the direntry, the
754 parent inode number is small too. */
755 if (smallino)
756 parent = grub_be_to_cpu32 (head->parent.i4);
757 else
758 parent = grub_be_to_cpu64 (head->parent.i8);
759
760 /* Synthesize the direntries for `.' and `..'. */
761 if (iterate_dir_call_hook (diro->ino, ".", &ctx))
762 return 1;
763
764 if (iterate_dir_call_hook (parent, "..", &ctx))
765 return 1;
766
767 for (i = 0; i < head->count; i++)
768 {
769 grub_uint64_t ino;
770 grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
771 grub_uint8_t c;
772
773 /* inopos might be unaligned. */
774 if (smallino)
775 ino = (((grub_uint32_t) inopos[0]) << 24)
776 | (((grub_uint32_t) inopos[1]) << 16)
777 | (((grub_uint32_t) inopos[2]) << 8)
778 | (((grub_uint32_t) inopos[3]) << 0);
779 else
780 ino = (((grub_uint64_t) inopos[0]) << 56)
781 | (((grub_uint64_t) inopos[1]) << 48)
782 | (((grub_uint64_t) inopos[2]) << 40)
783 | (((grub_uint64_t) inopos[3]) << 32)
784 | (((grub_uint64_t) inopos[4]) << 24)
785 | (((grub_uint64_t) inopos[5]) << 16)
786 | (((grub_uint64_t) inopos[6]) << 8)
787 | (((grub_uint64_t) inopos[7]) << 0);
788
789 c = de->name[de->len];
790 de->name[de->len] = '\0';
791 if (iterate_dir_call_hook (ino, de->name, &ctx))
792 {
793 de->name[de->len] = c;
794 return 1;
795 }
796 de->name[de->len] = c;
797
798 de = grub_xfs_inline_next_de(dir->data, head, de);
799 }
800 break;
801 }
802
803 case XFS_INODE_FORMAT_BTREE:
804 case XFS_INODE_FORMAT_EXT:
805 {
806 grub_ssize_t numread;
807 char *dirblock;
808 grub_uint64_t blk;
809 int dirblk_size, dirblk_log2;
810
811 dirblk_log2 = (dir->data->sblock.log2_bsize
812 + dir->data->sblock.log2_dirblk);
813 dirblk_size = 1 << dirblk_log2;
814
815 dirblock = grub_malloc (dirblk_size);
816 if (! dirblock)
817 return 0;
818
819 /* Iterate over every block the directory has. */
820 for (blk = 0;
821 blk < (grub_be_to_cpu64 (dir->inode.size)
822 >> dirblk_log2);
823 blk++)
824 {
825 struct grub_xfs_dir2_entry *direntry =
826 grub_xfs_first_de(dir->data, dirblock);
827 int entries;
828 struct grub_xfs_dirblock_tail *tail =
829 grub_xfs_dir_tail(dir->data, dirblock);
830
831 numread = grub_xfs_read_file (dir, 0, 0,
832 blk << dirblk_log2,
833 dirblk_size, dirblock, 0);
834 if (numread != dirblk_size)
835 return 0;
836
837 entries = (grub_be_to_cpu32 (tail->leaf_count)
838 - grub_be_to_cpu32 (tail->leaf_stale));
839
840 if (!entries)
841 continue;
842
843 /* Iterate over all entries within this block. */
844 while ((char *)direntry < (char *)tail)
845 {
846 grub_uint8_t *freetag;
847 char *filename;
848
849 freetag = (grub_uint8_t *) direntry;
850
851 if (grub_get_unaligned16 (freetag) == 0XFFFF)
852 {
853 grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
854
855 /* This entry is not used, go to the next one. */
856 direntry = (struct grub_xfs_dir2_entry *)
857 (((char *)direntry) +
858 grub_be_to_cpu16 (grub_get_unaligned16 (skip)));
859
860 continue;
861 }
862
863 filename = (char *)(direntry + 1);
864 /* The byte after the filename is for the filetype, padding, or
865 tag, which is not used by GRUB. So it can be overwritten. */
866 filename[direntry->len] = '\0';
867
868 if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode),
869 filename, &ctx))
870 {
871 grub_free (dirblock);
872 return 1;
873 }
874
875 /* Check if last direntry in this block is
876 reached. */
877 entries--;
878 if (!entries)
879 break;
880
881 /* Select the next directory entry. */
882 direntry = grub_xfs_next_de(dir->data, direntry);
883 }
884 }
885 grub_free (dirblock);
886 break;
887 }
888
889 default:
890 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
891 "XFS does not support inode format %d yet",
892 diro->inode.format);
893 }
894 return 0;
895 }
896
897
898 static struct grub_xfs_data *
899 grub_xfs_mount (grub_disk_t disk)
900 {
901 struct grub_xfs_data *data = 0;
902
903 data = grub_zalloc (sizeof (struct grub_xfs_data));
904 if (!data)
905 return 0;
906
907 grub_dprintf("xfs", "Reading sb\n");
908 /* Read the superblock. */
909 if (grub_disk_read (disk, 0, 0,
910 sizeof (struct grub_xfs_sblock), &data->sblock))
911 goto fail;
912
913 if (!grub_xfs_sb_valid(data))
914 goto fail;
915
916 data = grub_realloc (data,
917 sizeof (struct grub_xfs_data)
918 - sizeof (struct grub_xfs_inode)
919 + grub_xfs_inode_size(data) + 1);
920
921 if (! data)
922 goto fail;
923
924 data->diropen.data = data;
925 data->diropen.ino = grub_be_to_cpu64(data->sblock.rootino);
926 data->diropen.inode_read = 1;
927 data->bsize = grub_be_to_cpu32 (data->sblock.bsize);
928 data->agsize = grub_be_to_cpu32 (data->sblock.agsize);
929 data->hasftype = grub_xfs_sb_hasftype(data);
930 data->hascrc = grub_xfs_sb_hascrc(data);
931
932 data->disk = disk;
933 data->pos = 0;
934 grub_dprintf("xfs", "Reading root ino %"PRIuGRUB_UINT64_T"\n",
935 grub_cpu_to_be64(data->sblock.rootino));
936
937 grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
938
939 return data;
940 fail:
941
942 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
943 grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem");
944
945 grub_free (data);
946
947 return 0;
948 }
949
950 \f
951 /* Context for grub_xfs_dir. */
952 struct grub_xfs_dir_ctx
953 {
954 grub_fs_dir_hook_t hook;
955 void *hook_data;
956 };
957
958 /* Helper for grub_xfs_dir. */
959 static int
960 grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
961 grub_fshelp_node_t node, void *data)
962 {
963 struct grub_xfs_dir_ctx *ctx = data;
964 struct grub_dirhook_info info;
965
966 grub_memset (&info, 0, sizeof (info));
967 if (node->inode_read)
968 {
969 info.mtimeset = 1;
970 info.mtime = grub_be_to_cpu32 (node->inode.mtime.sec);
971 }
972 info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
973 if (!info.dir)
974 info.size = node->inode.size;
975 grub_free (node);
976 return ctx->hook (filename, &info, ctx->hook_data);
977 }
978
979 static grub_err_t
980 grub_xfs_dir (grub_device_t device, const char *path,
981 grub_fs_dir_hook_t hook, void *hook_data)
982 {
983 struct grub_xfs_dir_ctx ctx = { hook, hook_data };
984 struct grub_xfs_data *data = 0;
985 struct grub_fshelp_node *fdiro = 0;
986
987 grub_dl_ref (my_mod);
988
989 data = grub_xfs_mount (device->disk);
990 if (!data)
991 goto mount_fail;
992
993 grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir,
994 grub_xfs_read_symlink, GRUB_FSHELP_DIR);
995 if (grub_errno)
996 goto fail;
997
998 grub_xfs_iterate_dir (fdiro, grub_xfs_dir_iter, &ctx);
999
1000 fail:
1001 if (fdiro != &data->diropen)
1002 grub_free (fdiro);
1003 grub_free (data);
1004
1005 mount_fail:
1006
1007 grub_dl_unref (my_mod);
1008
1009 return grub_errno;
1010 }
1011
1012
1013 /* Open a file named NAME and initialize FILE. */
1014 static grub_err_t
1015 grub_xfs_open (struct grub_file *file, const char *name)
1016 {
1017 struct grub_xfs_data *data;
1018 struct grub_fshelp_node *fdiro = 0;
1019
1020 grub_dl_ref (my_mod);
1021
1022 data = grub_xfs_mount (file->device->disk);
1023 if (!data)
1024 goto mount_fail;
1025
1026 grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir,
1027 grub_xfs_read_symlink, GRUB_FSHELP_REG);
1028 if (grub_errno)
1029 goto fail;
1030
1031 if (!fdiro->inode_read)
1032 {
1033 grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode);
1034 if (grub_errno)
1035 goto fail;
1036 }
1037
1038 if (fdiro != &data->diropen)
1039 {
1040 grub_memcpy (&data->diropen, fdiro, grub_xfs_fshelp_size(data));
1041 grub_free (fdiro);
1042 }
1043
1044 file->size = grub_be_to_cpu64 (data->diropen.inode.size);
1045 file->data = data;
1046 file->offset = 0;
1047
1048 return 0;
1049
1050 fail:
1051 if (fdiro != &data->diropen)
1052 grub_free (fdiro);
1053 grub_free (data);
1054
1055 mount_fail:
1056 grub_dl_unref (my_mod);
1057
1058 return grub_errno;
1059 }
1060
1061
1062 static grub_ssize_t
1063 grub_xfs_read (grub_file_t file, char *buf, grub_size_t len)
1064 {
1065 struct grub_xfs_data *data =
1066 (struct grub_xfs_data *) file->data;
1067
1068 return grub_xfs_read_file (&data->diropen,
1069 file->read_hook, file->read_hook_data,
1070 file->offset, len, buf, 0);
1071 }
1072
1073
1074 static grub_err_t
1075 grub_xfs_close (grub_file_t file)
1076 {
1077 grub_free (file->data);
1078
1079 grub_dl_unref (my_mod);
1080
1081 return GRUB_ERR_NONE;
1082 }
1083
1084
1085 static grub_err_t
1086 grub_xfs_label (grub_device_t device, char **label)
1087 {
1088 struct grub_xfs_data *data;
1089 grub_disk_t disk = device->disk;
1090
1091 grub_dl_ref (my_mod);
1092
1093 data = grub_xfs_mount (disk);
1094 if (data)
1095 *label = grub_strndup ((char *) (data->sblock.label), 12);
1096 else
1097 *label = 0;
1098
1099 grub_dl_unref (my_mod);
1100
1101 grub_free (data);
1102
1103 return grub_errno;
1104 }
1105
1106 static grub_err_t
1107 grub_xfs_uuid (grub_device_t device, char **uuid)
1108 {
1109 struct grub_xfs_data *data;
1110 grub_disk_t disk = device->disk;
1111
1112 grub_dl_ref (my_mod);
1113
1114 data = grub_xfs_mount (disk);
1115 if (data)
1116 {
1117 *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
1118 grub_be_to_cpu16 (data->sblock.uuid[0]),
1119 grub_be_to_cpu16 (data->sblock.uuid[1]),
1120 grub_be_to_cpu16 (data->sblock.uuid[2]),
1121 grub_be_to_cpu16 (data->sblock.uuid[3]),
1122 grub_be_to_cpu16 (data->sblock.uuid[4]),
1123 grub_be_to_cpu16 (data->sblock.uuid[5]),
1124 grub_be_to_cpu16 (data->sblock.uuid[6]),
1125 grub_be_to_cpu16 (data->sblock.uuid[7]));
1126 }
1127 else
1128 *uuid = NULL;
1129
1130 grub_dl_unref (my_mod);
1131
1132 grub_free (data);
1133
1134 return grub_errno;
1135 }
1136
1137 \f
1138
1139 static struct grub_fs grub_xfs_fs =
1140 {
1141 .name = "xfs",
1142 .fs_dir = grub_xfs_dir,
1143 .fs_open = grub_xfs_open,
1144 .fs_read = grub_xfs_read,
1145 .fs_close = grub_xfs_close,
1146 .fs_label = grub_xfs_label,
1147 .fs_uuid = grub_xfs_uuid,
1148 #ifdef GRUB_UTIL
1149 .reserved_first_sector = 0,
1150 .blocklist_install = 1,
1151 #endif
1152 .next = 0
1153 };
1154
1155 GRUB_MOD_INIT(xfs)
1156 {
1157 grub_fs_register (&grub_xfs_fs);
1158 my_mod = mod;
1159 }
1160
1161 GRUB_MOD_FINI(xfs)
1162 {
1163 grub_fs_unregister (&grub_xfs_fs);
1164 }