3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2004,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/charset.h>
29
#define GRUB_JFS_MAX_SYMLNK_CNT 8
30
#define GRUB_JFS_FILETYPE_MASK 0170000
31
#define GRUB_JFS_FILETYPE_REG 0100000
32
#define GRUB_JFS_FILETYPE_LNK 0120000
33
#define GRUB_JFS_FILETYPE_DIR 0040000
35
#define GRUB_JFS_SBLOCK 64
36
#define GRUB_JFS_AGGR_INODE 2
37
#define GRUB_JFS_FS1_INODE_BLK 104
39
#define GRUB_JFS_TREE_LEAF 2
41
struct grub_jfs_sblock
43
/* The magic for JFS. It should contain the string "JFS1". */
44
grub_uint8_t magic[4];
45
grub_uint32_t version;
46
grub_uint64_t ag_size;
48
/* The size of a filesystem block in bytes. XXX: currently only
51
grub_uint16_t log2_blksz;
53
grub_uint8_t unused[71];
54
grub_uint8_t volname[11];
55
grub_uint8_t unused2[32];
56
grub_uint8_t uuid[16];
59
struct grub_jfs_extent
61
/* The length of the extent in filesystem blocks. */
65
/* The physical offset of the first block on the disk. */
68
} __attribute__ ((packed));
72
grub_uint8_t unused[3072];
73
struct grub_jfs_extent inodes[128];
74
} __attribute__ ((packed));
77
/* The head of the tree used to find extents. */
78
struct grub_jfs_treehead
88
grub_uint8_t unused2[10];
89
} __attribute__ ((packed));
91
/* A node in the extent tree. */
92
struct grub_jfs_tree_extent
97
/* The offset is the key used to lookup an extent. */
99
grub_uint32_t offset2;
101
struct grub_jfs_extent extent;
102
} __attribute__ ((packed));
104
/* The tree of directory entries. */
105
struct grub_jfs_tree_dir
107
/* Pointers to the previous and next tree headers of other nodes on
114
/* The amount of dirents in this node. */
116
grub_uint8_t freecnt;
117
grub_uint8_t freelist;
118
grub_uint8_t maxslot;
120
/* The location of the sorted array of pointers to dirents. */
122
grub_uint8_t unused[10];
123
} __attribute__ ((packed));
125
/* An internal node in the dirents tree. */
126
struct grub_jfs_internal_dirent
128
struct grub_jfs_extent ex;
131
grub_uint16_t namepart[11];
132
} __attribute__ ((packed));
134
/* A leaf node in the dirents tree. */
135
struct grub_jfs_leaf_dirent
137
/* The inode for this dirent. */
141
/* The size of the name. */
143
grub_uint16_t namepart[11];
145
} __attribute__ ((packed));
147
/* A leaf in the dirents tree. This one is used if the previously
148
dirent was not big enough to store the name. */
149
struct grub_jfs_leaf_next_dirent
153
grub_uint16_t namepart[15];
154
} __attribute__ ((packed));
156
struct grub_jfs_inode
159
grub_uint32_t fileset;
161
grub_uint8_t unused[12];
163
grub_uint8_t unused2[20];
165
grub_uint8_t unused3[72];
166
grub_uint8_t unused4[96];
170
/* The tree describing the extents of the file. */
171
struct __attribute__ ((packed))
173
struct grub_jfs_treehead tree;
174
struct grub_jfs_tree_extent extents[16];
178
/* The tree describing the dirents. */
181
grub_uint8_t unused[16];
184
/* Amount of dirents in this node. */
186
grub_uint8_t freecnt;
187
grub_uint8_t freelist;
188
grub_uint32_t idotdot;
189
grub_uint8_t sorted[8];
191
struct grub_jfs_leaf_dirent dirents[8];
192
} dir __attribute__ ((packed));
196
grub_uint8_t unused[32];
197
grub_uint8_t path[128];
199
} __attribute__ ((packed));
200
} __attribute__ ((packed));
204
struct grub_jfs_sblock sblock;
206
struct grub_jfs_inode fileset;
207
struct grub_jfs_inode currinode;
210
} __attribute__ ((packed));
212
struct grub_jfs_diropen
217
struct grub_jfs_tree_dir header;
218
struct grub_jfs_leaf_dirent dirent[0];
219
struct grub_jfs_leaf_next_dirent next_dirent[0];
221
} *dirpage __attribute__ ((packed));
222
struct grub_jfs_data *data;
223
struct grub_jfs_inode *inode;
226
struct grub_jfs_leaf_dirent *leaf;
227
struct grub_jfs_leaf_next_dirent *next_leaf;
229
/* The filename and inode of the last read dirent. */
232
} __attribute__ ((packed));
235
static grub_dl_t my_mod;
237
static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino);
239
/* Get the block number for the block BLK in the node INODE in the
240
mounted filesystem DATA. */
242
grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode,
245
auto int getblk (struct grub_jfs_treehead *treehead,
246
struct grub_jfs_tree_extent *extents);
248
int getblk (struct grub_jfs_treehead *treehead,
249
struct grub_jfs_tree_extent *extents)
254
for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2; i++)
256
if (treehead->flags & GRUB_JFS_TREE_LEAF)
258
/* Read the leafnode. */
259
if (grub_le_to_cpu32 (extents[i].offset2) <= blk
260
&& ((grub_le_to_cpu16 (extents[i].extent.length))
261
+ (extents[i].extent.length2 << 8)
262
+ grub_le_to_cpu32 (extents[i].offset2)) > blk)
263
return (blk - grub_le_to_cpu32 (extents[i].offset2)
264
+ grub_le_to_cpu32 (extents[i].extent.blk2));
267
if (blk >= grub_le_to_cpu32 (extents[i].offset2))
275
struct grub_jfs_treehead treehead;
276
struct grub_jfs_tree_extent extents[254];
279
if (grub_disk_read (data->disk,
280
grub_le_to_cpu32 (extents[found].extent.blk2)
281
<< (grub_le_to_cpu16 (data->sblock.log2_blksz)
282
- GRUB_DISK_SECTOR_BITS), 0,
283
sizeof (tree), (char *) &tree))
286
return getblk (&tree.treehead, &tree.extents[0]);
292
return getblk (&inode->file.tree, &inode->file.extents[0]);
297
grub_jfs_read_inode (struct grub_jfs_data *data, int ino,
298
struct grub_jfs_inode *inode)
300
struct grub_jfs_iag iag;
301
int iagnum = ino / 4096;
302
int inoext = (ino % 4096) / 32;
303
int inonum = (ino % 4096) % 32;
304
grub_uint32_t iagblk;
305
grub_uint32_t inoblk;
307
iagblk = grub_jfs_blkno (data, &data->fileset, iagnum + 1);
311
/* Read in the IAG. */
312
if (grub_disk_read (data->disk,
313
iagblk << (grub_le_to_cpu16 (data->sblock.log2_blksz)
314
- GRUB_DISK_SECTOR_BITS), 0,
315
sizeof (struct grub_jfs_iag), &iag))
318
inoblk = grub_le_to_cpu32 (iag.inodes[inoext].blk2);
319
inoblk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz)
320
- GRUB_DISK_SECTOR_BITS);
323
if (grub_disk_read (data->disk, inoblk, 0,
324
sizeof (struct grub_jfs_inode), inode))
331
static struct grub_jfs_data *
332
grub_jfs_mount (grub_disk_t disk)
334
struct grub_jfs_data *data = 0;
336
data = grub_malloc (sizeof (struct grub_jfs_data));
340
/* Read the superblock. */
341
if (grub_disk_read (disk, GRUB_JFS_SBLOCK, 0,
342
sizeof (struct grub_jfs_sblock), &data->sblock))
345
if (grub_strncmp ((char *) (data->sblock.magic), "JFS1", 4))
347
grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem");
355
/* Read the inode of the first fileset. */
356
if (grub_disk_read (data->disk, GRUB_JFS_FS1_INODE_BLK, 0,
357
sizeof (struct grub_jfs_inode), &data->fileset))
365
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
366
grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem");
372
static struct grub_jfs_diropen *
373
grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode)
375
struct grub_jfs_internal_dirent *de;
376
struct grub_jfs_diropen *diro;
379
de = (struct grub_jfs_internal_dirent *) inode->dir.dirents;
381
if (!((grub_le_to_cpu32 (inode->mode)
382
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR))
384
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
388
diro = grub_zalloc (sizeof (struct grub_jfs_diropen));
395
/* Check if the entire tree is contained within the inode. */
396
if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
398
diro->leaf = inode->dir.dirents;
399
diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de;
400
diro->sorted = (char *) (inode->dir.header.sorted);
401
diro->count = inode->dir.header.count;
406
diro->dirpage = grub_malloc (grub_le_to_cpu32 (data->sblock.blksz));
413
blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2);
414
blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS);
416
/* Read in the nodes until we are on the leaf node level. */
420
if (grub_disk_read (data->disk, blk, 0,
421
grub_le_to_cpu32 (data->sblock.blksz),
422
diro->dirpage->sorted))
424
grub_free (diro->dirpage);
429
de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent;
430
index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
431
blk = (grub_le_to_cpu32 (de[index].ex.blk2)
432
<< (grub_le_to_cpu16 (data->sblock.log2_blksz)
433
- GRUB_DISK_SECTOR_BITS));
434
} while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF));
436
diro->leaf = diro->dirpage->dirent;
437
diro->next_leaf = diro->dirpage->next_dirent;
438
diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
439
diro->count = diro->dirpage->header.count;
446
grub_jfs_closedir (struct grub_jfs_diropen *diro)
450
grub_free (diro->dirpage);
455
/* Read in the next dirent from the directory described by DIRO. */
457
grub_jfs_getent (struct grub_jfs_diropen *diro)
460
struct grub_jfs_leaf_dirent *leaf;
461
struct grub_jfs_leaf_next_dirent *next_leaf;
464
grub_uint16_t filename[255];
466
auto void addstr (grub_uint16_t *uname, int ulen);
468
/* Add the unicode string to the utf16 filename buffer. */
469
void addstr (grub_uint16_t *name, int ulen)
472
filename[strpos++] = *(name++);
475
/* The last node, read in more. */
476
if (diro->index == diro->count)
480
/* If the inode contains the entry tree or if this was the last
481
node, there is nothing to read. */
482
if ((diro->inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
483
|| !grub_le_to_cpu64 (diro->dirpage->header.nextb))
484
return GRUB_ERR_OUT_OF_RANGE;
486
next = grub_le_to_cpu64 (diro->dirpage->header.nextb);
487
next <<= (grub_le_to_cpu16 (diro->data->sblock.log2_blksz)
488
- GRUB_DISK_SECTOR_BITS);
490
if (grub_disk_read (diro->data->disk, next, 0,
491
grub_le_to_cpu32 (diro->data->sblock.blksz),
492
diro->dirpage->sorted))
495
diro->leaf = diro->dirpage->dirent;
496
diro->next_leaf = diro->dirpage->next_dirent;
497
diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
498
diro->count = diro->dirpage->header.count;
502
leaf = &diro->leaf[(int) diro->sorted[diro->index]];
503
next_leaf = &diro->next_leaf[diro->index];
509
return grub_jfs_getent (diro);
512
addstr (leaf->namepart, len < 11 ? len : 11);
513
diro->ino = grub_le_to_cpu32 (leaf->inode);
516
/* Move down to the leaf level. */
517
nextent = leaf->next;
518
if (leaf->next != 255)
521
next_leaf = &diro->next_leaf[nextent];
522
addstr (next_leaf->namepart, len < 15 ? len : 15 );
525
nextent = next_leaf->next;
526
} while (next_leaf->next != 255 && len > 0);
530
/* Convert the temporary UTF16 filename to UTF8. */
531
*grub_utf16_to_utf8 ((grub_uint8_t *) (diro->name), filename, strpos) = '\0';
537
/* Read LEN bytes from the file described by DATA starting with byte
538
POS. Return the amount of read bytes in READ. */
540
grub_jfs_read_file (struct grub_jfs_data *data,
541
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
542
unsigned offset, unsigned length),
543
int pos, grub_size_t len, char *buf)
548
blockcnt = ((len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1)
549
/ grub_le_to_cpu32 (data->sblock.blksz));
551
for (i = pos / grub_le_to_cpu32 (data->sblock.blksz); i < blockcnt; i++)
554
int blockoff = pos % grub_le_to_cpu32 (data->sblock.blksz);
555
int blockend = grub_le_to_cpu32 (data->sblock.blksz);
559
blknr = grub_jfs_blkno (data, &data->currinode, i);
564
if (i == blockcnt - 1)
566
blockend = (len + pos) % grub_le_to_cpu32 (data->sblock.blksz);
569
blockend = grub_le_to_cpu32 (data->sblock.blksz);
573
if (i == (pos / (int) grub_le_to_cpu32 (data->sblock.blksz)))
575
skipfirst = blockoff;
576
blockend -= skipfirst;
579
data->disk->read_hook = read_hook;
580
grub_disk_read (data->disk,
581
blknr << (grub_le_to_cpu16 (data->sblock.log2_blksz)
582
- GRUB_DISK_SECTOR_BITS),
583
skipfirst, blockend, buf);
585
data->disk->read_hook = 0;
589
buf += grub_le_to_cpu32 (data->sblock.blksz) - skipfirst;
596
/* Find the file with the pathname PATH on the filesystem described by
599
grub_jfs_find_file (struct grub_jfs_data *data, const char *path)
601
char fpath[grub_strlen (path)];
604
struct grub_jfs_diropen *diro;
606
grub_strncpy (fpath, path, grub_strlen (path) + 1);
608
if (grub_jfs_read_inode (data, GRUB_JFS_AGGR_INODE, &data->currinode))
611
/* Skip the first slashes. */
619
/* Extract the actual part from the pathname. */
620
next = grub_strchr (name, '/');
629
diro = grub_jfs_opendir (data, &data->currinode);
635
if (grub_strlen (name) == 0)
636
return GRUB_ERR_NONE;
638
if (grub_jfs_getent (diro) == GRUB_ERR_OUT_OF_RANGE)
641
/* Check if the current direntry matches the current part of the
643
if (!grub_strcmp (name, diro->name))
646
int dirino = grub_le_to_cpu32 (data->currinode.inode);
648
grub_jfs_closedir (diro);
651
if (grub_jfs_read_inode (data, ino, &data->currinode))
654
/* Check if this is a symlink. */
655
if ((grub_le_to_cpu32 (data->currinode.mode)
656
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_LNK)
658
grub_jfs_lookup_symlink (data, dirino);
667
next = grub_strchr (name, '/');
674
/* Open this directory for reading dirents. */
675
diro = grub_jfs_opendir (data, &data->currinode);
683
grub_jfs_closedir (diro);
684
grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
690
grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino)
692
int size = grub_le_to_cpu64 (data->currinode.size);
693
char symlink[size + 1];
695
if (++data->linknest > GRUB_JFS_MAX_SYMLNK_CNT)
696
return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
699
grub_strncpy (symlink, (char *) (data->currinode.symlink.path), 128);
700
else if (grub_jfs_read_file (data, 0, 0, size, symlink) < 0)
703
symlink[size] = '\0';
705
/* The symlink is an absolute path, go back to the root inode. */
706
if (symlink[0] == '/')
709
/* Now load in the old inode. */
710
if (grub_jfs_read_inode (data, ino, &data->currinode))
713
grub_jfs_find_file (data, symlink);
715
grub_error (grub_errno, "cannot follow symlink `%s'", symlink);
722
grub_jfs_dir (grub_device_t device, const char *path,
723
int (*hook) (const char *filename,
724
const struct grub_dirhook_info *info))
726
struct grub_jfs_data *data = 0;
727
struct grub_jfs_diropen *diro = 0;
729
grub_dl_ref (my_mod);
731
data = grub_jfs_mount (device->disk);
735
if (grub_jfs_find_file (data, path))
738
diro = grub_jfs_opendir (data, &data->currinode);
742
/* Iterate over the dirents in the directory that was found. */
743
while (grub_jfs_getent (diro) != GRUB_ERR_OUT_OF_RANGE)
745
struct grub_jfs_inode inode;
746
struct grub_dirhook_info info;
747
grub_memset (&info, 0, sizeof (info));
749
if (grub_jfs_read_inode (data, diro->ino, &inode))
752
info.dir = (grub_le_to_cpu32 (inode.mode)
753
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR;
754
if (hook (diro->name, &info))
758
/* XXX: GRUB_ERR_OUT_OF_RANGE is used for the last dirent. */
759
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
763
grub_jfs_closedir (diro);
766
grub_dl_unref (my_mod);
772
/* Open a file named NAME and initialize FILE. */
774
grub_jfs_open (struct grub_file *file, const char *name)
776
struct grub_jfs_data *data;
778
grub_dl_ref (my_mod);
780
data = grub_jfs_mount (file->device->disk);
784
grub_jfs_find_file (data, name);
788
/* It is only possible for open regular files. */
789
if (! ((grub_le_to_cpu32 (data->currinode.mode)
790
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_REG))
792
grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
797
file->size = grub_le_to_cpu64 (data->currinode.size);
803
grub_dl_unref (my_mod);
812
grub_jfs_read (grub_file_t file, char *buf, grub_size_t len)
814
struct grub_jfs_data *data =
815
(struct grub_jfs_data *) file->data;
817
return grub_jfs_read_file (data, file->read_hook, file->offset, len, buf);
822
grub_jfs_close (grub_file_t file)
824
grub_free (file->data);
826
grub_dl_unref (my_mod);
828
return GRUB_ERR_NONE;
832
grub_jfs_uuid (grub_device_t device, char **uuid)
834
struct grub_jfs_data *data;
835
grub_disk_t disk = device->disk;
837
grub_dl_ref (my_mod);
839
data = grub_jfs_mount (disk);
842
*uuid = grub_xasprintf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
843
"%02x%02x%02x%02x%02x%02x",
844
data->sblock.uuid[0], data->sblock.uuid[1],
845
data->sblock.uuid[2], data->sblock.uuid[3],
846
data->sblock.uuid[4], data->sblock.uuid[5],
847
data->sblock.uuid[6], data->sblock.uuid[7],
848
data->sblock.uuid[8], data->sblock.uuid[9],
849
data->sblock.uuid[10], data->sblock.uuid[11],
850
data->sblock.uuid[12], data->sblock.uuid[13],
851
data->sblock.uuid[14], data->sblock.uuid[15]);
856
grub_dl_unref (my_mod);
864
grub_jfs_label (grub_device_t device, char **label)
866
struct grub_jfs_data *data;
867
data = grub_jfs_mount (device->disk);
870
*label = grub_strndup ((char *) (data->sblock.volname), 11);
878
static struct grub_fs grub_jfs_fs =
882
.open = grub_jfs_open,
883
.read = grub_jfs_read,
884
.close = grub_jfs_close,
885
.label = grub_jfs_label,
886
.uuid = grub_jfs_uuid,
892
grub_fs_register (&grub_jfs_fs);
898
grub_fs_unregister (&grub_jfs_fs);