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
#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");
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");
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_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
781
grub_be_to_cpu16 (data->sblock.uuid[0]),
782
grub_be_to_cpu16 (data->sblock.uuid[1]),
783
grub_be_to_cpu16 (data->sblock.uuid[2]),
784
grub_be_to_cpu16 (data->sblock.uuid[3]),
785
grub_be_to_cpu16 (data->sblock.uuid[4]),
786
grub_be_to_cpu16 (data->sblock.uuid[5]),
787
grub_be_to_cpu16 (data->sblock.uuid[6]),
788
grub_be_to_cpu16 (data->sblock.uuid[7]));
793
grub_dl_unref (my_mod);
802
static struct grub_fs grub_xfs_fs =
806
.open = grub_xfs_open,
807
.read = grub_xfs_read,
808
.close = grub_xfs_close,
809
.label = grub_xfs_label,
810
.uuid = grub_xfs_uuid,
816
grub_fs_register (&grub_xfs_fs);
822
grub_fs_unregister (&grub_xfs_fs);