]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/fs/xfs.c
1.1.07 release
[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/time.h>
27 #include <grub/types.h>
28 #include <grub/fshelp.h>
29
30 GRUB_MOD_LICENSE ("GPLv3+");
31
32 #define NSEC_PER_SEC ((grub_int64_t) 1000000000)
33
34 // GRUB 2.04 doesn't have safemath.h
35 // #include <grub/safemath.h>
36
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; })
42
43 #define grub_mul(a, b, res) ({ *(res) = (a) * (b); 0; })
44
45 #define XFS_INODE_EXTENTS 9
46
47 #define XFS_INODE_FORMAT_INO 1
48 #define XFS_INODE_FORMAT_EXT 2
49 #define XFS_INODE_FORMAT_BTREE 3
50
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)
75
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 */
79
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)
90
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)
96
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 */
107
108 /*
109 * Directory entries with ftype are explicitly handled by GRUB code.
110 *
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.
113 *
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.
116 *
117 * We do not currently replay the log, so it is safe to read filesystems
118 * with the XFS_SB_FEAT_INCOMPAT_EXCHRANGE feature.
119 *
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.
122 *
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.
125 */
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)
136
137 struct grub_xfs_sblock
138 {
139 grub_uint8_t magic[4];
140 grub_uint32_t bsize;
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;
165 } GRUB_PACKED;
166
167 struct grub_xfs_dir_header
168 {
169 grub_uint8_t count;
170 grub_uint8_t largeino;
171 union
172 {
173 grub_uint32_t i4;
174 grub_uint64_t i8;
175 } GRUB_PACKED parent;
176 } GRUB_PACKED;
177
178 /* Structure for directory entry inlined in the inode */
179 struct grub_xfs_dir_entry
180 {
181 grub_uint8_t len;
182 grub_uint16_t offset;
183 char name[1];
184 /* Inode number follows, 32 / 64 bits. */
185 } GRUB_PACKED;
186
187 /* Structure for directory entry in a block */
188 struct grub_xfs_dir2_entry
189 {
190 grub_uint64_t inode;
191 grub_uint8_t len;
192 } GRUB_PACKED;
193
194 struct grub_xfs_extent
195 {
196 /* This should be a bitfield but bietfields are unportable, so just have
197 a raw array and functions extracting useful info from it.
198 */
199 grub_uint32_t raw[4];
200 } GRUB_PACKED;
201
202 struct grub_xfs_btree_node
203 {
204 grub_uint8_t magic[4];
205 grub_uint16_t level;
206 grub_uint16_t numrecs;
207 grub_uint64_t left;
208 grub_uint64_t right;
209 /* In V5 here follow crc, uuid, etc. */
210 /* Then follow keys and block pointers */
211 } GRUB_PACKED;
212
213 struct grub_xfs_btree_root
214 {
215 grub_uint16_t level;
216 grub_uint16_t numrecs;
217 grub_uint64_t keys[1];
218 } GRUB_PACKED;
219
220 struct grub_xfs_time_legacy
221 {
222 grub_uint32_t sec;
223 grub_uint32_t nanosec;
224 } GRUB_PACKED;
225
226 /*
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
230 */
231 struct grub_xfs_inode
232 {
233 grub_uint8_t magic[2];
234 grub_uint16_t mode;
235 grub_uint8_t version;
236 grub_uint8_t format;
237 grub_uint8_t unused2[18];
238 grub_uint64_t nextents_big;
239 grub_uint64_t atime;
240 grub_uint64_t mtime;
241 grub_uint64_t ctime;
242 grub_uint64_t size;
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. */
252 } GRUB_PACKED;
253
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)
257
258 struct grub_xfs_dir_leaf_entry
259 {
260 grub_uint32_t hashval;
261 grub_uint32_t address;
262 } GRUB_PACKED;
263
264 struct grub_xfs_dirblock_tail
265 {
266 grub_uint32_t leaf_count;
267 grub_uint32_t leaf_stale;
268 } GRUB_PACKED;
269
270 struct grub_fshelp_node
271 {
272 struct grub_xfs_data *data;
273 grub_uint64_t ino;
274 int inode_read;
275 struct grub_xfs_inode inode;
276 };
277
278 struct grub_xfs_data
279 {
280 grub_size_t data_size;
281 struct grub_xfs_sblock sblock;
282 grub_disk_t disk;
283 int pos;
284 int bsize;
285 grub_uint32_t agsize;
286 unsigned int hasftype:1;
287 unsigned int hascrc:1;
288 struct grub_fshelp_node diropen;
289 };
290
291 static grub_dl_t my_mod;
292
293 static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
294 {
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);
297 }
298
299 static int grub_xfs_sb_hasftype(struct grub_xfs_data *data)
300 {
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))
304 return 1;
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))
307 return 1;
308 return 0;
309 }
310
311 static int grub_xfs_sb_valid(struct grub_xfs_data *data)
312 {
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)
318 {
319 grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem");
320 return 0;
321 }
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))
324 {
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))
328 {
329 grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported "
330 "incompatible features");
331 return 0;
332 }
333 return 1;
334 }
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))
337 {
338 grub_dprintf("xfs", "XFS v4 superblock detected\n");
339 if (!(data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT)))
340 {
341 grub_error (GRUB_ERR_BAD_FS, "XFS filesystem without V2 directories "
342 "is unsupported");
343 return 0;
344 }
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)))
348 {
349 grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported version "
350 "bits");
351 return 0;
352 }
353 return 1;
354 }
355
356 grub_error (GRUB_ERR_BAD_FS, "unsupported XFS filesystem version");
357 return 0;
358 }
359
360 static int
361 grub_xfs_sb_needs_repair (struct grub_xfs_data *data)
362 {
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)));
368 }
369
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
375
376 static inline int
377 GRUB_XFS_INO_AGBITS(struct grub_xfs_data *data)
378 {
379 return ((data)->sblock.log2_agblk + (data)->sblock.log2_inop);
380 }
381
382 static inline grub_uint64_t
383 GRUB_XFS_INO_INOINAG (struct grub_xfs_data *data,
384 grub_uint64_t ino)
385 {
386 return (ino & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1));
387 }
388
389 static inline grub_uint64_t
390 GRUB_XFS_INO_AG (struct grub_xfs_data *data,
391 grub_uint64_t ino)
392 {
393 return (ino >> GRUB_XFS_INO_AGBITS (data));
394 }
395
396 static inline grub_disk_addr_t
397 GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data *data, grub_disk_addr_t fsb)
398 {
399 return ((fsb >> data->sblock.log2_agblk) * data->agsize
400 + (fsb & ((1LL << data->sblock.log2_agblk) - 1)));
401 }
402
403 static inline grub_uint64_t
404 GRUB_XFS_EXTENT_OFFSET (struct grub_xfs_extent *exts, int ex)
405 {
406 return ((grub_be_to_cpu32 (exts[ex].raw[0]) & ~(1 << 31)) << 23
407 | grub_be_to_cpu32 (exts[ex].raw[1]) >> 9);
408 }
409
410 static inline grub_uint64_t
411 GRUB_XFS_EXTENT_BLOCK (struct grub_xfs_extent *exts, int ex)
412 {
413 return ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex].raw[1])
414 & (0x1ff)) << 43
415 | (grub_uint64_t) grub_be_to_cpu32 (exts[ex].raw[2]) << 11
416 | grub_be_to_cpu32 (exts[ex].raw[3]) >> 21);
417 }
418
419 static inline grub_uint64_t
420 GRUB_XFS_EXTENT_SIZE (struct grub_xfs_extent *exts, int ex)
421 {
422 return (grub_be_to_cpu32 (exts[ex].raw[3]) & ((1 << 21) - 1));
423 }
424
425 static inline grub_uint64_t
426 grub_xfs_inode_block (struct grub_xfs_data *data,
427 grub_uint64_t ino)
428 {
429 long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino);
430 long long ag = GRUB_XFS_INO_AG (data, ino);
431 long long block;
432
433 block = (inoinag >> data->sblock.log2_inop) + ag * data->agsize;
434 block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS);
435 return block;
436 }
437
438
439 static inline int
440 grub_xfs_inode_offset (struct grub_xfs_data *data,
441 grub_uint64_t ino)
442 {
443 int inoag = GRUB_XFS_INO_INOINAG (data, ino);
444 return ((inoag & ((1 << data->sblock.log2_inop) - 1)) <<
445 data->sblock.log2_inode);
446 }
447
448 static inline grub_size_t
449 grub_xfs_inode_size(struct grub_xfs_data *data)
450 {
451 return (grub_size_t)1 << data->sblock.log2_inode;
452 }
453
454 /*
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.
459 */
460 static inline grub_size_t
461 grub_xfs_fshelp_size(struct grub_xfs_data *data)
462 {
463 return sizeof (struct grub_fshelp_node) - sizeof (struct grub_xfs_inode)
464 + grub_xfs_inode_size(data);
465 }
466
467 /* This should return void * but XFS code is error-prone with alignment, so
468 return char to retain cast-align.
469 */
470 static char *
471 grub_xfs_inode_data(struct grub_xfs_inode *inode)
472 {
473 if (inode->version <= 2)
474 return ((char *)inode) + XFS_V2_INODE_SIZE;
475 return ((char *)inode) + XFS_V3_INODE_SIZE;
476 }
477
478 static struct grub_xfs_dir_entry *
479 grub_xfs_inline_de(struct grub_xfs_dir_header *head)
480 {
481 /*
482 With small inode numbers the header is 4 bytes smaller because of
483 smaller parent pointer
484 */
485 return (struct grub_xfs_dir_entry *)
486 (((char *) head) + sizeof(struct grub_xfs_dir_header) -
487 (head->largeino ? 0 : sizeof(grub_uint32_t)));
488 }
489
490 static grub_uint8_t *
491 grub_xfs_inline_de_inopos(struct grub_xfs_data *data,
492 struct grub_xfs_dir_entry *de)
493 {
494 return ((grub_uint8_t *)(de + 1)) + de->len - 1 + (data->hasftype ? 1 : 0);
495 }
496
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)
501 {
502 char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len;
503
504 p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t);
505 if (data->hasftype)
506 p++;
507
508 return (struct grub_xfs_dir_entry *)p;
509 }
510
511 static struct grub_xfs_dirblock_tail *
512 grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock)
513 {
514 int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk);
515
516 return (struct grub_xfs_dirblock_tail *)
517 ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail));
518 }
519
520 static struct grub_xfs_dir2_entry *
521 grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock)
522 {
523 if (data->hascrc)
524 return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64);
525 return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16);
526 }
527
528 static struct grub_xfs_dir2_entry *
529 grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de)
530 {
531 int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */;
532
533 if (data->hasftype)
534 size++; /* File type */
535 return (struct grub_xfs_dir2_entry *)(((char *)de) + ALIGN_UP(size, 8));
536 }
537
538 /* This should return void * but XFS code is error-prone with alignment, so
539 return char to retain cast-align.
540 */
541 static char *
542 grub_xfs_btree_keys(struct grub_xfs_data *data,
543 struct grub_xfs_btree_node *leaf)
544 {
545 char *keys = (char *)(leaf + 1);
546
547 if (data->hascrc)
548 keys += 48; /* skip crc, uuid, ... */
549 return keys;
550 }
551
552 static grub_err_t
553 grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino,
554 struct grub_xfs_inode *inode)
555 {
556 grub_uint64_t block = grub_xfs_inode_block (data, ino);
557 int offset = grub_xfs_inode_offset (data, ino);
558
559 grub_dprintf("xfs", "Reading inode (%" PRIuGRUB_UINT64_T ") - %" PRIuGRUB_UINT64_T ", %d\n",
560 ino, block, offset);
561 /* Read the inode. */
562 if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data),
563 inode))
564 return grub_errno;
565
566 if (grub_strncmp ((char *) inode->magic, "IN", 2))
567 return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode");
568
569 return 0;
570 }
571
572 static grub_uint64_t
573 get_fsb (const void *keys, int idx)
574 {
575 const char *p = (const char *) keys + sizeof(grub_uint64_t) * idx;
576 return grub_be_to_cpu64 (grub_get_unaligned64 (p));
577 }
578
579 static int
580 grub_xfs_inode_has_large_extent_counts (const struct grub_xfs_inode *inode)
581 {
582 return inode->version >= 3 &&
583 (inode->flags2 & grub_cpu_to_be64_compile_time (XFS_DIFLAG2_NREXT64));
584 }
585
586 static grub_uint64_t
587 grub_xfs_get_inode_nextents (struct grub_xfs_inode *inode)
588 {
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);
592 }
593
594 static grub_disk_addr_t
595 grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
596 {
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;
601
602 if (node->inode.format == XFS_INODE_FORMAT_BTREE)
603 {
604 struct grub_xfs_btree_root *root;
605 const char *keys;
606 int recoffset;
607
608 leaf = grub_malloc (node->data->bsize);
609 if (leaf == 0)
610 return 0;
611
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;
617 else
618 recoffset = (grub_xfs_inode_size(node->data)
619 - ((char *) keys - (char *) &node->inode))
620 / (2 * sizeof (grub_uint64_t));
621 do
622 {
623 grub_uint64_t i;
624 grub_addr_t keys_end, data_end;
625
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) ||
629 keys_end > data_end)
630 {
631 grub_error (GRUB_ERR_BAD_FS, "invalid number of XFS root keys");
632 grub_free (leaf);
633 return 0;
634 }
635
636 for (i = 0; i < nrec; i++)
637 {
638 if (fileblock < get_fsb(keys, i))
639 break;
640 }
641
642 /* Sparse block. */
643 if (i == 0)
644 {
645 grub_free (leaf);
646 return 0;
647 }
648
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))
652 {
653 grub_free (leaf);
654 return 0;
655 }
656
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)))
661 {
662 grub_free (leaf);
663 grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node");
664 return 0;
665 }
666
667 nrec = grub_be_to_cpu16 (leaf->numrecs);
668 keys = grub_xfs_btree_keys(node->data, leaf);
669 recoffset = ((node->data->bsize - ((char *) keys
670 - (char *) leaf))
671 / (2 * sizeof (grub_uint64_t)));
672 }
673 while (leaf->level);
674 exts = (struct grub_xfs_extent *) keys;
675 }
676 else if (node->inode.format == XFS_INODE_FORMAT_EXT)
677 {
678 grub_addr_t exts_end = 0;
679 grub_addr_t data_end = 0;
680
681 nrec = grub_xfs_get_inode_nextents (&node->inode);
682 exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode);
683
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) ||
687 exts_end > data_end)
688 {
689 grub_error (GRUB_ERR_BAD_FS, "invalid number of XFS extents");
690 return 0;
691 }
692 }
693 else
694 {
695 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
696 "XFS does not support inode format %d yet",
697 node->inode.format);
698 return 0;
699 }
700
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++)
704 {
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);
708
709 /* Sparse block. */
710 if (fileblock < offset)
711 break;
712 else if (fileblock < offset + size)
713 {
714 ret = (fileblock - offset + start);
715 break;
716 }
717 }
718
719 grub_free (leaf);
720
721 return GRUB_XFS_FSB_TO_BLOCK(node->data, ret);
722 }
723
724
725 /* Read LEN bytes from the file described by DATA starting with byte
726 POS. Return the amount of read bytes in READ. */
727 static grub_ssize_t
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)
731 {
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);
738 }
739
740
741 static char *
742 grub_xfs_read_symlink (grub_fshelp_node_t node)
743 {
744 grub_ssize_t size = grub_be_to_cpu64 (node->inode.size);
745 grub_size_t sz;
746
747 if (size < 0)
748 {
749 grub_error (GRUB_ERR_BAD_FS, "invalid symlink");
750 return 0;
751 }
752
753 switch (node->inode.format)
754 {
755 case XFS_INODE_FORMAT_INO:
756 return grub_strndup (grub_xfs_inode_data(&node->inode), size);
757
758 case XFS_INODE_FORMAT_EXT:
759 {
760 char *symlink;
761 grub_ssize_t numread;
762 int off = 0;
763
764 if (node->data->hascrc)
765 off = 56;
766
767 if (grub_add (size, 1, &sz))
768 {
769 grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow"));
770 return 0;
771 }
772 symlink = grub_malloc (sz);
773 if (!symlink)
774 return 0;
775
776 node->inode.size = grub_be_to_cpu64 (size + off);
777 numread = grub_xfs_read_file (node, 0, 0, off, size, symlink, off);
778 if (numread != size)
779 {
780 grub_free (symlink);
781 return 0;
782 }
783 symlink[size] = '\0';
784 return symlink;
785 }
786 }
787
788 return 0;
789 }
790
791
792 static enum grub_fshelp_filetype
793 grub_xfs_mode_to_filetype (grub_uint16_t mode)
794 {
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;
805 }
806
807
808 /* Context for grub_xfs_iterate_dir. */
809 struct grub_xfs_iterate_dir_ctx
810 {
811 grub_fshelp_iterate_dir_hook_t hook;
812 void *hook_data;
813 struct grub_fshelp_node *diro;
814 };
815
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)
819 {
820 struct grub_fshelp_node *fdiro;
821 grub_err_t err;
822 grub_size_t sz;
823
824 if (grub_add (grub_xfs_fshelp_size(ctx->diro->data), 1, &sz))
825 {
826 grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory data size overflow"));
827 grub_print_error ();
828 return 0;
829 }
830 fdiro = grub_malloc (sz);
831 if (!fdiro)
832 {
833 grub_print_error ();
834 return 0;
835 }
836
837 /* The inode should be read, otherwise the filetype can
838 not be determined. */
839 fdiro->ino = ino;
840 fdiro->inode_read = 1;
841 fdiro->data = ctx->diro->data;
842 err = grub_xfs_read_inode (ctx->diro->data, ino, &fdiro->inode);
843 if (err)
844 {
845 grub_print_error ();
846 grub_free (fdiro);
847 return 0;
848 }
849
850 return ctx->hook (filename, grub_xfs_mode_to_filetype (fdiro->inode.mode),
851 fdiro, ctx->hook_data);
852 }
853
854 static int
855 grub_xfs_iterate_dir (grub_fshelp_node_t dir,
856 grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
857 {
858 struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
859 struct grub_xfs_iterate_dir_ctx ctx = {
860 .hook = hook,
861 .hook_data = hook_data,
862 .diro = diro
863 };
864
865 switch (diro->inode.format)
866 {
867 case XFS_INODE_FORMAT_INO:
868 {
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;
872 int i;
873 grub_uint64_t parent;
874
875 /* If small inode numbers are used to pack the direntry, the
876 parent inode number is small too. */
877 if (smallino)
878 parent = grub_be_to_cpu32 (head->parent.i4);
879 else
880 parent = grub_be_to_cpu64 (head->parent.i8);
881
882 /* Synthesize the direntries for `.' and `..'. */
883 if (iterate_dir_call_hook (diro->ino, ".", &ctx))
884 return 1;
885
886 if (iterate_dir_call_hook (parent, "..", &ctx))
887 return 1;
888
889 for (i = 0; i < head->count &&
890 (grub_uint8_t *) de < ((grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)); i++)
891 {
892 grub_uint64_t ino;
893 grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de);
894 grub_uint8_t c;
895
896 if ((inopos + (smallino ? 4 : 8)) > (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data))
897 {
898 grub_error (GRUB_ERR_BAD_FS, "invalid XFS inode");
899 return 0;
900 }
901
902
903 /* inopos might be unaligned. */
904 if (smallino)
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);
909 else
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);
918
919 c = de->name[de->len];
920 de->name[de->len] = '\0';
921 if (iterate_dir_call_hook (ino, de->name, &ctx))
922 {
923 de->name[de->len] = c;
924 return 1;
925 }
926 de->name[de->len] = c;
927
928 de = grub_xfs_inline_next_de(dir->data, head, de);
929 }
930 break;
931 }
932
933 case XFS_INODE_FORMAT_BTREE:
934 case XFS_INODE_FORMAT_EXT:
935 {
936 grub_ssize_t numread;
937 char *dirblock;
938 grub_uint64_t blk;
939 int dirblk_size, dirblk_log2;
940
941 dirblk_log2 = (dir->data->sblock.log2_bsize
942 + dir->data->sblock.log2_dirblk);
943 dirblk_size = 1 << dirblk_log2;
944
945 dirblock = grub_malloc (dirblk_size);
946 if (! dirblock)
947 return 0;
948
949 /* Iterate over every block the directory has. */
950 for (blk = 0;
951 blk < (grub_be_to_cpu64 (dir->inode.size)
952 >> dirblk_log2);
953 blk++)
954 {
955 struct grub_xfs_dir2_entry *direntry =
956 grub_xfs_first_de(dir->data, dirblock);
957 int entries = -1;
958 char *end = dirblock + dirblk_size;
959 grub_uint32_t magic;
960
961 numread = grub_xfs_read_file (dir, 0, 0,
962 blk << dirblk_log2,
963 dirblk_size, dirblock, 0);
964 if (numread != dirblk_size)
965 {
966 grub_free (dirblock);
967 return 0;
968 }
969
970 /*
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.
974 */
975 magic = *(grub_uint32_t *)(void *) dirblock;
976 if (!magic)
977 continue;
978
979 /*
980 * Leaf and tail information are only in the data block if the number
981 * of extents is 1.
982 */
983 if (grub_xfs_get_inode_nextents (&dir->inode) == 1)
984 {
985 struct grub_xfs_dirblock_tail *tail = grub_xfs_dir_tail (dir->data, dirblock);
986
987 end = (char *) tail;
988
989 /* Subtract the space used by leaf nodes. */
990 end -= grub_be_to_cpu32 (tail->leaf_count) * sizeof (struct grub_xfs_dir_leaf_entry);
991
992 entries = grub_be_to_cpu32 (tail->leaf_count) - grub_be_to_cpu32 (tail->leaf_stale);
993
994 if (!entries)
995 continue;
996 }
997
998 /* Iterate over all entries within this block. */
999 while ((char *) direntry < (char *) end)
1000 {
1001 grub_uint8_t *freetag;
1002 char *filename;
1003
1004 freetag = (grub_uint8_t *) direntry;
1005
1006 if (grub_get_unaligned16 (freetag) == 0XFFFF)
1007 {
1008 grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t));
1009
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)));
1014
1015 continue;
1016 }
1017
1018 filename = (char *)(direntry + 1);
1019 if (filename + direntry->len + 1 > (char *) end)
1020 {
1021 grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry");
1022 return 0;
1023 }
1024
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';
1028
1029 if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode),
1030 filename, &ctx))
1031 {
1032 grub_free (dirblock);
1033 return 1;
1034 }
1035
1036 /*
1037 * The expected number of directory entries is only tracked for the
1038 * single extent case.
1039 */
1040 if (grub_xfs_get_inode_nextents (&dir->inode) == 1)
1041 {
1042 /* Check if last direntry in this block is reached. */
1043 entries--;
1044 if (!entries)
1045 break;
1046 }
1047
1048 /* Select the next directory entry. */
1049 direntry = grub_xfs_next_de(dir->data, direntry);
1050 }
1051 }
1052 grub_free (dirblock);
1053 break;
1054 }
1055
1056 default:
1057 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1058 "XFS does not support inode format %d yet",
1059 diro->inode.format);
1060 }
1061 return 0;
1062 }
1063
1064
1065 static struct grub_xfs_data *
1066 grub_xfs_mount (grub_disk_t disk)
1067 {
1068 struct grub_xfs_data *data = 0;
1069 grub_size_t sz;
1070
1071 data = grub_zalloc (sizeof (struct grub_xfs_data));
1072 if (!data)
1073 return 0;
1074
1075 data->data_size = sizeof (struct grub_xfs_data);
1076
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))
1081 goto fail;
1082
1083 if (!grub_xfs_sb_valid(data))
1084 goto fail;
1085
1086 if (grub_xfs_sb_needs_repair (data))
1087 grub_dprintf ("xfs", "XFS filesystem needs repair, boot may fail\n");
1088
1089 if (grub_add (grub_xfs_inode_size (data),
1090 sizeof (struct grub_xfs_data) - sizeof (struct grub_xfs_inode) + 1, &sz))
1091 goto fail;
1092
1093 data = grub_realloc (data, sz);
1094
1095 if (! data)
1096 goto fail;
1097
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);
1106
1107 data->disk = disk;
1108 data->pos = 0;
1109 grub_dprintf("xfs", "Reading root ino %" PRIuGRUB_UINT64_T "\n",
1110 grub_cpu_to_be64(data->sblock.rootino));
1111
1112 grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode);
1113
1114 return data;
1115 fail:
1116
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");
1119
1120 grub_free (data);
1121
1122 return 0;
1123 }
1124
1125 /* Context for grub_xfs_dir. */
1126 struct grub_xfs_dir_ctx
1127 {
1128 grub_fs_dir_hook_t hook;
1129 void *hook_data;
1130 };
1131
1132 /* Bigtime inodes helpers. */
1133 #define XFS_BIGTIME_EPOCH_OFFSET (-(grub_int64_t) GRUB_INT32_MIN)
1134
1135 static int grub_xfs_inode_has_bigtime (const struct grub_xfs_inode *inode)
1136 {
1137 return inode->version >= 3 &&
1138 (inode->flags2 & grub_cpu_to_be64_compile_time (XFS_DIFLAG2_BIGTIME));
1139 }
1140
1141 static grub_int64_t
1142 grub_xfs_get_inode_time (struct grub_xfs_inode *inode)
1143 {
1144 struct grub_xfs_time_legacy *lts;
1145
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;
1148
1149 lts = (struct grub_xfs_time_legacy *) &inode->mtime;
1150 return grub_be_to_cpu32 (lts->sec);
1151 }
1152
1153 /* Helper for grub_xfs_dir. */
1154 static int
1155 grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
1156 grub_fshelp_node_t node, void *data)
1157 {
1158 struct grub_xfs_dir_ctx *ctx = data;
1159 struct grub_dirhook_info info;
1160
1161 grub_memset (&info, 0, sizeof (info));
1162 if (node->inode_read)
1163 {
1164 info.mtimeset = 1;
1165 info.mtime = grub_xfs_get_inode_time (&node->inode);
1166 }
1167 info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
1168 grub_free (node);
1169 return ctx->hook (filename, &info, ctx->hook_data);
1170 }
1171
1172 static grub_err_t
1173 grub_xfs_dir (grub_device_t device, const char *path,
1174 grub_fs_dir_hook_t hook, void *hook_data)
1175 {
1176 struct grub_xfs_dir_ctx ctx = { hook, hook_data };
1177 struct grub_xfs_data *data = 0;
1178 struct grub_fshelp_node *fdiro = 0;
1179
1180 grub_dl_ref (my_mod);
1181
1182 data = grub_xfs_mount (device->disk);
1183 if (!data)
1184 goto mount_fail;
1185
1186 grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir,
1187 grub_xfs_read_symlink, GRUB_FSHELP_DIR);
1188 if (grub_errno)
1189 goto fail;
1190
1191 grub_xfs_iterate_dir (fdiro, grub_xfs_dir_iter, &ctx);
1192
1193 fail:
1194 if (fdiro != &data->diropen)
1195 grub_free (fdiro);
1196 grub_free (data);
1197
1198 mount_fail:
1199
1200 grub_dl_unref (my_mod);
1201
1202 return grub_errno;
1203 }
1204
1205
1206 /* Open a file named NAME and initialize FILE. */
1207 static grub_err_t
1208 grub_xfs_open (struct grub_file *file, const char *name)
1209 {
1210 struct grub_xfs_data *data;
1211 struct grub_fshelp_node *fdiro = 0;
1212
1213 grub_dl_ref (my_mod);
1214
1215 data = grub_xfs_mount (file->device->disk);
1216 if (!data)
1217 goto mount_fail;
1218
1219 grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir,
1220 grub_xfs_read_symlink, GRUB_FSHELP_REG);
1221 if (grub_errno)
1222 goto fail;
1223
1224 if (!fdiro->inode_read)
1225 {
1226 grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode);
1227 if (grub_errno)
1228 goto fail;
1229 }
1230
1231 if (fdiro != &data->diropen)
1232 {
1233 grub_memcpy (&data->diropen, fdiro, grub_xfs_fshelp_size(data));
1234 grub_free (fdiro);
1235 }
1236
1237 file->size = grub_be_to_cpu64 (data->diropen.inode.size);
1238 file->data = data;
1239 file->offset = 0;
1240
1241 return 0;
1242
1243 fail:
1244 if (fdiro != &data->diropen)
1245 grub_free (fdiro);
1246 grub_free (data);
1247
1248 mount_fail:
1249 grub_dl_unref (my_mod);
1250
1251 return grub_errno;
1252 }
1253
1254
1255 static grub_ssize_t
1256 grub_xfs_read (grub_file_t file, char *buf, grub_size_t len)
1257 {
1258 struct grub_xfs_data *data =
1259 (struct grub_xfs_data *) file->data;
1260
1261 return grub_xfs_read_file (&data->diropen,
1262 file->read_hook, file->read_hook_data,
1263 file->offset, len, buf, 0);
1264 }
1265
1266
1267 static grub_err_t
1268 grub_xfs_close (grub_file_t file)
1269 {
1270 grub_free (file->data);
1271
1272 grub_dl_unref (my_mod);
1273
1274 return GRUB_ERR_NONE;
1275 }
1276
1277
1278 static grub_err_t
1279 grub_xfs_label (grub_device_t device, char **label)
1280 {
1281 struct grub_xfs_data *data;
1282 grub_disk_t disk = device->disk;
1283
1284 grub_dl_ref (my_mod);
1285
1286 data = grub_xfs_mount (disk);
1287 if (data)
1288 *label = grub_strndup ((char *) (data->sblock.label), 12);
1289 else
1290 *label = 0;
1291
1292 grub_dl_unref (my_mod);
1293
1294 grub_free (data);
1295
1296 return grub_errno;
1297 }
1298
1299 static grub_err_t
1300 grub_xfs_uuid (grub_device_t device, char **uuid)
1301 {
1302 struct grub_xfs_data *data;
1303 grub_disk_t disk = device->disk;
1304
1305 grub_dl_ref (my_mod);
1306
1307 data = grub_xfs_mount (disk);
1308 if (data)
1309 {
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]));
1319 }
1320 else
1321 *uuid = NULL;
1322
1323 grub_dl_unref (my_mod);
1324
1325 grub_free (data);
1326
1327 return grub_errno;
1328 }
1329
1330 static struct grub_fs grub_xfs_fs =
1331 {
1332 .name = "xfs",
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,
1339 #ifdef GRUB_UTIL
1340 .reserved_first_sector = 0,
1341 .blocklist_install = 1,
1342 #endif
1343 .next = 0
1344 };
1345
1346 GRUB_MOD_INIT(xfs)
1347 {
1348 //grub_xfs_fs.mod = mod;
1349 grub_fs_register (&grub_xfs_fs);
1350 my_mod = mod;
1351 }
1352
1353 GRUB_MOD_FINI(xfs)
1354 {
1355 grub_fs_unregister (&grub_xfs_fs);
1356 }