3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007,2008 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 #define XFS_INODE_EXTENTS 9
31 #define XFS_INODE_FORMAT_INO 1
32 #define XFS_INODE_FORMAT_EXT 2
33 #define XFS_INODE_FORMAT_BTREE 3
36 struct grub_xfs_sblock
38 grub_uint8_t magic
[4];
40 grub_uint8_t unused1
[24];
41 grub_uint16_t uuid
[8];
42 grub_uint8_t unused2
[8];
43 grub_uint64_t rootino
;
44 grub_uint8_t unused3
[20];
46 grub_uint8_t unused4
[20];
47 grub_uint8_t label
[12];
48 grub_uint8_t log2_bsize
;
49 grub_uint8_t log2_sect
;
50 grub_uint8_t log2_inode
;
51 grub_uint8_t log2_inop
;
52 grub_uint8_t log2_agblk
;
53 grub_uint8_t unused6
[67];
54 grub_uint8_t log2_dirblk
;
55 } __attribute__ ((packed
));
57 struct grub_xfs_dir_header
60 grub_uint8_t smallino
;
65 } parent
__attribute__ ((packed
));
66 } __attribute__ ((packed
));
68 struct grub_xfs_dir_entry
73 /* Inode number follows, 32 bits. */
74 } __attribute__ ((packed
));
76 struct grub_xfs_dir2_entry
80 } __attribute__ ((packed
));
82 typedef grub_uint32_t grub_xfs_extent
[4];
84 struct grub_xfs_btree_node
86 grub_uint8_t magic
[4];
88 grub_uint16_t numrecs
;
91 grub_uint64_t keys
[1];
92 } __attribute__ ((packed
));
94 struct grub_xfs_btree_root
97 grub_uint16_t numrecs
;
98 grub_uint64_t keys
[1];
99 } __attribute__ ((packed
));
101 struct grub_xfs_inode
103 grub_uint8_t magic
[2];
105 grub_uint8_t version
;
107 grub_uint8_t unused2
[50];
109 grub_uint64_t nblocks
;
110 grub_uint32_t extsize
;
111 grub_uint32_t nextents
;
112 grub_uint8_t unused3
[20];
118 struct grub_xfs_dir_header dirhead
;
119 struct grub_xfs_dir_entry direntry
[1];
121 grub_xfs_extent extents
[XFS_INODE_EXTENTS
];
122 struct grub_xfs_btree_root btree
;
123 } data
__attribute__ ((packed
));
124 } __attribute__ ((packed
));
126 struct grub_xfs_dirblock_tail
128 grub_uint32_t leaf_count
;
129 grub_uint32_t leaf_stale
;
130 } __attribute__ ((packed
));
132 struct grub_fshelp_node
134 struct grub_xfs_data
*data
;
137 struct grub_xfs_inode inode
;
142 struct grub_xfs_sblock sblock
;
147 struct grub_fshelp_node diropen
;
150 static grub_dl_t my_mod
;
154 /* Filetype information as used in inodes. */
155 #define FILETYPE_INO_MASK 0170000
156 #define FILETYPE_INO_REG 0100000
157 #define FILETYPE_INO_DIRECTORY 0040000
158 #define FILETYPE_INO_SYMLINK 0120000
160 #define GRUB_XFS_INO_AGBITS(data) \
161 ((data)->sblock.log2_agblk + (data)->sblock.log2_inop)
162 #define GRUB_XFS_INO_INOINAG(data, ino) \
163 (grub_be_to_cpu64 (ino) & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1))
164 #define GRUB_XFS_INO_AG(data,ino) \
165 (grub_be_to_cpu64 (ino) >> GRUB_XFS_INO_AGBITS (data))
167 #define GRUB_XFS_FSB_TO_BLOCK(data, fsb) \
168 (((fsb) >> (data)->sblock.log2_agblk) * (data)->agsize \
169 + ((fsb) & ((1LL << (data)->sblock.log2_agblk) - 1)))
171 #define GRUB_XFS_EXTENT_OFFSET(exts,ex) \
172 ((grub_be_to_cpu32 (exts[ex][0]) & ~(1 << 31)) << 23 \
173 | grub_be_to_cpu32 (exts[ex][1]) >> 9)
175 #define GRUB_XFS_EXTENT_BLOCK(exts,ex) \
176 ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex][1]) \
178 | (grub_uint64_t) grub_be_to_cpu32 (exts[ex][2]) << 11 \
179 | grub_be_to_cpu32 (exts[ex][3]) >> 21)
181 #define GRUB_XFS_EXTENT_SIZE(exts,ex) \
182 (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 20) - 1))
184 #define GRUB_XFS_ROUND_TO_DIRENT(pos) ((((pos) + 8 - 1) / 8) * 8)
185 #define GRUB_XFS_NEXT_DIRENT(pos,len) \
186 (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2)
188 static inline grub_uint64_t
189 grub_xfs_inode_block (struct grub_xfs_data
*data
,
192 long long int inoinag
= GRUB_XFS_INO_INOINAG (data
, ino
);
193 long long ag
= GRUB_XFS_INO_AG (data
, ino
);
196 block
= (inoinag
>> data
->sblock
.log2_inop
) + ag
* data
->agsize
;
197 block
<<= (data
->sblock
.log2_bsize
- GRUB_DISK_SECTOR_BITS
);
203 grub_xfs_inode_offset (struct grub_xfs_data
*data
,
206 int inoag
= GRUB_XFS_INO_INOINAG (data
, ino
);
207 return ((inoag
& ((1 << data
->sblock
.log2_inop
) - 1)) <<
208 data
->sblock
.log2_inode
);
213 grub_xfs_read_inode (struct grub_xfs_data
*data
, grub_uint64_t ino
,
214 struct grub_xfs_inode
*inode
)
216 grub_uint64_t block
= grub_xfs_inode_block (data
, ino
);
217 int offset
= grub_xfs_inode_offset (data
, ino
);
219 /* Read the inode. */
220 if (grub_disk_read (data
->disk
, block
, offset
,
221 1 << data
->sblock
.log2_inode
, inode
))
224 if (grub_strncmp ((char *) inode
->magic
, "IN", 2))
225 return grub_error (GRUB_ERR_BAD_FS
, "not a correct XFS inode.\n");
231 static grub_disk_addr_t
232 grub_xfs_read_block (grub_fshelp_node_t node
, grub_disk_addr_t fileblock
)
234 struct grub_xfs_btree_node
*leaf
= 0;
236 grub_xfs_extent
*exts
;
237 grub_uint64_t ret
= 0;
239 if (node
->inode
.format
== XFS_INODE_FORMAT_BTREE
)
243 leaf
= grub_malloc (node
->data
->sblock
.bsize
);
247 nrec
= grub_be_to_cpu16 (node
->inode
.data
.btree
.numrecs
);
248 keys
= &node
->inode
.data
.btree
.keys
[0];
253 for (i
= 0; i
< nrec
; i
++)
255 if (fileblock
< grub_be_to_cpu64 (keys
[i
]))
266 if (grub_disk_read (node
->data
->disk
,
267 grub_be_to_cpu64 (keys
[i
- 1 + nrec
])
268 << (node
->data
->sblock
.log2_bsize
269 - GRUB_DISK_SECTOR_BITS
),
270 0, node
->data
->sblock
.bsize
, leaf
))
273 if (grub_strncmp ((char *) leaf
->magic
, "BMAP", 4))
276 grub_error (GRUB_ERR_BAD_FS
, "not a correct XFS BMAP node.\n");
280 nrec
= grub_be_to_cpu16 (leaf
->numrecs
);
281 keys
= &leaf
->keys
[0];
282 } while (leaf
->level
);
283 exts
= (grub_xfs_extent
*) keys
;
285 else if (node
->inode
.format
== XFS_INODE_FORMAT_EXT
)
287 nrec
= grub_be_to_cpu32 (node
->inode
.nextents
);
288 exts
= &node
->inode
.data
.extents
[0];
292 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
293 "xfs does not support inode format %d yet",
298 /* Iterate over each extent to figure out which extent has
299 the block we are looking for. */
300 for (ex
= 0; ex
< nrec
; ex
++)
302 grub_uint64_t start
= GRUB_XFS_EXTENT_BLOCK (exts
, ex
);
303 grub_uint64_t offset
= GRUB_XFS_EXTENT_OFFSET (exts
, ex
);
304 grub_uint64_t size
= GRUB_XFS_EXTENT_SIZE (exts
, ex
);
307 if (fileblock
< offset
)
309 else if (fileblock
< offset
+ size
)
311 ret
= (fileblock
- offset
+ start
);
319 return GRUB_XFS_FSB_TO_BLOCK(node
->data
, ret
);
323 /* Read LEN bytes from the file described by DATA starting with byte
324 POS. Return the amount of read bytes in READ. */
326 grub_xfs_read_file (grub_fshelp_node_t node
,
327 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
328 unsigned offset
, unsigned length
),
329 int pos
, grub_size_t len
, char *buf
)
331 return grub_fshelp_read_file (node
->data
->disk
, node
, read_hook
,
332 pos
, len
, buf
, grub_xfs_read_block
,
333 grub_be_to_cpu64 (node
->inode
.size
),
334 node
->data
->sblock
.log2_bsize
335 - GRUB_DISK_SECTOR_BITS
);
340 grub_xfs_read_symlink (grub_fshelp_node_t node
)
342 int size
= grub_be_to_cpu64 (node
->inode
.size
);
344 switch (node
->inode
.format
)
346 case XFS_INODE_FORMAT_INO
:
347 return grub_strndup (node
->inode
.data
.raw
, size
);
349 case XFS_INODE_FORMAT_EXT
:
352 grub_ssize_t numread
;
354 symlink
= grub_malloc (size
+ 1);
358 numread
= grub_xfs_read_file (node
, 0, 0, size
, symlink
);
364 symlink
[size
] = '\0';
373 static enum grub_fshelp_filetype
374 grub_xfs_mode_to_filetype (grub_uint16_t mode
)
376 if ((grub_be_to_cpu16 (mode
)
377 & FILETYPE_INO_MASK
) == FILETYPE_INO_DIRECTORY
)
378 return GRUB_FSHELP_DIR
;
379 else if ((grub_be_to_cpu16 (mode
)
380 & FILETYPE_INO_MASK
) == FILETYPE_INO_SYMLINK
)
381 return GRUB_FSHELP_SYMLINK
;
382 else if ((grub_be_to_cpu16 (mode
)
383 & FILETYPE_INO_MASK
) == FILETYPE_INO_REG
)
384 return GRUB_FSHELP_REG
;
385 return GRUB_FSHELP_UNKNOWN
;
390 grub_xfs_iterate_dir (grub_fshelp_node_t dir
,
392 (*hook
) (const char *filename
,
393 enum grub_fshelp_filetype filetype
,
394 grub_fshelp_node_t node
))
396 struct grub_fshelp_node
*diro
= (struct grub_fshelp_node
*) dir
;
397 auto int NESTED_FUNC_ATTR
call_hook (grub_uint64_t ino
, char *filename
);
399 int NESTED_FUNC_ATTR
call_hook (grub_uint64_t ino
, char *filename
)
401 struct grub_fshelp_node
*fdiro
;
403 fdiro
= grub_malloc (sizeof (struct grub_fshelp_node
)
404 - sizeof (struct grub_xfs_inode
)
405 + (1 << diro
->data
->sblock
.log2_inode
));
409 /* The inode should be read, otherwise the filetype can
410 not be determined. */
412 fdiro
->inode_read
= 1;
413 fdiro
->data
= diro
->data
;
414 grub_xfs_read_inode (diro
->data
, ino
, &fdiro
->inode
);
416 return hook (filename
,
417 grub_xfs_mode_to_filetype (fdiro
->inode
.mode
),
421 switch (diro
->inode
.format
)
423 case XFS_INODE_FORMAT_INO
:
425 struct grub_xfs_dir_entry
*de
= &diro
->inode
.data
.dir
.direntry
[0];
426 int smallino
= !diro
->inode
.data
.dir
.dirhead
.smallino
;
428 grub_uint64_t parent
;
430 /* If small inode numbers are used to pack the direntry, the
431 parent inode number is small too. */
434 parent
= grub_be_to_cpu32 (diro
->inode
.data
.dir
.dirhead
.parent
.i4
);
435 parent
= grub_cpu_to_be64 (parent
);
436 /* The header is a bit smaller than usual. */
437 de
= (struct grub_xfs_dir_entry
*) ((char *) de
- 4);
441 parent
= diro
->inode
.data
.dir
.dirhead
.parent
.i8
;
444 /* Synthesize the direntries for `.' and `..'. */
445 if (call_hook (diro
->ino
, "."))
448 if (call_hook (parent
, ".."))
451 for (i
= 0; i
< diro
->inode
.data
.dir
.dirhead
.count
; i
++)
454 void *inopos
= (((char *) de
)
455 + sizeof (struct grub_xfs_dir_entry
)
457 char name
[de
->len
+ 1];
461 ino
= grub_be_to_cpu32 (*(grub_uint32_t
*) inopos
);
462 ino
= grub_cpu_to_be64 (ino
);
465 ino
= *(grub_uint64_t
*) inopos
;
467 grub_memcpy (name
, de
->name
, de
->len
);
468 name
[de
->len
] = '\0';
469 if (call_hook (ino
, name
))
472 de
= ((struct grub_xfs_dir_entry
*)
473 (((char *) de
)+ sizeof (struct grub_xfs_dir_entry
) + de
->len
474 + ((smallino
? sizeof (grub_uint32_t
)
475 : sizeof (grub_uint64_t
))) - 1));
480 case XFS_INODE_FORMAT_BTREE
:
481 case XFS_INODE_FORMAT_EXT
:
483 grub_ssize_t numread
;
486 int dirblk_size
, dirblk_log2
;
488 dirblk_log2
= (dir
->data
->sblock
.log2_bsize
489 + dir
->data
->sblock
.log2_dirblk
);
490 dirblk_size
= 1 << dirblk_log2
;
492 dirblock
= grub_malloc (dirblk_size
);
496 /* Iterate over every block the directory has. */
498 blk
< (grub_be_to_cpu64 (dir
->inode
.size
)
502 /* The header is skipped, the first direntry is stored
506 int tail_start
= (dirblk_size
507 - sizeof (struct grub_xfs_dirblock_tail
));
509 struct grub_xfs_dirblock_tail
*tail
;
510 tail
= (struct grub_xfs_dirblock_tail
*) &dirblock
[tail_start
];
512 numread
= grub_xfs_read_file (dir
, 0,
514 dirblk_size
, dirblock
);
515 if (numread
!= dirblk_size
)
518 entries
= (grub_be_to_cpu32 (tail
->leaf_count
)
519 - grub_be_to_cpu32 (tail
->leaf_stale
));
521 /* Iterate over all entries within this block. */
522 while (pos
< (dirblk_size
523 - (int) sizeof (struct grub_xfs_dir2_entry
)))
525 struct grub_xfs_dir2_entry
*direntry
;
526 grub_uint16_t
*freetag
;
529 direntry
= (struct grub_xfs_dir2_entry
*) &dirblock
[pos
];
530 freetag
= (grub_uint16_t
*) direntry
;
532 if (*freetag
== 0XFFFF)
534 grub_uint16_t
*skip
= (grub_uint16_t
*) (freetag
+ 1);
536 /* This entry is not used, go to the next one. */
537 pos
+= grub_be_to_cpu16 (*skip
);
542 filename
= &dirblock
[pos
+ sizeof (*direntry
)];
543 /* The byte after the filename is for the tag, which
544 is not used by GRUB. So it can be overwritten. */
545 filename
[direntry
->len
] = '\0';
547 if (call_hook (direntry
->inode
, filename
))
549 grub_free (dirblock
);
553 /* Check if last direntry in this block is
559 /* Select the next directory entry. */
560 pos
= GRUB_XFS_NEXT_DIRENT (pos
, direntry
->len
);
561 pos
= GRUB_XFS_ROUND_TO_DIRENT (pos
);
564 grub_free (dirblock
);
569 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
570 "xfs does not support inode format %d yet",
577 static struct grub_xfs_data
*
578 grub_xfs_mount (grub_disk_t disk
)
580 struct grub_xfs_data
*data
= 0;
582 data
= grub_zalloc (sizeof (struct grub_xfs_data
));
586 /* Read the superblock. */
587 if (grub_disk_read (disk
, 0, 0,
588 sizeof (struct grub_xfs_sblock
), &data
->sblock
))
591 if (grub_strncmp ((char *) (data
->sblock
.magic
), "XFSB", 4))
593 grub_error (GRUB_ERR_BAD_FS
, "not a xfs filesystem");
597 data
= grub_realloc (data
,
598 sizeof (struct grub_xfs_data
)
599 - sizeof (struct grub_xfs_inode
)
600 + (1 << data
->sblock
.log2_inode
));
605 data
->diropen
.data
= data
;
606 data
->diropen
.ino
= data
->sblock
.rootino
;
607 data
->diropen
.inode_read
= 1;
608 data
->bsize
= grub_be_to_cpu32 (data
->sblock
.bsize
);
609 data
->agsize
= grub_be_to_cpu32 (data
->sblock
.agsize
);
614 grub_xfs_read_inode (data
, data
->diropen
.ino
, &data
->diropen
.inode
);
619 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
620 grub_error (GRUB_ERR_BAD_FS
, "not an xfs filesystem");
629 grub_xfs_dir (grub_device_t device
, const char *path
,
630 int (*hook
) (const char *filename
,
631 const struct grub_dirhook_info
*info
))
633 struct grub_xfs_data
*data
= 0;
634 struct grub_fshelp_node
*fdiro
= 0;
636 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
637 enum grub_fshelp_filetype filetype
,
638 grub_fshelp_node_t node
);
640 int NESTED_FUNC_ATTR
iterate (const char *filename
,
641 enum grub_fshelp_filetype filetype
,
642 grub_fshelp_node_t node
)
644 struct grub_dirhook_info info
;
645 grub_memset (&info
, 0, sizeof (info
));
646 info
.dir
= ((filetype
& GRUB_FSHELP_TYPE_MASK
) == GRUB_FSHELP_DIR
);
648 return hook (filename
, &info
);
651 grub_dl_ref (my_mod
);
653 data
= grub_xfs_mount (device
->disk
);
657 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_xfs_iterate_dir
,
658 grub_xfs_read_symlink
, GRUB_FSHELP_DIR
);
662 grub_xfs_iterate_dir (fdiro
, iterate
);
665 if (fdiro
!= &data
->diropen
)
671 grub_dl_unref (my_mod
);
677 /* Open a file named NAME and initialize FILE. */
679 grub_xfs_open (struct grub_file
*file
, const char *name
)
681 struct grub_xfs_data
*data
;
682 struct grub_fshelp_node
*fdiro
= 0;
684 grub_dl_ref (my_mod
);
686 data
= grub_xfs_mount (file
->device
->disk
);
690 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_xfs_iterate_dir
,
691 grub_xfs_read_symlink
, GRUB_FSHELP_REG
);
695 if (!fdiro
->inode_read
)
697 grub_xfs_read_inode (data
, fdiro
->ino
, &fdiro
->inode
);
702 if (fdiro
!= &data
->diropen
)
703 grub_memcpy (&data
->diropen
, fdiro
,
704 sizeof (struct grub_fshelp_node
)
705 - sizeof (struct grub_xfs_inode
)
706 + (1 << data
->sblock
.log2_inode
));
708 file
->size
= grub_be_to_cpu64 (data
->diropen
.inode
.size
);
715 if (fdiro
!= &data
->diropen
)
720 grub_dl_unref (my_mod
);
727 grub_xfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
729 struct grub_xfs_data
*data
=
730 (struct grub_xfs_data
*) file
->data
;
732 return grub_xfs_read_file (&data
->diropen
, file
->read_hook
,
733 file
->offset
, len
, buf
);
738 grub_xfs_close (grub_file_t file
)
740 grub_free (file
->data
);
742 grub_dl_unref (my_mod
);
744 return GRUB_ERR_NONE
;
749 grub_xfs_label (grub_device_t device
, char **label
)
751 struct grub_xfs_data
*data
;
752 grub_disk_t disk
= device
->disk
;
754 grub_dl_ref (my_mod
);
756 data
= grub_xfs_mount (disk
);
758 *label
= grub_strndup ((char *) (data
->sblock
.label
), 12);
762 grub_dl_unref (my_mod
);
770 grub_xfs_uuid (grub_device_t device
, char **uuid
)
772 struct grub_xfs_data
*data
;
773 grub_disk_t disk
= device
->disk
;
775 grub_dl_ref (my_mod
);
777 data
= grub_xfs_mount (disk
);
780 *uuid
= grub_malloc (sizeof ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"));
781 grub_sprintf (*uuid
, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
782 grub_be_to_cpu16 (data
->sblock
.uuid
[0]), grub_be_to_cpu16 (data
->sblock
.uuid
[1]),
783 grub_be_to_cpu16 (data
->sblock
.uuid
[2]), grub_be_to_cpu16 (data
->sblock
.uuid
[3]),
784 grub_be_to_cpu16 (data
->sblock
.uuid
[4]), grub_be_to_cpu16 (data
->sblock
.uuid
[5]),
785 grub_be_to_cpu16 (data
->sblock
.uuid
[6]), grub_be_to_cpu16 (data
->sblock
.uuid
[7]));
790 grub_dl_unref (my_mod
);
799 static struct grub_fs grub_xfs_fs
=
803 .open
= grub_xfs_open
,
804 .read
= grub_xfs_read
,
805 .close
= grub_xfs_close
,
806 .label
= grub_xfs_label
,
807 .uuid
= grub_xfs_uuid
,
813 grub_fs_register (&grub_xfs_fs
);
819 grub_fs_unregister (&grub_xfs_fs
);