3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
21 #include <grub/file.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
26 #include <grub/time.h>
27 #include <grub/types.h>
28 #include <grub/fshelp.h>
30 GRUB_MOD_LICENSE ("GPLv3+");
32 #define NSEC_PER_SEC ((grub_int64_t) 1000000000)
34 // GRUB 2.04 doesn't have safemath.h
35 // #include <grub/safemath.h>
37 // gcc < 5.1 doesn't support __builtin_add_overflow and __builtin_mul_overflow
38 // #define grub_add(a, b, res) __builtin_add_overflow(a, b, res)
39 // #define grub_mul(a, b, res) __builtin_mul_overflow(a, b, res)
40 // Warning: This is unsafe!
41 #define grub_add(a, b, res) ({ *(res) = (a) + (b); 0; })
43 #define grub_mul(a, b, res) ({ *(res) = (a) * (b); 0; })
45 #define XFS_INODE_EXTENTS 9
47 #define XFS_INODE_FORMAT_INO 1
48 #define XFS_INODE_FORMAT_EXT 2
49 #define XFS_INODE_FORMAT_BTREE 3
51 /* Superblock version field flags */
52 #define XFS_SB_VERSION_NUMBITS 0x000f
53 #define XFS_SB_VERSION_ATTRBIT 0x0010
54 #define XFS_SB_VERSION_NLINKBIT 0x0020
55 #define XFS_SB_VERSION_QUOTABIT 0x0040
56 #define XFS_SB_VERSION_ALIGNBIT 0x0080
57 #define XFS_SB_VERSION_DALIGNBIT 0x0100
58 #define XFS_SB_VERSION_LOGV2BIT 0x0400
59 #define XFS_SB_VERSION_SECTORBIT 0x0800
60 #define XFS_SB_VERSION_EXTFLGBIT 0x1000
61 #define XFS_SB_VERSION_DIRV2BIT 0x2000
62 #define XFS_SB_VERSION_MOREBITSBIT 0x8000
63 #define XFS_SB_VERSION_BITS_SUPPORTED \
64 (XFS_SB_VERSION_NUMBITS | \
65 XFS_SB_VERSION_ATTRBIT | \
66 XFS_SB_VERSION_NLINKBIT | \
67 XFS_SB_VERSION_QUOTABIT | \
68 XFS_SB_VERSION_ALIGNBIT | \
69 XFS_SB_VERSION_DALIGNBIT | \
70 XFS_SB_VERSION_LOGV2BIT | \
71 XFS_SB_VERSION_SECTORBIT | \
72 XFS_SB_VERSION_EXTFLGBIT | \
73 XFS_SB_VERSION_DIRV2BIT | \
74 XFS_SB_VERSION_MOREBITSBIT)
76 /* Recognized xfs format versions */
77 #define XFS_SB_VERSION_4 4 /* Good old XFS filesystem */
78 #define XFS_SB_VERSION_5 5 /* CRC enabled filesystem */
80 /* features2 field flags */
81 #define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
82 #define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
83 #define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32-bit project ids */
84 #define XFS_SB_VERSION2_FTYPE 0x00000200 /* inode type in dir */
85 #define XFS_SB_VERSION2_BITS_SUPPORTED \
86 (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
87 XFS_SB_VERSION2_ATTR2BIT | \
88 XFS_SB_VERSION2_PROJID32BIT | \
89 XFS_SB_VERSION2_FTYPE)
91 /* Inode flags2 flags */
92 #define XFS_DIFLAG2_BIGTIME_BIT 3
93 #define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT)
94 #define XFS_DIFLAG2_NREXT64_BIT 4
95 #define XFS_DIFLAG2_NREXT64 (1 << XFS_DIFLAG2_NREXT64_BIT)
97 /* incompat feature flags */
98 #define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
99 #define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */
100 #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */
101 #define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */
102 #define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */
103 #define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */
104 #define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6) /* exchangerange supported */
105 #define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 7) /* parent pointers */
106 #define XFS_SB_FEAT_INCOMPAT_METADIR (1 << 8) /* metadata dir tree */
109 * Directory entries with ftype are explicitly handled by GRUB code.
111 * We do not currently read the inode btrees, so it is safe to read filesystems
112 * with the XFS_SB_FEAT_INCOMPAT_SPINODES feature.
114 * We do not currently verify metadata UUID, so it is safe to read filesystems
115 * with the XFS_SB_FEAT_INCOMPAT_META_UUID feature.
117 * We do not currently replay the log, so it is safe to read filesystems
118 * with the XFS_SB_FEAT_INCOMPAT_EXCHRANGE feature.
120 * We do not currently read directory parent pointers, so it is safe to read
121 * filesystems with the XFS_SB_FEAT_INCOMPAT_PARENT feature.
123 * We do not currently look at realtime or quota metadata, so it is safe to
124 * read filesystems with the XFS_SB_FEAT_INCOMPAT_METADIR feature.
126 #define XFS_SB_FEAT_INCOMPAT_SUPPORTED \
127 (XFS_SB_FEAT_INCOMPAT_FTYPE | \
128 XFS_SB_FEAT_INCOMPAT_SPINODES | \
129 XFS_SB_FEAT_INCOMPAT_META_UUID | \
130 XFS_SB_FEAT_INCOMPAT_BIGTIME | \
131 XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \
132 XFS_SB_FEAT_INCOMPAT_NREXT64 | \
133 XFS_SB_FEAT_INCOMPAT_EXCHRANGE | \
134 XFS_SB_FEAT_INCOMPAT_PARENT | \
135 XFS_SB_FEAT_INCOMPAT_METADIR)
137 struct grub_xfs_sblock
139 grub_uint8_t magic
[4];
141 grub_uint8_t unused1
[24];
142 grub_uint16_t uuid
[8];
143 grub_uint8_t unused2
[8];
144 grub_uint64_t rootino
;
145 grub_uint8_t unused3
[20];
146 grub_uint32_t agsize
;
147 grub_uint8_t unused4
[12];
148 grub_uint16_t version
;
149 grub_uint8_t unused5
[6];
150 grub_uint8_t label
[12];
151 grub_uint8_t log2_bsize
;
152 grub_uint8_t log2_sect
;
153 grub_uint8_t log2_inode
;
154 grub_uint8_t log2_inop
;
155 grub_uint8_t log2_agblk
;
156 grub_uint8_t unused6
[67];
157 grub_uint8_t log2_dirblk
;
158 grub_uint8_t unused7
[7];
159 grub_uint32_t features2
;
160 grub_uint8_t unused8
[4];
161 grub_uint32_t sb_features_compat
;
162 grub_uint32_t sb_features_ro_compat
;
163 grub_uint32_t sb_features_incompat
;
164 grub_uint32_t sb_features_log_incompat
;
167 struct grub_xfs_dir_header
170 grub_uint8_t largeino
;
175 } GRUB_PACKED parent
;
178 /* Structure for directory entry inlined in the inode */
179 struct grub_xfs_dir_entry
182 grub_uint16_t offset
;
184 /* Inode number follows, 32 / 64 bits. */
187 /* Structure for directory entry in a block */
188 struct grub_xfs_dir2_entry
194 struct grub_xfs_extent
196 /* This should be a bitfield but bietfields are unportable, so just have
197 a raw array and functions extracting useful info from it.
199 grub_uint32_t raw
[4];
202 struct grub_xfs_btree_node
204 grub_uint8_t magic
[4];
206 grub_uint16_t numrecs
;
209 /* In V5 here follow crc, uuid, etc. */
210 /* Then follow keys and block pointers */
213 struct grub_xfs_btree_root
216 grub_uint16_t numrecs
;
217 grub_uint64_t keys
[1];
220 struct grub_xfs_time_legacy
223 grub_uint32_t nanosec
;
227 * The struct grub_xfs_inode layout was taken from the
228 * struct xfs_dinode_core which is described here:
229 * https://mirrors.edge.kernel.org/pub/linux/utils/fs/xfs/docs/xfs_filesystem_structure.pdf
231 struct grub_xfs_inode
233 grub_uint8_t magic
[2];
235 grub_uint8_t version
;
237 grub_uint8_t unused2
[18];
238 grub_uint64_t nextents_big
;
243 grub_uint64_t nblocks
;
244 grub_uint32_t extsize
;
245 grub_uint32_t nextents
;
246 grub_uint16_t unused3
;
247 grub_uint8_t fork_offset
;
248 grub_uint8_t unused4
[17]; /* Last member of inode v2. */
249 grub_uint8_t unused5
[20]; /* First member of inode v3. */
250 grub_uint64_t flags2
;
251 grub_uint8_t unused6
[48]; /* Last member of inode v3. */
254 #define XFS_V3_INODE_SIZE sizeof(struct grub_xfs_inode)
255 /* Size of struct grub_xfs_inode v2, up to unused4 member included. */
256 #define XFS_V2_INODE_SIZE (XFS_V3_INODE_SIZE - 76)
258 struct grub_xfs_dir_leaf_entry
260 grub_uint32_t hashval
;
261 grub_uint32_t address
;
264 struct grub_xfs_dirblock_tail
266 grub_uint32_t leaf_count
;
267 grub_uint32_t leaf_stale
;
270 struct grub_fshelp_node
272 struct grub_xfs_data
*data
;
275 struct grub_xfs_inode inode
;
280 grub_size_t data_size
;
281 struct grub_xfs_sblock sblock
;
285 grub_uint32_t agsize
;
286 unsigned int hasftype
:1;
287 unsigned int hascrc
:1;
288 struct grub_fshelp_node diropen
;
291 static grub_dl_t my_mod
;
293 static int grub_xfs_sb_hascrc(struct grub_xfs_data
*data
)
295 return (data
->sblock
.version
& grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS
)) ==
296 grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5
);
299 static int grub_xfs_sb_hasftype(struct grub_xfs_data
*data
)
301 if ((data
->sblock
.version
& grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS
)) ==
302 grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5
) &&
303 data
->sblock
.sb_features_incompat
& grub_cpu_to_be32_compile_time(XFS_SB_FEAT_INCOMPAT_FTYPE
))
305 if (data
->sblock
.version
& grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT
) &&
306 data
->sblock
.features2
& grub_cpu_to_be32_compile_time(XFS_SB_VERSION2_FTYPE
))
311 static int grub_xfs_sb_valid(struct grub_xfs_data
*data
)
313 grub_dprintf("xfs", "Validating superblock\n");
314 if (grub_strncmp ((char *) (data
->sblock
.magic
), "XFSB", 4)
315 || data
->sblock
.log2_bsize
< GRUB_DISK_SECTOR_BITS
316 || ((int) data
->sblock
.log2_bsize
317 + (int) data
->sblock
.log2_dirblk
) >= 27)
319 grub_error (GRUB_ERR_BAD_FS
, "not a XFS filesystem");
322 if ((data
->sblock
.version
& grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS
)) ==
323 grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5
))
325 grub_dprintf("xfs", "XFS v5 superblock detected\n");
326 if (data
->sblock
.sb_features_incompat
&
327 grub_cpu_to_be32_compile_time(~XFS_SB_FEAT_INCOMPAT_SUPPORTED
))
329 grub_error (GRUB_ERR_BAD_FS
, "XFS filesystem has unsupported "
330 "incompatible features");
335 else if ((data
->sblock
.version
& grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS
)) ==
336 grub_cpu_to_be16_compile_time(XFS_SB_VERSION_4
))
338 grub_dprintf("xfs", "XFS v4 superblock detected\n");
339 if (!(data
->sblock
.version
& grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT
)))
341 grub_error (GRUB_ERR_BAD_FS
, "XFS filesystem without V2 directories "
345 if (data
->sblock
.version
& grub_cpu_to_be16_compile_time(~XFS_SB_VERSION_BITS_SUPPORTED
) ||
346 (data
->sblock
.version
& grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT
) &&
347 data
->sblock
.features2
& grub_cpu_to_be16_compile_time(~XFS_SB_VERSION2_BITS_SUPPORTED
)))
349 grub_error (GRUB_ERR_BAD_FS
, "XFS filesystem has unsupported version "
356 grub_error (GRUB_ERR_BAD_FS
, "unsupported XFS filesystem version");
361 grub_xfs_sb_needs_repair (struct grub_xfs_data
*data
)
363 return ((data
->sblock
.version
&
364 grub_cpu_to_be16_compile_time (XFS_SB_VERSION_NUMBITS
)) ==
365 grub_cpu_to_be16_compile_time (XFS_SB_VERSION_5
) &&
366 (data
->sblock
.sb_features_incompat
&
367 grub_cpu_to_be32_compile_time (XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR
)));
370 /* Filetype information as used in inodes. */
371 #define FILETYPE_INO_MASK 0170000
372 #define FILETYPE_INO_REG 0100000
373 #define FILETYPE_INO_DIRECTORY 0040000
374 #define FILETYPE_INO_SYMLINK 0120000
377 GRUB_XFS_INO_AGBITS(struct grub_xfs_data
*data
)
379 return ((data
)->sblock
.log2_agblk
+ (data
)->sblock
.log2_inop
);
382 static inline grub_uint64_t
383 GRUB_XFS_INO_INOINAG (struct grub_xfs_data
*data
,
386 return (ino
& ((1LL << GRUB_XFS_INO_AGBITS (data
)) - 1));
389 static inline grub_uint64_t
390 GRUB_XFS_INO_AG (struct grub_xfs_data
*data
,
393 return (ino
>> GRUB_XFS_INO_AGBITS (data
));
396 static inline grub_disk_addr_t
397 GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data
*data
, grub_disk_addr_t fsb
)
399 return ((fsb
>> data
->sblock
.log2_agblk
) * data
->agsize
400 + (fsb
& ((1LL << data
->sblock
.log2_agblk
) - 1)));
403 static inline grub_uint64_t
404 GRUB_XFS_EXTENT_OFFSET (struct grub_xfs_extent
*exts
, int ex
)
406 return ((grub_be_to_cpu32 (exts
[ex
].raw
[0]) & ~(1 << 31)) << 23
407 | grub_be_to_cpu32 (exts
[ex
].raw
[1]) >> 9);
410 static inline grub_uint64_t
411 GRUB_XFS_EXTENT_BLOCK (struct grub_xfs_extent
*exts
, int ex
)
413 return ((grub_uint64_t
) (grub_be_to_cpu32 (exts
[ex
].raw
[1])
415 | (grub_uint64_t
) grub_be_to_cpu32 (exts
[ex
].raw
[2]) << 11
416 | grub_be_to_cpu32 (exts
[ex
].raw
[3]) >> 21);
419 static inline grub_uint64_t
420 GRUB_XFS_EXTENT_SIZE (struct grub_xfs_extent
*exts
, int ex
)
422 return (grub_be_to_cpu32 (exts
[ex
].raw
[3]) & ((1 << 21) - 1));
425 static inline grub_uint64_t
426 grub_xfs_inode_block (struct grub_xfs_data
*data
,
429 long long int inoinag
= GRUB_XFS_INO_INOINAG (data
, ino
);
430 long long ag
= GRUB_XFS_INO_AG (data
, ino
);
433 block
= (inoinag
>> data
->sblock
.log2_inop
) + ag
* data
->agsize
;
434 block
<<= (data
->sblock
.log2_bsize
- GRUB_DISK_SECTOR_BITS
);
440 grub_xfs_inode_offset (struct grub_xfs_data
*data
,
443 int inoag
= GRUB_XFS_INO_INOINAG (data
, ino
);
444 return ((inoag
& ((1 << data
->sblock
.log2_inop
) - 1)) <<
445 data
->sblock
.log2_inode
);
448 static inline grub_size_t
449 grub_xfs_inode_size(struct grub_xfs_data
*data
)
451 return (grub_size_t
)1 << data
->sblock
.log2_inode
;
455 * Returns size occupied by XFS inode stored in memory - we store struct
456 * grub_fshelp_node there but on disk inode size may be actually larger than
457 * struct grub_xfs_inode so we need to account for that so that we can read
458 * from disk directly into in-memory structure.
460 static inline grub_size_t
461 grub_xfs_fshelp_size(struct grub_xfs_data
*data
)
463 return sizeof (struct grub_fshelp_node
) - sizeof (struct grub_xfs_inode
)
464 + grub_xfs_inode_size(data
);
467 /* This should return void * but XFS code is error-prone with alignment, so
468 return char to retain cast-align.
471 grub_xfs_inode_data(struct grub_xfs_inode
*inode
)
473 if (inode
->version
<= 2)
474 return ((char *)inode
) + XFS_V2_INODE_SIZE
;
475 return ((char *)inode
) + XFS_V3_INODE_SIZE
;
478 static struct grub_xfs_dir_entry
*
479 grub_xfs_inline_de(struct grub_xfs_dir_header
*head
)
482 With small inode numbers the header is 4 bytes smaller because of
483 smaller parent pointer
485 return (struct grub_xfs_dir_entry
*)
486 (((char *) head
) + sizeof(struct grub_xfs_dir_header
) -
487 (head
->largeino
? 0 : sizeof(grub_uint32_t
)));
490 static grub_uint8_t
*
491 grub_xfs_inline_de_inopos(struct grub_xfs_data
*data
,
492 struct grub_xfs_dir_entry
*de
)
494 return ((grub_uint8_t
*)(de
+ 1)) + de
->len
- 1 + (data
->hasftype
? 1 : 0);
497 static struct grub_xfs_dir_entry
*
498 grub_xfs_inline_next_de(struct grub_xfs_data
*data
,
499 struct grub_xfs_dir_header
*head
,
500 struct grub_xfs_dir_entry
*de
)
502 char *p
= (char *)de
+ sizeof(struct grub_xfs_dir_entry
) - 1 + de
->len
;
504 p
+= head
->largeino
? sizeof(grub_uint64_t
) : sizeof(grub_uint32_t
);
508 return (struct grub_xfs_dir_entry
*)p
;
511 static struct grub_xfs_dirblock_tail
*
512 grub_xfs_dir_tail(struct grub_xfs_data
*data
, void *dirblock
)
514 int dirblksize
= 1 << (data
->sblock
.log2_bsize
+ data
->sblock
.log2_dirblk
);
516 return (struct grub_xfs_dirblock_tail
*)
517 ((char *)dirblock
+ dirblksize
- sizeof (struct grub_xfs_dirblock_tail
));
520 static struct grub_xfs_dir2_entry
*
521 grub_xfs_first_de(struct grub_xfs_data
*data
, void *dirblock
)
524 return (struct grub_xfs_dir2_entry
*)((char *)dirblock
+ 64);
525 return (struct grub_xfs_dir2_entry
*)((char *)dirblock
+ 16);
528 static struct grub_xfs_dir2_entry
*
529 grub_xfs_next_de(struct grub_xfs_data
*data
, struct grub_xfs_dir2_entry
*de
)
531 int size
= sizeof (struct grub_xfs_dir2_entry
) + de
->len
+ 2 /* Tag */;
534 size
++; /* File type */
535 return (struct grub_xfs_dir2_entry
*)(((char *)de
) + ALIGN_UP(size
, 8));
538 /* This should return void * but XFS code is error-prone with alignment, so
539 return char to retain cast-align.
542 grub_xfs_btree_keys(struct grub_xfs_data
*data
,
543 struct grub_xfs_btree_node
*leaf
)
545 char *keys
= (char *)(leaf
+ 1);
548 keys
+= 48; /* skip crc, uuid, ... */
553 grub_xfs_read_inode (struct grub_xfs_data
*data
, grub_uint64_t ino
,
554 struct grub_xfs_inode
*inode
)
556 grub_uint64_t block
= grub_xfs_inode_block (data
, ino
);
557 int offset
= grub_xfs_inode_offset (data
, ino
);
559 grub_dprintf("xfs", "Reading inode (%" PRIuGRUB_UINT64_T
") - %" PRIuGRUB_UINT64_T
", %d\n",
561 /* Read the inode. */
562 if (grub_disk_read (data
->disk
, block
, offset
, grub_xfs_inode_size(data
),
566 if (grub_strncmp ((char *) inode
->magic
, "IN", 2))
567 return grub_error (GRUB_ERR_BAD_FS
, "not a correct XFS inode");
573 get_fsb (const void *keys
, int idx
)
575 const char *p
= (const char *) keys
+ sizeof(grub_uint64_t
) * idx
;
576 return grub_be_to_cpu64 (grub_get_unaligned64 (p
));
580 grub_xfs_inode_has_large_extent_counts (const struct grub_xfs_inode
*inode
)
582 return inode
->version
>= 3 &&
583 (inode
->flags2
& grub_cpu_to_be64_compile_time (XFS_DIFLAG2_NREXT64
));
587 grub_xfs_get_inode_nextents (struct grub_xfs_inode
*inode
)
589 return (grub_xfs_inode_has_large_extent_counts (inode
)) ?
590 grub_be_to_cpu64 (inode
->nextents_big
) :
591 grub_be_to_cpu32 (inode
->nextents
);
594 static grub_disk_addr_t
595 grub_xfs_read_block (grub_fshelp_node_t node
, grub_disk_addr_t fileblock
)
597 struct grub_xfs_btree_node
*leaf
= 0;
598 grub_uint64_t ex
, nrec
;
599 struct grub_xfs_extent
*exts
;
600 grub_uint64_t ret
= 0;
602 if (node
->inode
.format
== XFS_INODE_FORMAT_BTREE
)
604 struct grub_xfs_btree_root
*root
;
608 leaf
= grub_malloc (node
->data
->bsize
);
612 root
= (struct grub_xfs_btree_root
*) grub_xfs_inode_data(&node
->inode
);
613 nrec
= grub_be_to_cpu16 (root
->numrecs
);
614 keys
= (char *) &root
->keys
[0];
615 if (node
->inode
.fork_offset
)
616 recoffset
= (node
->inode
.fork_offset
- 1) / 2;
618 recoffset
= (grub_xfs_inode_size(node
->data
)
619 - ((char *) keys
- (char *) &node
->inode
))
620 / (2 * sizeof (grub_uint64_t
));
624 grub_addr_t keys_end
, data_end
;
626 if (grub_mul (sizeof (grub_uint64_t
), nrec
, &keys_end
) ||
627 grub_add ((grub_addr_t
) keys
, keys_end
, &keys_end
) ||
628 grub_add ((grub_addr_t
) node
->data
, node
->data
->data_size
, &data_end
) ||
631 grub_error (GRUB_ERR_BAD_FS
, "invalid number of XFS root keys");
636 for (i
= 0; i
< nrec
; i
++)
638 if (fileblock
< get_fsb(keys
, i
))
649 if (grub_disk_read (node
->data
->disk
,
650 GRUB_XFS_FSB_TO_BLOCK (node
->data
, get_fsb (keys
, i
- 1 + recoffset
)) << (node
->data
->sblock
.log2_bsize
- GRUB_DISK_SECTOR_BITS
),
651 0, node
->data
->bsize
, leaf
))
657 if ((!node
->data
->hascrc
&&
658 grub_strncmp ((char *) leaf
->magic
, "BMAP", 4)) ||
659 (node
->data
->hascrc
&&
660 grub_strncmp ((char *) leaf
->magic
, "BMA3", 4)))
663 grub_error (GRUB_ERR_BAD_FS
, "not a correct XFS BMAP node");
667 nrec
= grub_be_to_cpu16 (leaf
->numrecs
);
668 keys
= grub_xfs_btree_keys(node
->data
, leaf
);
669 recoffset
= ((node
->data
->bsize
- ((char *) keys
671 / (2 * sizeof (grub_uint64_t
)));
674 exts
= (struct grub_xfs_extent
*) keys
;
676 else if (node
->inode
.format
== XFS_INODE_FORMAT_EXT
)
678 grub_addr_t exts_end
= 0;
679 grub_addr_t data_end
= 0;
681 nrec
= grub_xfs_get_inode_nextents (&node
->inode
);
682 exts
= (struct grub_xfs_extent
*) grub_xfs_inode_data(&node
->inode
);
684 if (grub_mul (sizeof (struct grub_xfs_extent
), nrec
, &exts_end
) ||
685 grub_add ((grub_addr_t
) node
->data
, exts_end
, &exts_end
) ||
686 grub_add ((grub_addr_t
) node
->data
, node
->data
->data_size
, &data_end
) ||
689 grub_error (GRUB_ERR_BAD_FS
, "invalid number of XFS extents");
695 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
696 "XFS does not support inode format %d yet",
701 /* Iterate over each extent to figure out which extent has
702 the block we are looking for. */
703 for (ex
= 0; ex
< nrec
; ex
++)
705 grub_uint64_t start
= GRUB_XFS_EXTENT_BLOCK (exts
, ex
);
706 grub_uint64_t offset
= GRUB_XFS_EXTENT_OFFSET (exts
, ex
);
707 grub_uint64_t size
= GRUB_XFS_EXTENT_SIZE (exts
, ex
);
710 if (fileblock
< offset
)
712 else if (fileblock
< offset
+ size
)
714 ret
= (fileblock
- offset
+ start
);
721 return GRUB_XFS_FSB_TO_BLOCK(node
->data
, ret
);
725 /* Read LEN bytes from the file described by DATA starting with byte
726 POS. Return the amount of read bytes in READ. */
728 grub_xfs_read_file (grub_fshelp_node_t node
,
729 grub_disk_read_hook_t read_hook
, void *read_hook_data
,
730 grub_off_t pos
, grub_size_t len
, char *buf
, grub_uint32_t header_size
)
732 return grub_fshelp_read_file (node
->data
->disk
, node
,
733 read_hook
, read_hook_data
,
734 pos
, len
, buf
, grub_xfs_read_block
,
735 grub_be_to_cpu64 (node
->inode
.size
) + header_size
,
736 node
->data
->sblock
.log2_bsize
737 - GRUB_DISK_SECTOR_BITS
, 0);
742 grub_xfs_read_symlink (grub_fshelp_node_t node
)
744 grub_ssize_t size
= grub_be_to_cpu64 (node
->inode
.size
);
749 grub_error (GRUB_ERR_BAD_FS
, "invalid symlink");
753 switch (node
->inode
.format
)
755 case XFS_INODE_FORMAT_INO
:
756 return grub_strndup (grub_xfs_inode_data(&node
->inode
), size
);
758 case XFS_INODE_FORMAT_EXT
:
761 grub_ssize_t numread
;
764 if (node
->data
->hascrc
)
767 if (grub_add (size
, 1, &sz
))
769 grub_error (GRUB_ERR_OUT_OF_RANGE
, N_("symlink size overflow"));
772 symlink
= grub_malloc (sz
);
776 node
->inode
.size
= grub_be_to_cpu64 (size
+ off
);
777 numread
= grub_xfs_read_file (node
, 0, 0, off
, size
, symlink
, off
);
783 symlink
[size
] = '\0';
792 static enum grub_fshelp_filetype
793 grub_xfs_mode_to_filetype (grub_uint16_t mode
)
795 if ((grub_be_to_cpu16 (mode
)
796 & FILETYPE_INO_MASK
) == FILETYPE_INO_DIRECTORY
)
797 return GRUB_FSHELP_DIR
;
798 else if ((grub_be_to_cpu16 (mode
)
799 & FILETYPE_INO_MASK
) == FILETYPE_INO_SYMLINK
)
800 return GRUB_FSHELP_SYMLINK
;
801 else if ((grub_be_to_cpu16 (mode
)
802 & FILETYPE_INO_MASK
) == FILETYPE_INO_REG
)
803 return GRUB_FSHELP_REG
;
804 return GRUB_FSHELP_UNKNOWN
;
808 /* Context for grub_xfs_iterate_dir. */
809 struct grub_xfs_iterate_dir_ctx
811 grub_fshelp_iterate_dir_hook_t hook
;
813 struct grub_fshelp_node
*diro
;
816 /* Helper for grub_xfs_iterate_dir. */
817 static int iterate_dir_call_hook (grub_uint64_t ino
, const char *filename
,
818 struct grub_xfs_iterate_dir_ctx
*ctx
)
820 struct grub_fshelp_node
*fdiro
;
824 if (grub_add (grub_xfs_fshelp_size(ctx
->diro
->data
), 1, &sz
))
826 grub_error (GRUB_ERR_OUT_OF_RANGE
, N_("directory data size overflow"));
830 fdiro
= grub_malloc (sz
);
837 /* The inode should be read, otherwise the filetype can
838 not be determined. */
840 fdiro
->inode_read
= 1;
841 fdiro
->data
= ctx
->diro
->data
;
842 err
= grub_xfs_read_inode (ctx
->diro
->data
, ino
, &fdiro
->inode
);
850 return ctx
->hook (filename
, grub_xfs_mode_to_filetype (fdiro
->inode
.mode
),
851 fdiro
, ctx
->hook_data
);
855 grub_xfs_iterate_dir (grub_fshelp_node_t dir
,
856 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
858 struct grub_fshelp_node
*diro
= (struct grub_fshelp_node
*) dir
;
859 struct grub_xfs_iterate_dir_ctx ctx
= {
861 .hook_data
= hook_data
,
865 switch (diro
->inode
.format
)
867 case XFS_INODE_FORMAT_INO
:
869 struct grub_xfs_dir_header
*head
= (struct grub_xfs_dir_header
*) grub_xfs_inode_data(&diro
->inode
);
870 struct grub_xfs_dir_entry
*de
= grub_xfs_inline_de(head
);
871 int smallino
= !head
->largeino
;
873 grub_uint64_t parent
;
875 /* If small inode numbers are used to pack the direntry, the
876 parent inode number is small too. */
878 parent
= grub_be_to_cpu32 (head
->parent
.i4
);
880 parent
= grub_be_to_cpu64 (head
->parent
.i8
);
882 /* Synthesize the direntries for `.' and `..'. */
883 if (iterate_dir_call_hook (diro
->ino
, ".", &ctx
))
886 if (iterate_dir_call_hook (parent
, "..", &ctx
))
889 for (i
= 0; i
< head
->count
&&
890 (grub_uint8_t
*) de
< ((grub_uint8_t
*) dir
+ grub_xfs_fshelp_size (dir
->data
)); i
++)
893 grub_uint8_t
*inopos
= grub_xfs_inline_de_inopos(dir
->data
, de
);
896 if ((inopos
+ (smallino
? 4 : 8)) > (grub_uint8_t
*) dir
+ grub_xfs_fshelp_size (dir
->data
))
898 grub_error (GRUB_ERR_BAD_FS
, "invalid XFS inode");
903 /* inopos might be unaligned. */
905 ino
= (((grub_uint32_t
) inopos
[0]) << 24)
906 | (((grub_uint32_t
) inopos
[1]) << 16)
907 | (((grub_uint32_t
) inopos
[2]) << 8)
908 | (((grub_uint32_t
) inopos
[3]) << 0);
910 ino
= (((grub_uint64_t
) inopos
[0]) << 56)
911 | (((grub_uint64_t
) inopos
[1]) << 48)
912 | (((grub_uint64_t
) inopos
[2]) << 40)
913 | (((grub_uint64_t
) inopos
[3]) << 32)
914 | (((grub_uint64_t
) inopos
[4]) << 24)
915 | (((grub_uint64_t
) inopos
[5]) << 16)
916 | (((grub_uint64_t
) inopos
[6]) << 8)
917 | (((grub_uint64_t
) inopos
[7]) << 0);
919 c
= de
->name
[de
->len
];
920 de
->name
[de
->len
] = '\0';
921 if (iterate_dir_call_hook (ino
, de
->name
, &ctx
))
923 de
->name
[de
->len
] = c
;
926 de
->name
[de
->len
] = c
;
928 de
= grub_xfs_inline_next_de(dir
->data
, head
, de
);
933 case XFS_INODE_FORMAT_BTREE
:
934 case XFS_INODE_FORMAT_EXT
:
936 grub_ssize_t numread
;
939 int dirblk_size
, dirblk_log2
;
941 dirblk_log2
= (dir
->data
->sblock
.log2_bsize
942 + dir
->data
->sblock
.log2_dirblk
);
943 dirblk_size
= 1 << dirblk_log2
;
945 dirblock
= grub_malloc (dirblk_size
);
949 /* Iterate over every block the directory has. */
951 blk
< (grub_be_to_cpu64 (dir
->inode
.size
)
955 struct grub_xfs_dir2_entry
*direntry
=
956 grub_xfs_first_de(dir
->data
, dirblock
);
958 char *end
= dirblock
+ dirblk_size
;
961 numread
= grub_xfs_read_file (dir
, 0, 0,
963 dirblk_size
, dirblock
, 0);
964 if (numread
!= dirblk_size
)
966 grub_free (dirblock
);
971 * If this data block isn't actually part of the extent list then
972 * grub_xfs_read_file() returns a block of zeros. So, if the magic
973 * number field is all zeros then this block should be skipped.
975 magic
= *(grub_uint32_t
*)(void *) dirblock
;
980 * Leaf and tail information are only in the data block if the number
983 if (grub_xfs_get_inode_nextents (&dir
->inode
) == 1)
985 struct grub_xfs_dirblock_tail
*tail
= grub_xfs_dir_tail (dir
->data
, dirblock
);
989 /* Subtract the space used by leaf nodes. */
990 end
-= grub_be_to_cpu32 (tail
->leaf_count
) * sizeof (struct grub_xfs_dir_leaf_entry
);
992 entries
= grub_be_to_cpu32 (tail
->leaf_count
) - grub_be_to_cpu32 (tail
->leaf_stale
);
998 /* Iterate over all entries within this block. */
999 while ((char *) direntry
< (char *) end
)
1001 grub_uint8_t
*freetag
;
1004 freetag
= (grub_uint8_t
*) direntry
;
1006 if (grub_get_unaligned16 (freetag
) == 0XFFFF)
1008 grub_uint8_t
*skip
= (freetag
+ sizeof (grub_uint16_t
));
1010 /* This entry is not used, go to the next one. */
1011 direntry
= (struct grub_xfs_dir2_entry
*)
1012 (((char *)direntry
) +
1013 grub_be_to_cpu16 (grub_get_unaligned16 (skip
)));
1018 filename
= (char *)(direntry
+ 1);
1019 if (filename
+ direntry
->len
+ 1 > (char *) end
)
1021 grub_error (GRUB_ERR_BAD_FS
, "invalid XFS directory entry");
1025 /* The byte after the filename is for the filetype, padding, or
1026 tag, which is not used by GRUB. So it can be overwritten. */
1027 filename
[direntry
->len
] = '\0';
1029 if (iterate_dir_call_hook (grub_be_to_cpu64(direntry
->inode
),
1032 grub_free (dirblock
);
1037 * The expected number of directory entries is only tracked for the
1038 * single extent case.
1040 if (grub_xfs_get_inode_nextents (&dir
->inode
) == 1)
1042 /* Check if last direntry in this block is reached. */
1048 /* Select the next directory entry. */
1049 direntry
= grub_xfs_next_de(dir
->data
, direntry
);
1052 grub_free (dirblock
);
1057 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
1058 "XFS does not support inode format %d yet",
1059 diro
->inode
.format
);
1065 static struct grub_xfs_data
*
1066 grub_xfs_mount (grub_disk_t disk
)
1068 struct grub_xfs_data
*data
= 0;
1071 data
= grub_zalloc (sizeof (struct grub_xfs_data
));
1075 data
->data_size
= sizeof (struct grub_xfs_data
);
1077 grub_dprintf("xfs", "Reading sb\n");
1078 /* Read the superblock. */
1079 if (grub_disk_read (disk
, 0, 0,
1080 sizeof (struct grub_xfs_sblock
), &data
->sblock
))
1083 if (!grub_xfs_sb_valid(data
))
1086 if (grub_xfs_sb_needs_repair (data
))
1087 grub_dprintf ("xfs", "XFS filesystem needs repair, boot may fail\n");
1089 if (grub_add (grub_xfs_inode_size (data
),
1090 sizeof (struct grub_xfs_data
) - sizeof (struct grub_xfs_inode
) + 1, &sz
))
1093 data
= grub_realloc (data
, sz
);
1098 data
->data_size
= sz
;
1099 data
->diropen
.data
= data
;
1100 data
->diropen
.ino
= grub_be_to_cpu64(data
->sblock
.rootino
);
1101 data
->diropen
.inode_read
= 1;
1102 data
->bsize
= grub_be_to_cpu32 (data
->sblock
.bsize
);
1103 data
->agsize
= grub_be_to_cpu32 (data
->sblock
.agsize
);
1104 data
->hasftype
= grub_xfs_sb_hasftype(data
);
1105 data
->hascrc
= grub_xfs_sb_hascrc(data
);
1109 grub_dprintf("xfs", "Reading root ino %" PRIuGRUB_UINT64_T
"\n",
1110 grub_cpu_to_be64(data
->sblock
.rootino
));
1112 grub_xfs_read_inode (data
, data
->diropen
.ino
, &data
->diropen
.inode
);
1117 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
|| grub_errno
== GRUB_ERR_NONE
)
1118 grub_error (GRUB_ERR_BAD_FS
, "not an XFS filesystem");
1125 /* Context for grub_xfs_dir. */
1126 struct grub_xfs_dir_ctx
1128 grub_fs_dir_hook_t hook
;
1132 /* Bigtime inodes helpers. */
1133 #define XFS_BIGTIME_EPOCH_OFFSET (-(grub_int64_t) GRUB_INT32_MIN)
1135 static int grub_xfs_inode_has_bigtime (const struct grub_xfs_inode
*inode
)
1137 return inode
->version
>= 3 &&
1138 (inode
->flags2
& grub_cpu_to_be64_compile_time (XFS_DIFLAG2_BIGTIME
));
1142 grub_xfs_get_inode_time (struct grub_xfs_inode
*inode
)
1144 struct grub_xfs_time_legacy
*lts
;
1146 if (grub_xfs_inode_has_bigtime (inode
))
1147 return grub_divmod64 (grub_be_to_cpu64 (inode
->mtime
), NSEC_PER_SEC
, NULL
) - XFS_BIGTIME_EPOCH_OFFSET
;
1149 lts
= (struct grub_xfs_time_legacy
*) &inode
->mtime
;
1150 return grub_be_to_cpu32 (lts
->sec
);
1153 /* Helper for grub_xfs_dir. */
1155 grub_xfs_dir_iter (const char *filename
, enum grub_fshelp_filetype filetype
,
1156 grub_fshelp_node_t node
, void *data
)
1158 struct grub_xfs_dir_ctx
*ctx
= data
;
1159 struct grub_dirhook_info info
;
1161 grub_memset (&info
, 0, sizeof (info
));
1162 if (node
->inode_read
)
1165 info
.mtime
= grub_xfs_get_inode_time (&node
->inode
);
1167 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
1169 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
1173 grub_xfs_dir (grub_device_t device
, const char *path
,
1174 grub_fs_dir_hook_t hook
, void *hook_data
)
1176 struct grub_xfs_dir_ctx ctx
= { hook
, hook_data
};
1177 struct grub_xfs_data
*data
= 0;
1178 struct grub_fshelp_node
*fdiro
= 0;
1180 grub_dl_ref (my_mod
);
1182 data
= grub_xfs_mount (device
->disk
);
1186 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_xfs_iterate_dir
,
1187 grub_xfs_read_symlink
, GRUB_FSHELP_DIR
);
1191 grub_xfs_iterate_dir (fdiro
, grub_xfs_dir_iter
, &ctx
);
1194 if (fdiro
!= &data
->diropen
)
1200 grub_dl_unref (my_mod
);
1206 /* Open a file named NAME and initialize FILE. */
1208 grub_xfs_open (struct grub_file
*file
, const char *name
)
1210 struct grub_xfs_data
*data
;
1211 struct grub_fshelp_node
*fdiro
= 0;
1213 grub_dl_ref (my_mod
);
1215 data
= grub_xfs_mount (file
->device
->disk
);
1219 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_xfs_iterate_dir
,
1220 grub_xfs_read_symlink
, GRUB_FSHELP_REG
);
1224 if (!fdiro
->inode_read
)
1226 grub_xfs_read_inode (data
, fdiro
->ino
, &fdiro
->inode
);
1231 if (fdiro
!= &data
->diropen
)
1233 grub_memcpy (&data
->diropen
, fdiro
, grub_xfs_fshelp_size(data
));
1237 file
->size
= grub_be_to_cpu64 (data
->diropen
.inode
.size
);
1244 if (fdiro
!= &data
->diropen
)
1249 grub_dl_unref (my_mod
);
1256 grub_xfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
1258 struct grub_xfs_data
*data
=
1259 (struct grub_xfs_data
*) file
->data
;
1261 return grub_xfs_read_file (&data
->diropen
,
1262 file
->read_hook
, file
->read_hook_data
,
1263 file
->offset
, len
, buf
, 0);
1268 grub_xfs_close (grub_file_t file
)
1270 grub_free (file
->data
);
1272 grub_dl_unref (my_mod
);
1274 return GRUB_ERR_NONE
;
1279 grub_xfs_label (grub_device_t device
, char **label
)
1281 struct grub_xfs_data
*data
;
1282 grub_disk_t disk
= device
->disk
;
1284 grub_dl_ref (my_mod
);
1286 data
= grub_xfs_mount (disk
);
1288 *label
= grub_strndup ((char *) (data
->sblock
.label
), 12);
1292 grub_dl_unref (my_mod
);
1300 grub_xfs_uuid (grub_device_t device
, char **uuid
)
1302 struct grub_xfs_data
*data
;
1303 grub_disk_t disk
= device
->disk
;
1305 grub_dl_ref (my_mod
);
1307 data
= grub_xfs_mount (disk
);
1310 *uuid
= grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
1311 grub_be_to_cpu16 (data
->sblock
.uuid
[0]),
1312 grub_be_to_cpu16 (data
->sblock
.uuid
[1]),
1313 grub_be_to_cpu16 (data
->sblock
.uuid
[2]),
1314 grub_be_to_cpu16 (data
->sblock
.uuid
[3]),
1315 grub_be_to_cpu16 (data
->sblock
.uuid
[4]),
1316 grub_be_to_cpu16 (data
->sblock
.uuid
[5]),
1317 grub_be_to_cpu16 (data
->sblock
.uuid
[6]),
1318 grub_be_to_cpu16 (data
->sblock
.uuid
[7]));
1323 grub_dl_unref (my_mod
);
1330 static struct grub_fs grub_xfs_fs
=
1333 .fs_dir
= grub_xfs_dir
,
1334 .fs_open
= grub_xfs_open
,
1335 .fs_read
= grub_xfs_read
,
1336 .fs_close
= grub_xfs_close
,
1337 .fs_label
= grub_xfs_label
,
1338 .fs_uuid
= grub_xfs_uuid
,
1340 .reserved_first_sector
= 0,
1341 .blocklist_install
= 1,
1348 //grub_xfs_fs.mod = mod;
1349 grub_fs_register (&grub_xfs_fs
);
1355 grub_fs_unregister (&grub_xfs_fs
);