3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007 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
[48];
41 grub_uint64_t rootino
;
42 grub_uint8_t unused2
[20];
44 grub_uint8_t unused3
[20];
45 grub_uint8_t label
[12];
46 grub_uint8_t log2_bsize
;
47 grub_uint8_t unused4
[2];
48 grub_uint8_t log2_inop
;
49 grub_uint8_t log2_agblk
;
50 } __attribute__ ((packed
));
52 struct grub_xfs_dir_header
55 grub_uint8_t smallino
;
57 } __attribute__ ((packed
));
59 struct grub_xfs_dir_entry
64 /* Inode number follows, 32 bits. */
65 } __attribute__ ((packed
));
67 struct grub_xfs_dir2_entry
71 } __attribute__ ((packed
));
73 typedef grub_uint32_t grub_xfs_extent
[4];
77 grub_uint8_t magic
[2];
81 grub_uint8_t unused2
[50];
83 grub_uint8_t unused3
[36];
89 struct grub_xfs_dir_header dirhead
;
90 struct grub_xfs_dir_entry direntry
[1];
92 grub_xfs_extent extents
[XFS_INODE_EXTENTS
];
93 } data
__attribute__ ((packed
));
94 } __attribute__ ((packed
));
96 struct grub_xfs_dirblock_tail
98 grub_uint32_t leaf_count
;
99 grub_uint32_t leaf_stale
;
100 } __attribute__ ((packed
));
102 struct grub_fshelp_node
104 struct grub_xfs_data
*data
;
105 struct grub_xfs_inode inode
;
112 struct grub_xfs_sblock sblock
;
113 struct grub_xfs_inode
*inode
;
118 struct grub_fshelp_node diropen
;
123 static grub_dl_t my_mod
;
128 /* Filetype information as used in inodes. */
129 #define FILETYPE_INO_MASK 0170000
130 #define FILETYPE_INO_REG 0100000
131 #define FILETYPE_INO_DIRECTORY 0040000
132 #define FILETYPE_INO_SYMLINK 0120000
134 #define GRUB_XFS_INO_AGBITS(data) \
135 ((data)->sblock.log2_agblk + (data)->sblock.log2_inop)
136 #define GRUB_XFS_INO_INOINAG(data, ino) \
137 (grub_be_to_cpu64 (ino) & ((1 << GRUB_XFS_INO_AGBITS (data)) - 1))
138 #define GRUB_XFS_INO_AG(data,ino) \
139 (grub_be_to_cpu64 (ino) >> GRUB_XFS_INO_AGBITS (data))
141 #define GRUB_XFS_EXTENT_OFFSET(inode,ex) \
142 ((grub_be_to_cpu32 ((inode)->data.extents[ex][0]) & ~(1 << 31)) << 23 \
143 | grub_be_to_cpu32 ((inode)->data.extents[ex][1]) >> 9)
145 #define GRUB_XFS_EXTENT_BLOCK(inode,ex) \
146 ((grub_uint64_t) (grub_be_to_cpu32 ((inode)->data.extents[ex][1]) \
148 | (grub_uint64_t) grub_be_to_cpu32 ((inode)->data.extents[ex][2]) << 11 \
149 | grub_be_to_cpu32 ((inode)->data.extents[ex][3]) >> 21)
151 #define GRUB_XFS_EXTENT_SIZE(inode,ex) \
152 (grub_be_to_cpu32 ((inode)->data.extents[ex][3]) & ((1 << 20) - 1))
154 #define GRUB_XFS_ROUND_TO_DIRENT(pos) ((((pos) + 8 - 1) / 8) * 8)
155 #define GRUB_XFS_NEXT_DIRENT(pos,len) \
156 (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2)
159 grub_xfs_inode_block (struct grub_xfs_data
*data
,
162 long long int inoinag
= GRUB_XFS_INO_INOINAG (data
, ino
);
163 long long ag
= GRUB_XFS_INO_AG (data
, ino
);
166 block
= (inoinag
>> 4) + ag
* data
->agsize
;
167 block
<<= (data
->sblock
.log2_bsize
- GRUB_DISK_SECTOR_BITS
);
173 grub_xfs_inode_offset (struct grub_xfs_data
*data
,
176 int inoag
= GRUB_XFS_INO_INOINAG (data
, ino
);
177 return (inoag
& ((1 << 4) - 1)) << 8;
182 grub_xfs_read_inode (struct grub_xfs_data
*data
, grub_uint64_t ino
,
183 struct grub_xfs_inode
*inode
)
185 int block
= grub_xfs_inode_block (data
, ino
);
186 int offset
= grub_xfs_inode_offset (data
, ino
);
188 /* Read the inode. */
189 if (grub_disk_read (data
->disk
, block
, offset
,
190 sizeof (struct grub_xfs_inode
), (char *) inode
))
193 if (grub_strncmp ((char *) inode
->magic
, "IN", 2))
194 return grub_error (GRUB_ERR_BAD_FS
, "not a correct XFS inode.\n");
201 grub_xfs_read_block (grub_fshelp_node_t node
, int fileblock
)
205 if (node
->inode
.format
!= XFS_INODE_FORMAT_EXT
)
207 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
208 "xfs does not support inode format %d yet",
213 /* Iterate over each extent to figure out which extent has
214 the block we are looking for. */
215 for (ex
= 0; ex
< XFS_INODE_EXTENTS
; ex
++)
217 grub_uint64_t start
= GRUB_XFS_EXTENT_BLOCK (&node
->inode
, ex
);
218 int offset
= GRUB_XFS_EXTENT_OFFSET (&node
->inode
, ex
);
219 int size
= GRUB_XFS_EXTENT_SIZE (&node
->inode
, ex
);
221 unsigned int ag
= start
>> node
->data
->sblock
.log2_agblk
;
222 unsigned int block
= start
& ((1 << node
->data
->sblock
.log2_agblk
) - 1);
224 if (fileblock
< offset
+ size
)
225 return (fileblock
- offset
+ block
) + ag
* node
->data
->agsize
;
228 grub_error (GRUB_ERR_FILE_READ_ERROR
,
229 "xfs block %d for inode %d is not in an extent.\n",
230 fileblock
, grub_be_to_cpu64 (node
->ino
));
235 /* Read LEN bytes from the file described by DATA starting with byte
236 POS. Return the amount of read bytes in READ. */
238 grub_xfs_read_file (grub_fshelp_node_t node
,
239 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
240 unsigned offset
, unsigned length
),
241 int pos
, grub_size_t len
, char *buf
)
243 return grub_fshelp_read_file (node
->data
->disk
, node
, read_hook
,
244 pos
, len
, buf
, grub_xfs_read_block
,
245 grub_be_to_cpu64 (node
->inode
.size
),
246 node
->data
->sblock
.log2_bsize
247 - GRUB_DISK_SECTOR_BITS
);
252 grub_xfs_read_symlink (grub_fshelp_node_t node
)
254 int size
= grub_be_to_cpu64 (node
->inode
.size
);
256 switch (node
->inode
.format
)
258 case XFS_INODE_FORMAT_INO
:
259 return grub_strndup (node
->inode
.data
.raw
, size
);
261 case XFS_INODE_FORMAT_EXT
:
264 grub_ssize_t numread
;
266 symlink
= grub_malloc (size
+ 1);
270 numread
= grub_xfs_read_file (node
, 0, 0, size
, symlink
);
276 symlink
[size
] = '\0';
285 static enum grub_fshelp_filetype
286 grub_xfs_mode_to_filetype (grub_uint16_t mode
)
288 if ((grub_be_to_cpu16 (mode
)
289 & FILETYPE_INO_MASK
) == FILETYPE_INO_DIRECTORY
)
290 return GRUB_FSHELP_DIR
;
291 else if ((grub_be_to_cpu16 (mode
)
292 & FILETYPE_INO_MASK
) == FILETYPE_INO_SYMLINK
)
293 return GRUB_FSHELP_SYMLINK
;
294 else if ((grub_be_to_cpu16 (mode
)
295 & FILETYPE_INO_MASK
) == FILETYPE_INO_REG
)
296 return GRUB_FSHELP_REG
;
297 return GRUB_FSHELP_UNKNOWN
;
302 grub_xfs_iterate_dir (grub_fshelp_node_t dir
,
304 (*hook
) (const char *filename
,
305 enum grub_fshelp_filetype filetype
,
306 grub_fshelp_node_t node
))
308 struct grub_fshelp_node
*diro
= (struct grub_fshelp_node
*) dir
;
309 auto int call_hook (grub_uint64_t ino
, char *filename
);
311 int call_hook (grub_uint64_t ino
, char *filename
)
313 struct grub_fshelp_node
*fdiro
;
315 fdiro
= grub_malloc (sizeof (struct grub_fshelp_node
));
319 /* The inode should be read, otherwise the filetype can
320 not be determined. */
322 fdiro
->inode_read
= 1;
323 fdiro
->data
= diro
->data
;
324 grub_xfs_read_inode (diro
->data
, ino
, &fdiro
->inode
);
326 return hook (filename
,
327 grub_xfs_mode_to_filetype (fdiro
->inode
.mode
),
331 switch (diro
->inode
.format
)
333 case XFS_INODE_FORMAT_INO
:
335 struct grub_xfs_dir_entry
*de
= &diro
->inode
.data
.dir
.direntry
[0];
336 int smallino
= !diro
->inode
.data
.dir
.dirhead
.smallino
;
338 grub_uint64_t parent
;
340 /* If small inode numbers are used to pack the direntry, the
341 parent inode number is small too. */
344 parent
= grub_be_to_cpu32 (diro
->inode
.data
.dir
.dirhead
.parent
);
345 parent
= grub_cpu_to_be64 (parent
);
349 parent
= *(grub_uint64_t
*) &diro
->inode
.data
.dir
.dirhead
.parent
;
350 /* The header is a bit bigger than usual. */
351 de
= (struct grub_xfs_dir_entry
*) ((char *) de
+ 4);
354 /* Synthesize the direntries for `.' and `..'. */
355 if (call_hook (diro
->ino
, "."))
358 if (call_hook (parent
, ".."))
361 for (i
= 0; i
< diro
->inode
.data
.dir
.dirhead
.entries
; i
++)
364 void *inopos
= (((char *) de
)
365 + sizeof (struct grub_xfs_dir_entry
)
367 char name
[de
->len
+ 1];
371 ino
= grub_be_to_cpu32 (*(grub_uint32_t
*) inopos
);
372 ino
= grub_cpu_to_be64 (ino
);
375 ino
= *(grub_uint64_t
*) inopos
;
377 grub_memcpy (name
, de
->name
, de
->len
);
378 name
[de
->len
] = '\0';
379 if (call_hook (ino
, name
))
382 de
= ((struct grub_xfs_dir_entry
*)
383 (((char *) de
)+ sizeof (struct grub_xfs_dir_entry
) + de
->len
384 + ((smallino
? sizeof (grub_uint32_t
)
385 : sizeof (grub_uint64_t
))) - 1));
390 case XFS_INODE_FORMAT_BTREE
:
391 case XFS_INODE_FORMAT_EXT
:
393 grub_ssize_t numread
;
397 dirblock
= grub_malloc (dir
->data
->bsize
);
401 /* Iterate over every block the directory has. */
403 blk
< (grub_be_to_cpu64 (dir
->inode
.size
)
404 >> dir
->data
->sblock
.log2_bsize
);
407 /* The header is skipped, the first direntry is stored
411 int tail_start
= (dir
->data
->bsize
412 - sizeof (struct grub_xfs_dirblock_tail
));
414 struct grub_xfs_dirblock_tail
*tail
;
415 tail
= (struct grub_xfs_dirblock_tail
*) &dirblock
[tail_start
];
417 numread
= grub_xfs_read_file (dir
, 0,
418 blk
<< dir
->data
->sblock
.log2_bsize
,
419 dir
->data
->bsize
, dirblock
);
420 if (numread
!= dir
->data
->bsize
)
423 entries
= (grub_be_to_cpu32 (tail
->leaf_count
)
424 - grub_be_to_cpu32 (tail
->leaf_stale
));
426 /* Iterate over all entries within this block. */
427 while (pos
< (dir
->data
->bsize
428 - (int) sizeof (struct grub_xfs_dir2_entry
)))
430 struct grub_xfs_dir2_entry
*direntry
;
431 grub_uint16_t
*freetag
;
434 direntry
= (struct grub_xfs_dir2_entry
*) &dirblock
[pos
];
435 freetag
= (grub_uint16_t
*) direntry
;
437 if (*freetag
== 0XFFFF)
439 grub_uint16_t
*skip
= (grub_uint16_t
*) (freetag
+ 1);
441 /* This entry is not used, go to the next one. */
442 pos
+= grub_be_to_cpu16 (*skip
);
447 filename
= &dirblock
[pos
+ sizeof (*direntry
)];
448 /* The byte after the filename is for the tag, which
449 is not used by GRUB. So it can be overwritten. */
450 filename
[direntry
->len
] = '\0';
452 if (call_hook (direntry
->inode
, filename
))
454 grub_free (dirblock
);
458 /* Check if last direntry in this block is
464 /* Select the next directory entry. */
465 pos
= GRUB_XFS_NEXT_DIRENT (pos
, direntry
->len
);
466 pos
= GRUB_XFS_ROUND_TO_DIRENT (pos
);
469 grub_free (dirblock
);
474 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
475 "xfs does not support inode format %d yet",
482 static struct grub_xfs_data
*
483 grub_xfs_mount (grub_disk_t disk
)
485 struct grub_xfs_data
*data
= 0;
487 data
= grub_malloc (sizeof (struct grub_xfs_data
));
491 /* Read the superblock. */
492 if (grub_disk_read (disk
, 0, 0,
493 sizeof (struct grub_xfs_sblock
), (char *) &data
->sblock
))
496 if (grub_strncmp ((char *) (data
->sblock
.magic
), "XFSB", 4))
498 grub_error (GRUB_ERR_BAD_FS
, "not a xfs filesystem");
502 data
->diropen
.data
= data
;
503 data
->diropen
.ino
= data
->sblock
.rootino
;
504 data
->diropen
.inode_read
= 1;
505 data
->bsize
= grub_be_to_cpu32 (data
->sblock
.bsize
);
506 data
->agsize
= grub_be_to_cpu32 (data
->sblock
.agsize
);
509 data
->inode
= &data
->diropen
.inode
;
512 grub_xfs_read_inode (data
, data
->diropen
.ino
, data
->inode
);
517 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
518 grub_error (GRUB_ERR_BAD_FS
, "not an xfs filesystem");
527 grub_xfs_dir (grub_device_t device
, const char *path
,
528 int (*hook
) (const char *filename
, int dir
))
530 struct grub_xfs_data
*data
= 0;;
531 struct grub_fshelp_node
*fdiro
= 0;
533 auto int NESTED_FUNC_ATTR
iterate (const char *filename
,
534 enum grub_fshelp_filetype filetype
,
535 grub_fshelp_node_t node
);
537 int NESTED_FUNC_ATTR
iterate (const char *filename
,
538 enum grub_fshelp_filetype filetype
,
539 grub_fshelp_node_t node
)
543 if (filetype
== GRUB_FSHELP_DIR
)
544 return hook (filename
, 1);
546 return hook (filename
, 0);
552 grub_dl_ref (my_mod
);
555 data
= grub_xfs_mount (device
->disk
);
559 grub_fshelp_find_file (path
, &data
->diropen
, &fdiro
, grub_xfs_iterate_dir
,
560 grub_xfs_read_symlink
, GRUB_FSHELP_DIR
);
564 grub_xfs_iterate_dir (fdiro
, iterate
);
567 if (fdiro
!= &data
->diropen
)
572 grub_dl_unref (my_mod
);
581 /* Open a file named NAME and initialize FILE. */
583 grub_xfs_open (struct grub_file
*file
, const char *name
)
585 struct grub_xfs_data
*data
;
586 struct grub_fshelp_node
*fdiro
= 0;
589 grub_dl_ref (my_mod
);
592 data
= grub_xfs_mount (file
->device
->disk
);
596 grub_fshelp_find_file (name
, &data
->diropen
, &fdiro
, grub_xfs_iterate_dir
,
597 grub_xfs_read_symlink
, GRUB_FSHELP_REG
);
601 if (!fdiro
->inode_read
)
603 grub_xfs_read_inode (data
, fdiro
->ino
, &fdiro
->inode
);
608 grub_memcpy (data
->inode
,
610 sizeof (struct grub_xfs_inode
));
613 file
->size
= grub_be_to_cpu64 (data
->inode
->size
);
620 if (fdiro
!= &data
->diropen
)
625 grub_dl_unref (my_mod
);
633 grub_xfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
635 struct grub_xfs_data
*data
=
636 (struct grub_xfs_data
*) file
->data
;
638 return grub_xfs_read_file (&data
->diropen
, file
->read_hook
,
639 file
->offset
, len
, buf
);
644 grub_xfs_close (grub_file_t file
)
646 grub_free (file
->data
);
649 grub_dl_unref (my_mod
);
652 return GRUB_ERR_NONE
;
657 grub_xfs_label (grub_device_t device
, char **label
)
659 struct grub_xfs_data
*data
;
660 grub_disk_t disk
= device
->disk
;
663 grub_dl_ref (my_mod
);
666 data
= grub_xfs_mount (disk
);
668 *label
= grub_strndup ((char *) (data
->sblock
.label
), 12);
673 grub_dl_unref (my_mod
);
683 static struct grub_fs grub_xfs_fs
=
687 .open
= grub_xfs_open
,
688 .read
= grub_xfs_read
,
689 .close
= grub_xfs_close
,
690 .label
= grub_xfs_label
,
696 grub_fs_register (&grub_xfs_fs
);
704 grub_fs_unregister (&grub_xfs_fs
);