3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
11 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <grub/file.h>
24 #include <grub/misc.h>
25 #include <grub/disk.h>
27 #include <grub/types.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];
57 struct grub_jfs_extent
59 /* The length of the extent in filesystem blocks. */
63 /* The physical offset of the first block on the disk. */
66 } __attribute__ ((packed
));
70 grub_uint8_t unused
[3072];
71 struct grub_jfs_extent inodes
[128];
72 } __attribute__ ((packed
));
75 /* The head of the tree used to find extents. */
76 struct grub_jfs_treehead
86 grub_uint8_t unused2
[10];
87 } __attribute__ ((packed
));
89 /* A node in the extent tree. */
90 struct grub_jfs_tree_extent
95 /* The offset is the key used to lookup an extent. */
97 grub_uint32_t offset2
;
99 struct grub_jfs_extent extent
;
100 } __attribute__ ((packed
));
102 /* The tree of directory entries. */
103 struct grub_jfs_tree_dir
105 /* Pointers to the previous and next tree headers of other nodes on
112 /* The amount of dirents in this node. */
114 grub_uint8_t freecnt
;
115 grub_uint8_t freelist
;
116 grub_uint8_t maxslot
;
118 /* The location of the sorted array of pointers to dirents. */
120 grub_uint8_t unused
[10];
121 } __attribute__ ((packed
));
123 /* An internal node in the dirents tree. */
124 struct grub_jfs_internal_dirent
126 struct grub_jfs_extent ex
;
129 grub_uint16_t namepart
[11];
130 } __attribute__ ((packed
));
132 /* A leaf node in the dirents tree. */
133 struct grub_jfs_leaf_dirent
135 /* The inode for this dirent. */
139 /* The size of the name. */
141 grub_uint16_t namepart
[11];
143 } __attribute__ ((packed
));
145 /* A leaf in the dirents tree. This one is used if the previously
146 dirent was not big enough to store the name. */
147 struct grub_jfs_leaf_next_dirent
151 grub_uint16_t namepart
[15];
152 } __attribute__ ((packed
));
154 struct grub_jfs_inode
157 grub_uint32_t fileset
;
159 grub_uint8_t unused
[12];
161 grub_uint8_t unused2
[20];
163 grub_uint8_t unused3
[72];
164 grub_uint8_t unused4
[96];
168 /* The tree describing the extents of the file. */
171 struct grub_jfs_treehead tree
;
172 struct grub_jfs_tree_extent extents
[16];
173 } file
__attribute__ ((packed
));
176 /* The tree describing the dirents. */
179 grub_uint8_t unused
[16];
182 /* Amount of dirents in this node. */
184 grub_uint8_t freecnt
;
185 grub_uint8_t freelist
;
186 grub_uint32_t idotdot
;
187 grub_uint8_t sorted
[8];
189 struct grub_jfs_leaf_dirent dirents
[8];
190 } dir
__attribute__ ((packed
));
194 grub_uint8_t unused
[32];
195 grub_uint8_t path
[128];
197 } __attribute__ ((packed
));
198 } __attribute__ ((packed
));
202 struct grub_jfs_sblock sblock
;
204 struct grub_jfs_inode fileset
;
205 struct grub_jfs_inode currinode
;
208 } __attribute__ ((packed
));
210 struct grub_jfs_diropen
215 struct grub_jfs_tree_dir header
;
216 struct grub_jfs_leaf_dirent dirent
[0];
217 struct grub_jfs_leaf_next_dirent next_dirent
[0];
219 } *dirpage
__attribute__ ((packed
));
220 struct grub_jfs_data
*data
;
221 struct grub_jfs_inode
*inode
;
224 struct grub_jfs_leaf_dirent
*leaf
;
225 struct grub_jfs_leaf_next_dirent
*next_leaf
;
227 /* The filename and inode of the last read dirent. */
230 } __attribute__ ((packed
));
234 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
), (char *) &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
), (char *) 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
), (char *) &data
->sblock
))
345 if (grub_strncmp (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
), (char *) &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_malloc (sizeof (struct grub_jfs_diropen
));
396 /* Check if the entire tree is contained within the inode. */
397 if (inode
->file
.tree
.flags
& GRUB_JFS_TREE_LEAF
)
399 diro
->leaf
= inode
->dir
.dirents
;
400 diro
->next_leaf
= (struct grub_jfs_leaf_next_dirent
*) de
;
401 diro
->sorted
= inode
->dir
.header
.sorted
;
402 diro
->count
= inode
->dir
.header
.count
;
408 diro
->dirpage
= grub_malloc (grub_le_to_cpu32 (data
->sblock
.blksz
));
415 blk
= grub_le_to_cpu32 (de
[inode
->dir
.header
.sorted
[0]].ex
.blk2
);
416 blk
<<= (grub_le_to_cpu16 (data
->sblock
.log2_blksz
) - GRUB_DISK_SECTOR_BITS
);
418 /* Read in the nodes until we are on the leaf node level. */
422 if (grub_disk_read (data
->disk
, blk
, 0,
423 grub_le_to_cpu32 (data
->sblock
.blksz
),
424 diro
->dirpage
->sorted
))
426 grub_free (diro
->dirpage
);
431 de
= (struct grub_jfs_internal_dirent
*) diro
->dirpage
->dirent
;
432 index
= diro
->dirpage
->sorted
[diro
->dirpage
->header
.sindex
* 32];
433 blk
= (grub_le_to_cpu32 (de
[index
].ex
.blk2
)
434 << (grub_le_to_cpu16 (data
->sblock
.log2_blksz
)
435 - GRUB_DISK_SECTOR_BITS
));
436 } while (!(diro
->dirpage
->header
.flags
& GRUB_JFS_TREE_LEAF
));
438 diro
->leaf
= diro
->dirpage
->dirent
;
439 diro
->next_leaf
= diro
->dirpage
->next_dirent
;
440 diro
->sorted
= &diro
->dirpage
->sorted
[diro
->dirpage
->header
.sindex
* 32];
441 diro
->count
= diro
->dirpage
->header
.count
;
448 grub_jfs_closedir (struct grub_jfs_diropen
*diro
)
452 grub_free (diro
->dirpage
);
457 /* Read in the next dirent from the directory described by DIRO. */
459 grub_jfs_getent (struct grub_jfs_diropen
*diro
)
462 struct grub_jfs_leaf_dirent
*leaf
;
463 struct grub_jfs_leaf_next_dirent
*next_leaf
;
466 grub_uint16_t filename
[255];
468 auto void addstr (grub_uint16_t
*uname
, int ulen
);
470 /* Add the unicode string to the utf16 filename buffer. */
471 void addstr (grub_uint16_t
*name
, int ulen
)
474 filename
[strpos
++] = *(name
++);
477 /* The last node, read in more. */
478 if (diro
->index
== diro
->count
)
482 /* If the inode contains the entrie tree or if this was the last
483 node, there is nothing to read. */
484 if ((diro
->inode
->file
.tree
.flags
& GRUB_JFS_TREE_LEAF
)
485 || !grub_le_to_cpu64 (diro
->dirpage
->header
.nextb
))
486 return GRUB_ERR_OUT_OF_RANGE
;
488 next
= grub_le_to_cpu64 (diro
->dirpage
->header
.nextb
);
489 next
<<= (grub_le_to_cpu16 (diro
->data
->sblock
.log2_blksz
)
490 - GRUB_DISK_SECTOR_BITS
);
492 if (grub_disk_read (diro
->data
->disk
, next
, 0,
493 grub_le_to_cpu32 (diro
->data
->sblock
.blksz
),
494 diro
->dirpage
->sorted
))
497 diro
->leaf
= diro
->dirpage
->dirent
;
498 diro
->next_leaf
= diro
->dirpage
->next_dirent
;
499 diro
->sorted
= &diro
->dirpage
->sorted
[diro
->dirpage
->header
.sindex
* 32];
500 diro
->count
= diro
->dirpage
->header
.count
;
504 leaf
= &diro
->leaf
[(int) diro
->sorted
[diro
->index
]];
505 next_leaf
= &diro
->next_leaf
[diro
->index
];
511 return grub_jfs_getent (diro
);
514 addstr (leaf
->namepart
, len
< 11 ? len
: 11);
515 diro
->ino
= grub_le_to_cpu32 (leaf
->inode
);
518 /* Move down to the leaf level. */
519 nextent
= leaf
->next
;
520 if (leaf
->next
!= 255)
523 next_leaf
= &diro
->next_leaf
[nextent
];
524 addstr (next_leaf
->namepart
, len
< 15 ? len
: 15 );
527 nextent
= next_leaf
->next
;
528 } while (next_leaf
->next
!= 255 && len
> 0);
532 /* Convert the temporary UTF16 filename to UTF8. */
533 *grub_utf16_to_utf8 (diro
->name
, filename
, strpos
) = '\0';
539 /* Read LEN bytes from the file described by DATA starting with byte
540 POS. Return the amount of read bytes in READ. */
542 grub_jfs_read_file (struct grub_jfs_data
*data
,
543 void (*read_hook
) (unsigned long sector
,
544 unsigned offset
, unsigned length
),
545 int pos
, unsigned int len
, char *buf
)
550 /* Adjust len so it we can't read past the end of the file. */
551 if (len
> data
->currinode
.size
)
552 len
= data
->currinode
.size
;
554 blockcnt
= ((len
+ pos
+ grub_le_to_cpu32 (data
->sblock
.blksz
) - 1)
555 / grub_le_to_cpu32 (data
->sblock
.blksz
));
557 for (i
= pos
/ grub_le_to_cpu32 (data
->sblock
.blksz
); i
< blockcnt
; i
++)
560 int blockoff
= pos
% grub_le_to_cpu32 (data
->sblock
.blksz
);
561 int blockend
= grub_le_to_cpu32 (data
->sblock
.blksz
);
565 blknr
= grub_jfs_blkno (data
, &data
->currinode
, i
);
570 if (i
== blockcnt
- 1)
572 blockend
= (len
+ pos
) % grub_le_to_cpu32 (data
->sblock
.blksz
);
575 blockend
= grub_le_to_cpu32 (data
->sblock
.blksz
);
579 if (i
== (pos
/ (int) grub_le_to_cpu32 (data
->sblock
.blksz
)))
581 skipfirst
= blockoff
;
582 blockend
-= skipfirst
;
585 data
->disk
->read_hook
= read_hook
;
586 grub_disk_read (data
->disk
,
587 blknr
<< (grub_le_to_cpu16 (data
->sblock
.log2_blksz
)
588 - GRUB_DISK_SECTOR_BITS
),
589 skipfirst
, blockend
, buf
);
591 data
->disk
->read_hook
= 0;
595 buf
+= grub_le_to_cpu32 (data
->sblock
.blksz
) - skipfirst
;
602 /* Find the file with the pathname PATH on the filesystem described by
605 grub_jfs_find_file (struct grub_jfs_data
*data
, const char *path
)
607 char fpath
[grub_strlen (path
)];
610 unsigned int pos
= 0;
611 struct grub_jfs_diropen
*diro
;
613 grub_strncpy (fpath
, path
, grub_strlen (path
) + 1);
615 if (grub_jfs_read_inode (data
, GRUB_JFS_AGGR_INODE
, &data
->currinode
))
618 /* Skip the first slash. */
626 /* Extract the actual part from the pathname. */
627 next
= grub_strchr (name
, '/');
634 diro
= grub_jfs_opendir (data
, &data
->currinode
);
640 if (grub_strlen (name
) == 0)
641 return GRUB_ERR_NONE
;
643 if (grub_jfs_getent (diro
) == GRUB_ERR_OUT_OF_RANGE
)
646 /* Check if the current direntry matches the current part of the
648 if (!grub_strcmp (name
, diro
->name
))
651 int dirino
= grub_le_to_cpu32 (data
->currinode
.inode
);
653 grub_jfs_closedir (diro
);
656 if (grub_jfs_read_inode (data
, ino
, &data
->currinode
))
659 /* Check if this is a symlink. */
660 if ((grub_le_to_cpu32 (data
->currinode
.mode
)
661 & GRUB_JFS_FILETYPE_MASK
) == GRUB_JFS_FILETYPE_LNK
)
663 grub_jfs_lookup_symlink (data
, dirino
);
674 next
= grub_strchr (name
, '/');
681 /* Open this directory for reading dirents. */
682 diro
= grub_jfs_opendir (data
, &data
->currinode
);
690 grub_jfs_closedir (diro
);
691 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
697 grub_jfs_lookup_symlink (struct grub_jfs_data
*data
, int ino
)
699 int size
= grub_le_to_cpu64 (data
->currinode
.size
);
700 char symlink
[size
+ 1];
702 if (++data
->linknest
> GRUB_JFS_MAX_SYMLNK_CNT
)
703 return grub_error (GRUB_ERR_SYMLINK_LOOP
, "too deep nesting of symlinks");
706 grub_strncpy (symlink
, data
->currinode
.symlink
.path
, 128);
707 else if (grub_jfs_read_file (data
, 0, 0, size
, symlink
) < 0)
710 symlink
[size
] = '\0';
712 /* The symlink is an absolute path, go back to the root inode. */
713 if (symlink
[0] == '/')
716 /* Now load in the old inode. */
717 if (grub_jfs_read_inode (data
, ino
, &data
->currinode
))
720 grub_jfs_find_file (data
, symlink
);
722 grub_error (grub_errno
, "Can not follow symlink `%s'.", symlink
);
729 grub_jfs_dir (grub_device_t device
, const char *path
,
730 int (*hook
) (const char *filename
, int dir
))
732 struct grub_jfs_data
*data
= 0;
733 struct grub_jfs_diropen
*diro
= 0;
736 grub_dl_ref (my_mod
);
739 data
= grub_jfs_mount (device
->disk
);
743 if (grub_jfs_find_file (data
, path
))
746 diro
= grub_jfs_opendir (data
, &data
->currinode
);
750 /* Iterate over the dirents in the directory that was found. */
751 while (grub_jfs_getent (diro
) != GRUB_ERR_OUT_OF_RANGE
)
753 struct grub_jfs_inode inode
;
756 if (grub_jfs_read_inode (data
, diro
->ino
, &inode
))
759 isdir
= (grub_le_to_cpu32 (inode
.mode
)
760 & GRUB_JFS_FILETYPE_MASK
) == GRUB_JFS_FILETYPE_DIR
;
761 if (hook (diro
->name
, isdir
))
765 /* XXX: GRUB_ERR_OUT_OF_RANGE is used for the last dirent. */
766 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
770 grub_jfs_closedir (diro
);
774 grub_dl_unref (my_mod
);
781 /* Open a file named NAME and initialize FILE. */
783 grub_jfs_open (struct grub_file
*file
, const char *name
)
785 struct grub_jfs_data
*data
;
788 grub_dl_ref (my_mod
);
791 data
= grub_jfs_mount (file
->device
->disk
);
795 grub_jfs_find_file (data
, name
);
799 /* It is only possible for open regular files. */
800 if (! ((grub_le_to_cpu32 (data
->currinode
.mode
)
801 & GRUB_JFS_FILETYPE_MASK
) == GRUB_JFS_FILETYPE_REG
))
803 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a regular file");
808 file
->size
= grub_le_to_cpu64 (data
->currinode
.size
);
815 grub_dl_unref (my_mod
);
825 grub_jfs_read (grub_file_t file
, char *buf
, grub_ssize_t len
)
827 struct grub_jfs_data
*data
=
828 (struct grub_jfs_data
*) file
->data
;
830 return grub_jfs_read_file (data
, file
->read_hook
, file
->offset
, len
, buf
);
835 grub_jfs_close (grub_file_t file
)
837 grub_free (file
->data
);
840 grub_dl_unref (my_mod
);
843 return GRUB_ERR_NONE
;
848 grub_jfs_label (grub_device_t device
, char **label
)
850 struct grub_jfs_data
*data
;
851 data
= grub_jfs_mount (device
->disk
);
854 *label
= grub_strndup (data
->sblock
.volname
, 11);
862 static struct grub_fs grub_jfs_fs
=
866 .open
= grub_jfs_open
,
867 .read
= grub_jfs_read
,
868 .close
= grub_jfs_close
,
869 .label
= grub_jfs_label
,
875 grub_fs_register (&grub_jfs_fs
);
883 grub_fs_unregister (&grub_jfs_fs
);