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/types.h>
27 #include <grub/fshelp.h>
29 GRUB_MOD_LICENSE ("GPLv3+");
31 #define XFS_INODE_EXTENTS 9
33 #define XFS_INODE_FORMAT_INO 1
34 #define XFS_INODE_FORMAT_EXT 2
35 #define XFS_INODE_FORMAT_BTREE 3
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)
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 */
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)
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 */
83 * Directory entries with ftype are explicitly handled by GRUB code.
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.
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.
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)
96 struct grub_xfs_sblock
98 grub_uint8_t magic
[4];
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
;
126 struct grub_xfs_dir_header
129 grub_uint8_t largeino
;
134 } GRUB_PACKED parent
;
137 /* Structure for directory entry inlined in the inode */
138 struct grub_xfs_dir_entry
141 grub_uint16_t offset
;
143 /* Inode number follows, 32 / 64 bits. */
146 /* Structure for directory entry in a block */
147 struct grub_xfs_dir2_entry
153 struct grub_xfs_extent
155 /* This should be a bitfield but bietfields are unportable, so just have
156 a raw array and functions extracting useful info from it.
158 grub_uint32_t raw
[4];
161 struct grub_xfs_btree_node
163 grub_uint8_t magic
[4];
165 grub_uint16_t numrecs
;
168 /* In V5 here follow crc, uuid, etc. */
169 /* Then follow keys and block pointers */
172 struct grub_xfs_btree_root
175 grub_uint16_t numrecs
;
176 grub_uint64_t keys
[1];
182 grub_uint32_t nanosec
;
185 struct grub_xfs_inode
187 grub_uint8_t magic
[2];
189 grub_uint8_t version
;
191 grub_uint8_t unused2
[26];
192 struct grub_xfs_time atime
;
193 struct grub_xfs_time mtime
;
194 struct grub_xfs_time ctime
;
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];
204 #define XFS_V2_INODE_SIZE sizeof(struct grub_xfs_inode)
205 #define XFS_V3_INODE_SIZE (XFS_V2_INODE_SIZE + 76)
207 struct grub_xfs_dirblock_tail
209 grub_uint32_t leaf_count
;
210 grub_uint32_t leaf_stale
;
213 struct grub_fshelp_node
215 struct grub_xfs_data
*data
;
218 struct grub_xfs_inode inode
;
223 struct grub_xfs_sblock sblock
;
227 grub_uint32_t agsize
;
228 unsigned int hasftype
:1;
229 unsigned int hascrc
:1;
230 struct grub_fshelp_node diropen
;
233 static grub_dl_t my_mod
;
237 static int grub_xfs_sb_hascrc(struct grub_xfs_data
*data
)
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
);
243 static int grub_xfs_sb_hasftype(struct grub_xfs_data
*data
)
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
))
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
))
255 static int grub_xfs_sb_valid(struct grub_xfs_data
*data
)
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)
263 grub_error (GRUB_ERR_BAD_FS
, "not a XFS filesystem");
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
))
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
))
273 grub_error (GRUB_ERR_BAD_FS
, "XFS filesystem has unsupported "
274 "incompatible features");
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
))
282 grub_dprintf("xfs", "XFS v4 superblock detected\n");
283 if (!(data
->sblock
.version
& grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT
)))
285 grub_error (GRUB_ERR_BAD_FS
, "XFS filesystem without V2 directories "
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
)))
293 grub_error (GRUB_ERR_BAD_FS
, "XFS filesystem has unsupported version "
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
309 GRUB_XFS_INO_AGBITS(struct grub_xfs_data
*data
)
311 return ((data
)->sblock
.log2_agblk
+ (data
)->sblock
.log2_inop
);
314 static inline grub_uint64_t
315 GRUB_XFS_INO_INOINAG (struct grub_xfs_data
*data
,
318 return (ino
& ((1LL << GRUB_XFS_INO_AGBITS (data
)) - 1));
321 static inline grub_uint64_t
322 GRUB_XFS_INO_AG (struct grub_xfs_data
*data
,
325 return (ino
>> GRUB_XFS_INO_AGBITS (data
));
328 static inline grub_disk_addr_t
329 GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data
*data
, grub_disk_addr_t fsb
)
331 return ((fsb
>> data
->sblock
.log2_agblk
) * data
->agsize
332 + (fsb
& ((1LL << data
->sblock
.log2_agblk
) - 1)));
335 static inline grub_uint64_t
336 GRUB_XFS_EXTENT_OFFSET (struct grub_xfs_extent
*exts
, int ex
)
338 return ((grub_be_to_cpu32 (exts
[ex
].raw
[0]) & ~(1 << 31)) << 23
339 | grub_be_to_cpu32 (exts
[ex
].raw
[1]) >> 9);
342 static inline grub_uint64_t
343 GRUB_XFS_EXTENT_BLOCK (struct grub_xfs_extent
*exts
, int ex
)
345 return ((grub_uint64_t
) (grub_be_to_cpu32 (exts
[ex
].raw
[1])
347 | (grub_uint64_t
) grub_be_to_cpu32 (exts
[ex
].raw
[2]) << 11
348 | grub_be_to_cpu32 (exts
[ex
].raw
[3]) >> 21);
351 static inline grub_uint64_t
352 GRUB_XFS_EXTENT_SIZE (struct grub_xfs_extent
*exts
, int ex
)
354 return (grub_be_to_cpu32 (exts
[ex
].raw
[3]) & ((1 << 21) - 1));
358 static inline grub_uint64_t
359 grub_xfs_inode_block (struct grub_xfs_data
*data
,
362 long long int inoinag
= GRUB_XFS_INO_INOINAG (data
, ino
);
363 long long ag
= GRUB_XFS_INO_AG (data
, ino
);
366 block
= (inoinag
>> data
->sblock
.log2_inop
) + ag
* data
->agsize
;
367 block
<<= (data
->sblock
.log2_bsize
- GRUB_DISK_SECTOR_BITS
);
373 grub_xfs_inode_offset (struct grub_xfs_data
*data
,
376 int inoag
= GRUB_XFS_INO_INOINAG (data
, ino
);
377 return ((inoag
& ((1 << data
->sblock
.log2_inop
) - 1)) <<
378 data
->sblock
.log2_inode
);
381 static inline grub_size_t
382 grub_xfs_inode_size(struct grub_xfs_data
*data
)
384 return (grub_size_t
)1 << data
->sblock
.log2_inode
;
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.
393 static inline grub_size_t
394 grub_xfs_fshelp_size(struct grub_xfs_data
*data
)
396 return sizeof (struct grub_fshelp_node
) - sizeof (struct grub_xfs_inode
)
397 + grub_xfs_inode_size(data
);
400 /* This should return void * but XFS code is error-prone with alignment, so
401 return char to retain cast-align.
404 grub_xfs_inode_data(struct grub_xfs_inode
*inode
)
406 if (inode
->version
<= 2)
407 return ((char *)inode
) + XFS_V2_INODE_SIZE
;
408 return ((char *)inode
) + XFS_V3_INODE_SIZE
;
411 static struct grub_xfs_dir_entry
*
412 grub_xfs_inline_de(struct grub_xfs_dir_header
*head
)
415 With small inode numbers the header is 4 bytes smaller because of
416 smaller parent pointer
418 return (struct grub_xfs_dir_entry
*)
419 (((char *) head
) + sizeof(struct grub_xfs_dir_header
) -
420 (head
->largeino
? 0 : sizeof(grub_uint32_t
)));
423 static grub_uint8_t
*
424 grub_xfs_inline_de_inopos(struct grub_xfs_data
*data
,
425 struct grub_xfs_dir_entry
*de
)
427 return ((grub_uint8_t
*)(de
+ 1)) + de
->len
- 1 + (data
->hasftype
? 1 : 0);
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
)
435 char *p
= (char *)de
+ sizeof(struct grub_xfs_dir_entry
) - 1 + de
->len
;
437 p
+= head
->largeino
? sizeof(grub_uint64_t
) : sizeof(grub_uint32_t
);
441 return (struct grub_xfs_dir_entry
*)p
;
444 static struct grub_xfs_dirblock_tail
*
445 grub_xfs_dir_tail(struct grub_xfs_data
*data
, void *dirblock
)
447 int dirblksize
= 1 << (data
->sblock
.log2_bsize
+ data
->sblock
.log2_dirblk
);
449 return (struct grub_xfs_dirblock_tail
*)
450 ((char *)dirblock
+ dirblksize
- sizeof (struct grub_xfs_dirblock_tail
));
453 static struct grub_xfs_dir2_entry
*
454 grub_xfs_first_de(struct grub_xfs_data
*data
, void *dirblock
)
457 return (struct grub_xfs_dir2_entry
*)((char *)dirblock
+ 64);
458 return (struct grub_xfs_dir2_entry
*)((char *)dirblock
+ 16);
461 static struct grub_xfs_dir2_entry
*
462 grub_xfs_next_de(struct grub_xfs_data
*data
, struct grub_xfs_dir2_entry
*de
)
464 int size
= sizeof (struct grub_xfs_dir2_entry
) + de
->len
+ 2 /* Tag */;
467 size
++; /* File type */
468 return (struct grub_xfs_dir2_entry
*)(((char *)de
) + ALIGN_UP(size
, 8));
471 /* This should return void * but XFS code is error-prone with alignment, so
472 return char to retain cast-align.
475 grub_xfs_btree_keys(struct grub_xfs_data
*data
,
476 struct grub_xfs_btree_node
*leaf
)
478 char *keys
= (char *)(leaf
+ 1);
481 keys
+= 48; /* skip crc, uuid, ... */
486 grub_xfs_read_inode (struct grub_xfs_data
*data
, grub_uint64_t ino
,
487 struct grub_xfs_inode
*inode
)
489 grub_uint64_t block
= grub_xfs_inode_block (data
, ino
);
490 int offset
= grub_xfs_inode_offset (data
, ino
);
492 grub_dprintf("xfs", "Reading inode (%"PRIuGRUB_UINT64_T
") - %"PRIuGRUB_UINT64_T
", %d\n",
494 /* Read the inode. */
495 if (grub_disk_read (data
->disk
, block
, offset
, grub_xfs_inode_size(data
),
499 if (grub_strncmp ((char *) inode
->magic
, "IN", 2))
500 return grub_error (GRUB_ERR_BAD_FS
, "not a correct XFS inode");
506 get_fsb (const void *keys
, int idx
)
508 const char *p
= (const char *) keys
+ sizeof(grub_uint64_t
) * idx
;
509 return grub_be_to_cpu64 (grub_get_unaligned64 (p
));
512 static grub_disk_addr_t
513 grub_xfs_read_block (grub_fshelp_node_t node
, grub_disk_addr_t fileblock
)
515 struct grub_xfs_btree_node
*leaf
= 0;
517 struct grub_xfs_extent
*exts
;
518 grub_uint64_t ret
= 0;
520 if (node
->inode
.format
== XFS_INODE_FORMAT_BTREE
)
522 struct grub_xfs_btree_root
*root
;
526 leaf
= grub_malloc (node
->data
->bsize
);
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;
536 recoffset
= (grub_xfs_inode_size(node
->data
)
537 - ((char *) keys
- (char *) &node
->inode
))
538 / (2 * sizeof (grub_uint64_t
));
543 for (i
= 0; i
< nrec
; i
++)
545 if (fileblock
< get_fsb(keys
, i
))
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
))
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)))
567 grub_error (GRUB_ERR_BAD_FS
, "not a correct XFS BMAP node");
571 nrec
= grub_be_to_cpu16 (leaf
->numrecs
);
572 keys
= grub_xfs_btree_keys(node
->data
, leaf
);
573 recoffset
= ((node
->data
->bsize
- ((char *) keys
575 / (2 * sizeof (grub_uint64_t
)));
578 exts
= (struct grub_xfs_extent
*) keys
;
580 else if (node
->inode
.format
== XFS_INODE_FORMAT_EXT
)
582 nrec
= grub_be_to_cpu32 (node
->inode
.nextents
);
583 exts
= (struct grub_xfs_extent
*) grub_xfs_inode_data(&node
->inode
);
587 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
588 "XFS does not support inode format %d yet",
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
++)
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
);
602 if (fileblock
< offset
)
604 else if (fileblock
< offset
+ size
)
606 ret
= (fileblock
- offset
+ start
);
613 return GRUB_XFS_FSB_TO_BLOCK(node
->data
, ret
);
617 /* Read LEN bytes from the file described by DATA starting with byte
618 POS. Return the amount of read bytes in READ. */
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
)
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);
634 grub_xfs_read_symlink (grub_fshelp_node_t node
)
636 grub_ssize_t size
= grub_be_to_cpu64 (node
->inode
.size
);
640 grub_error (GRUB_ERR_BAD_FS
, "invalid symlink");
644 switch (node
->inode
.format
)
646 case XFS_INODE_FORMAT_INO
:
647 return grub_strndup (grub_xfs_inode_data(&node
->inode
), size
);
649 case XFS_INODE_FORMAT_EXT
:
652 grub_ssize_t numread
;
655 if (node
->data
->hascrc
)
658 symlink
= grub_malloc (size
+ 1);
662 node
->inode
.size
= grub_be_to_cpu64 (size
+ off
);
663 numread
= grub_xfs_read_file (node
, 0, 0, off
, size
, symlink
, off
);
669 symlink
[size
] = '\0';
678 static enum grub_fshelp_filetype
679 grub_xfs_mode_to_filetype (grub_uint16_t mode
)
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
;
694 /* Context for grub_xfs_iterate_dir. */
695 struct grub_xfs_iterate_dir_ctx
697 grub_fshelp_iterate_dir_hook_t hook
;
699 struct grub_fshelp_node
*diro
;
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
)
706 struct grub_fshelp_node
*fdiro
;
709 fdiro
= grub_malloc (grub_xfs_fshelp_size(ctx
->diro
->data
) + 1);
716 /* The inode should be read, otherwise the filetype can
717 not be determined. */
719 fdiro
->inode_read
= 1;
720 fdiro
->data
= ctx
->diro
->data
;
721 err
= grub_xfs_read_inode (ctx
->diro
->data
, ino
, &fdiro
->inode
);
728 return ctx
->hook (filename
, grub_xfs_mode_to_filetype (fdiro
->inode
.mode
),
729 fdiro
, ctx
->hook_data
);
733 grub_xfs_iterate_dir (grub_fshelp_node_t dir
,
734 grub_fshelp_iterate_dir_hook_t hook
, void *hook_data
)
736 struct grub_fshelp_node
*diro
= (struct grub_fshelp_node
*) dir
;
737 struct grub_xfs_iterate_dir_ctx ctx
= {
739 .hook_data
= hook_data
,
743 switch (diro
->inode
.format
)
745 case XFS_INODE_FORMAT_INO
:
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
;
751 grub_uint64_t parent
;
753 /* If small inode numbers are used to pack the direntry, the
754 parent inode number is small too. */
756 parent
= grub_be_to_cpu32 (head
->parent
.i4
);
758 parent
= grub_be_to_cpu64 (head
->parent
.i8
);
760 /* Synthesize the direntries for `.' and `..'. */
761 if (iterate_dir_call_hook (diro
->ino
, ".", &ctx
))
764 if (iterate_dir_call_hook (parent
, "..", &ctx
))
767 for (i
= 0; i
< head
->count
; i
++)
770 grub_uint8_t
*inopos
= grub_xfs_inline_de_inopos(dir
->data
, de
);
773 /* inopos might be unaligned. */
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);
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);
789 c
= de
->name
[de
->len
];
790 de
->name
[de
->len
] = '\0';
791 if (iterate_dir_call_hook (ino
, de
->name
, &ctx
))
793 de
->name
[de
->len
] = c
;
796 de
->name
[de
->len
] = c
;
798 de
= grub_xfs_inline_next_de(dir
->data
, head
, de
);
803 case XFS_INODE_FORMAT_BTREE
:
804 case XFS_INODE_FORMAT_EXT
:
806 grub_ssize_t numread
;
809 int dirblk_size
, dirblk_log2
;
811 dirblk_log2
= (dir
->data
->sblock
.log2_bsize
812 + dir
->data
->sblock
.log2_dirblk
);
813 dirblk_size
= 1 << dirblk_log2
;
815 dirblock
= grub_malloc (dirblk_size
);
819 /* Iterate over every block the directory has. */
821 blk
< (grub_be_to_cpu64 (dir
->inode
.size
)
825 struct grub_xfs_dir2_entry
*direntry
=
826 grub_xfs_first_de(dir
->data
, dirblock
);
828 struct grub_xfs_dirblock_tail
*tail
=
829 grub_xfs_dir_tail(dir
->data
, dirblock
);
831 numread
= grub_xfs_read_file (dir
, 0, 0,
833 dirblk_size
, dirblock
, 0);
834 if (numread
!= dirblk_size
)
837 entries
= (grub_be_to_cpu32 (tail
->leaf_count
)
838 - grub_be_to_cpu32 (tail
->leaf_stale
));
843 /* Iterate over all entries within this block. */
844 while ((char *)direntry
< (char *)tail
)
846 grub_uint8_t
*freetag
;
849 freetag
= (grub_uint8_t
*) direntry
;
851 if (grub_get_unaligned16 (freetag
) == 0XFFFF)
853 grub_uint8_t
*skip
= (freetag
+ sizeof (grub_uint16_t
));
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
)));
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';
868 if (iterate_dir_call_hook (grub_be_to_cpu64(direntry
->inode
),
871 grub_free (dirblock
);
875 /* Check if last direntry in this block is
881 /* Select the next directory entry. */
882 direntry
= grub_xfs_next_de(dir
->data
, direntry
);
885 grub_free (dirblock
);
890 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
891 "XFS does not support inode format %d yet",
898 static struct grub_xfs_data
*
899 grub_xfs_mount (grub_disk_t disk
)
901 struct grub_xfs_data
*data
= 0;
903 data
= grub_zalloc (sizeof (struct grub_xfs_data
));
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
))
913 if (!grub_xfs_sb_valid(data
))
916 data
= grub_realloc (data
,
917 sizeof (struct grub_xfs_data
)
918 - sizeof (struct grub_xfs_inode
)
919 + grub_xfs_inode_size(data
) + 1);
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
);
934 grub_dprintf("xfs", "Reading root ino %"PRIuGRUB_UINT64_T
"\n",
935 grub_cpu_to_be64(data
->sblock
.rootino
));
937 grub_xfs_read_inode (data
, data
->diropen
.ino
, &data
->diropen
.inode
);
942 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
943 grub_error (GRUB_ERR_BAD_FS
, "not an XFS filesystem");
951 /* Context for grub_xfs_dir. */
952 struct grub_xfs_dir_ctx
954 grub_fs_dir_hook_t hook
;
958 /* Helper for grub_xfs_dir. */
960 grub_xfs_dir_iter (const char *filename
, enum grub_fshelp_filetype filetype
,
961 grub_fshelp_node_t node
, void *data
)
963 struct grub_xfs_dir_ctx
*ctx
= data
;
964 struct grub_dirhook_info info
;
966 grub_memset (&info
, 0, sizeof (info
));
967 if (node
->inode_read
)
970 info
.mtime
= grub_be_to_cpu32 (node
->inode
.mtime
.sec
);
972 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
974 info
.size
= node
->inode
.size
;
976 return ctx
->hook (filename
, &info
, ctx
->hook_data
);
980 grub_xfs_dir (grub_device_t device
, const char *path
,
981 grub_fs_dir_hook_t hook
, void *hook_data
)
983 struct grub_xfs_dir_ctx ctx
= { hook
, hook_data
};
984 struct grub_xfs_data
*data
= 0;
985 struct grub_fshelp_node
*fdiro
= 0;
987 grub_dl_ref (my_mod
);
989 data
= grub_xfs_mount (device
->disk
);
993 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_xfs_iterate_dir
,
994 grub_xfs_read_symlink
, GRUB_FSHELP_DIR
);
998 grub_xfs_iterate_dir (fdiro
, grub_xfs_dir_iter
, &ctx
);
1001 if (fdiro
!= &data
->diropen
)
1007 grub_dl_unref (my_mod
);
1013 /* Open a file named NAME and initialize FILE. */
1015 grub_xfs_open (struct grub_file
*file
, const char *name
)
1017 struct grub_xfs_data
*data
;
1018 struct grub_fshelp_node
*fdiro
= 0;
1020 grub_dl_ref (my_mod
);
1022 data
= grub_xfs_mount (file
->device
->disk
);
1026 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_xfs_iterate_dir
,
1027 grub_xfs_read_symlink
, GRUB_FSHELP_REG
);
1031 if (!fdiro
->inode_read
)
1033 grub_xfs_read_inode (data
, fdiro
->ino
, &fdiro
->inode
);
1038 if (fdiro
!= &data
->diropen
)
1040 grub_memcpy (&data
->diropen
, fdiro
, grub_xfs_fshelp_size(data
));
1044 file
->size
= grub_be_to_cpu64 (data
->diropen
.inode
.size
);
1051 if (fdiro
!= &data
->diropen
)
1056 grub_dl_unref (my_mod
);
1063 grub_xfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
1065 struct grub_xfs_data
*data
=
1066 (struct grub_xfs_data
*) file
->data
;
1068 return grub_xfs_read_file (&data
->diropen
,
1069 file
->read_hook
, file
->read_hook_data
,
1070 file
->offset
, len
, buf
, 0);
1075 grub_xfs_close (grub_file_t file
)
1077 grub_free (file
->data
);
1079 grub_dl_unref (my_mod
);
1081 return GRUB_ERR_NONE
;
1086 grub_xfs_label (grub_device_t device
, char **label
)
1088 struct grub_xfs_data
*data
;
1089 grub_disk_t disk
= device
->disk
;
1091 grub_dl_ref (my_mod
);
1093 data
= grub_xfs_mount (disk
);
1095 *label
= grub_strndup ((char *) (data
->sblock
.label
), 12);
1099 grub_dl_unref (my_mod
);
1107 grub_xfs_uuid (grub_device_t device
, char **uuid
)
1109 struct grub_xfs_data
*data
;
1110 grub_disk_t disk
= device
->disk
;
1112 grub_dl_ref (my_mod
);
1114 data
= grub_xfs_mount (disk
);
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]));
1130 grub_dl_unref (my_mod
);
1139 static struct grub_fs grub_xfs_fs
=
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
,
1149 .reserved_first_sector
= 0,
1150 .blocklist_install
= 1,
1157 grub_fs_register (&grub_xfs_fs
);
1163 grub_fs_unregister (&grub_xfs_fs
);