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>
28 #define GRUB_JFS_MAX_SYMLNK_CNT 8
29 #define GRUB_JFS_FILETYPE_MASK 0170000
30 #define GRUB_JFS_FILETYPE_REG 0100000
31 #define GRUB_JFS_FILETYPE_LNK 0120000
32 #define GRUB_JFS_FILETYPE_DIR 0040000
34 #define GRUB_JFS_SBLOCK 64
35 #define GRUB_JFS_AGGR_INODE 2
36 #define GRUB_JFS_FS1_INODE_BLK 104
38 #define GRUB_JFS_TREE_LEAF 2
40 struct grub_jfs_sblock
42 /* The magic for JFS. It should contain the string "JFS1". */
43 grub_uint8_t magic
[4];
44 grub_uint32_t version
;
45 grub_uint64_t ag_size
;
47 /* The size of a filesystem block in bytes. XXX: currently only
50 grub_uint16_t log2_blksz
;
52 grub_uint8_t unused
[71];
53 grub_uint8_t volname
[11];
54 grub_uint8_t unused2
[32];
55 grub_uint8_t uuid
[16];
58 struct grub_jfs_extent
60 /* The length of the extent in filesystem blocks. */
64 /* The physical offset of the first block on the disk. */
67 } __attribute__ ((packed
));
71 grub_uint8_t unused
[3072];
72 struct grub_jfs_extent inodes
[128];
73 } __attribute__ ((packed
));
76 /* The head of the tree used to find extents. */
77 struct grub_jfs_treehead
87 grub_uint8_t unused2
[10];
88 } __attribute__ ((packed
));
90 /* A node in the extent tree. */
91 struct grub_jfs_tree_extent
96 /* The offset is the key used to lookup an extent. */
98 grub_uint32_t offset2
;
100 struct grub_jfs_extent extent
;
101 } __attribute__ ((packed
));
103 /* The tree of directory entries. */
104 struct grub_jfs_tree_dir
106 /* Pointers to the previous and next tree headers of other nodes on
113 /* The amount of dirents in this node. */
115 grub_uint8_t freecnt
;
116 grub_uint8_t freelist
;
117 grub_uint8_t maxslot
;
119 /* The location of the sorted array of pointers to dirents. */
121 grub_uint8_t unused
[10];
122 } __attribute__ ((packed
));
124 /* An internal node in the dirents tree. */
125 struct grub_jfs_internal_dirent
127 struct grub_jfs_extent ex
;
130 grub_uint16_t namepart
[11];
131 } __attribute__ ((packed
));
133 /* A leaf node in the dirents tree. */
134 struct grub_jfs_leaf_dirent
136 /* The inode for this dirent. */
140 /* The size of the name. */
142 grub_uint16_t namepart
[11];
144 } __attribute__ ((packed
));
146 /* A leaf in the dirents tree. This one is used if the previously
147 dirent was not big enough to store the name. */
148 struct grub_jfs_leaf_next_dirent
152 grub_uint16_t namepart
[15];
153 } __attribute__ ((packed
));
155 struct grub_jfs_inode
158 grub_uint32_t fileset
;
160 grub_uint8_t unused
[12];
162 grub_uint8_t unused2
[20];
164 grub_uint8_t unused3
[72];
165 grub_uint8_t unused4
[96];
169 /* The tree describing the extents of the file. */
170 struct __attribute__ ((packed
))
172 struct grub_jfs_treehead tree
;
173 struct grub_jfs_tree_extent extents
[16];
177 /* The tree describing the dirents. */
180 grub_uint8_t unused
[16];
183 /* Amount of dirents in this node. */
185 grub_uint8_t freecnt
;
186 grub_uint8_t freelist
;
187 grub_uint32_t idotdot
;
188 grub_uint8_t sorted
[8];
190 struct grub_jfs_leaf_dirent dirents
[8];
191 } dir
__attribute__ ((packed
));
195 grub_uint8_t unused
[32];
196 grub_uint8_t path
[128];
198 } __attribute__ ((packed
));
199 } __attribute__ ((packed
));
203 struct grub_jfs_sblock sblock
;
205 struct grub_jfs_inode fileset
;
206 struct grub_jfs_inode currinode
;
209 } __attribute__ ((packed
));
211 struct grub_jfs_diropen
216 struct grub_jfs_tree_dir header
;
217 struct grub_jfs_leaf_dirent dirent
[0];
218 struct grub_jfs_leaf_next_dirent next_dirent
[0];
220 } *dirpage
__attribute__ ((packed
));
221 struct grub_jfs_data
*data
;
222 struct grub_jfs_inode
*inode
;
225 struct grub_jfs_leaf_dirent
*leaf
;
226 struct grub_jfs_leaf_next_dirent
*next_leaf
;
228 /* The filename and inode of the last read dirent. */
231 } __attribute__ ((packed
));
234 static grub_dl_t my_mod
;
236 static grub_err_t
grub_jfs_lookup_symlink (struct grub_jfs_data
*data
, int ino
);
238 /* Get the block number for the block BLK in the node INODE in the
239 mounted filesystem DATA. */
241 grub_jfs_blkno (struct grub_jfs_data
*data
, struct grub_jfs_inode
*inode
,
244 auto int getblk (struct grub_jfs_treehead
*treehead
,
245 struct grub_jfs_tree_extent
*extents
);
247 int getblk (struct grub_jfs_treehead
*treehead
,
248 struct grub_jfs_tree_extent
*extents
)
253 for (i
= 0; i
< grub_le_to_cpu16 (treehead
->count
) - 2; i
++)
255 if (treehead
->flags
& GRUB_JFS_TREE_LEAF
)
257 /* Read the leafnode. */
258 if (grub_le_to_cpu32 (extents
[i
].offset2
) <= blk
259 && ((grub_le_to_cpu16 (extents
[i
].extent
.length
))
260 + (extents
[i
].extent
.length2
<< 8)
261 + grub_le_to_cpu32 (extents
[i
].offset2
)) > blk
)
262 return (blk
- grub_le_to_cpu32 (extents
[i
].offset2
)
263 + grub_le_to_cpu32 (extents
[i
].extent
.blk2
));
266 if (blk
>= grub_le_to_cpu32 (extents
[i
].offset2
))
274 struct grub_jfs_treehead treehead
;
275 struct grub_jfs_tree_extent extents
[254];
278 if (grub_disk_read (data
->disk
,
279 grub_le_to_cpu32 (extents
[found
].extent
.blk2
)
280 << (grub_le_to_cpu16 (data
->sblock
.log2_blksz
)
281 - GRUB_DISK_SECTOR_BITS
), 0,
282 sizeof (tree
), (char *) &tree
))
285 return getblk (&tree
.treehead
, &tree
.extents
[0]);
291 return getblk (&inode
->file
.tree
, &inode
->file
.extents
[0]);
296 grub_jfs_read_inode (struct grub_jfs_data
*data
, int ino
,
297 struct grub_jfs_inode
*inode
)
299 struct grub_jfs_iag iag
;
300 int iagnum
= ino
/ 4096;
301 int inoext
= (ino
% 4096) / 32;
302 int inonum
= (ino
% 4096) % 32;
303 grub_uint32_t iagblk
;
304 grub_uint32_t inoblk
;
306 iagblk
= grub_jfs_blkno (data
, &data
->fileset
, iagnum
+ 1);
310 /* Read in the IAG. */
311 if (grub_disk_read (data
->disk
,
312 iagblk
<< (grub_le_to_cpu16 (data
->sblock
.log2_blksz
)
313 - GRUB_DISK_SECTOR_BITS
), 0,
314 sizeof (struct grub_jfs_iag
), &iag
))
317 inoblk
= grub_le_to_cpu32 (iag
.inodes
[inoext
].blk2
);
318 inoblk
<<= (grub_le_to_cpu16 (data
->sblock
.log2_blksz
)
319 - GRUB_DISK_SECTOR_BITS
);
322 if (grub_disk_read (data
->disk
, inoblk
, 0,
323 sizeof (struct grub_jfs_inode
), inode
))
330 static struct grub_jfs_data
*
331 grub_jfs_mount (grub_disk_t disk
)
333 struct grub_jfs_data
*data
= 0;
335 data
= grub_malloc (sizeof (struct grub_jfs_data
));
339 /* Read the superblock. */
340 if (grub_disk_read (disk
, GRUB_JFS_SBLOCK
, 0,
341 sizeof (struct grub_jfs_sblock
), &data
->sblock
))
344 if (grub_strncmp ((char *) (data
->sblock
.magic
), "JFS1", 4))
346 grub_error (GRUB_ERR_BAD_FS
, "not a jfs filesystem");
354 /* Read the inode of the first fileset. */
355 if (grub_disk_read (data
->disk
, GRUB_JFS_FS1_INODE_BLK
, 0,
356 sizeof (struct grub_jfs_inode
), &data
->fileset
))
364 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
365 grub_error (GRUB_ERR_BAD_FS
, "not a jfs filesystem");
371 static struct grub_jfs_diropen
*
372 grub_jfs_opendir (struct grub_jfs_data
*data
, struct grub_jfs_inode
*inode
)
374 struct grub_jfs_internal_dirent
*de
;
375 struct grub_jfs_diropen
*diro
;
378 de
= (struct grub_jfs_internal_dirent
*) inode
->dir
.dirents
;
380 if (!((grub_le_to_cpu32 (inode
->mode
)
381 & GRUB_JFS_FILETYPE_MASK
) == GRUB_JFS_FILETYPE_DIR
))
383 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
387 diro
= grub_zalloc (sizeof (struct grub_jfs_diropen
));
394 /* Check if the entire tree is contained within the inode. */
395 if (inode
->file
.tree
.flags
& GRUB_JFS_TREE_LEAF
)
397 diro
->leaf
= inode
->dir
.dirents
;
398 diro
->next_leaf
= (struct grub_jfs_leaf_next_dirent
*) de
;
399 diro
->sorted
= (char *) (inode
->dir
.header
.sorted
);
400 diro
->count
= inode
->dir
.header
.count
;
405 diro
->dirpage
= grub_malloc (grub_le_to_cpu32 (data
->sblock
.blksz
));
412 blk
= grub_le_to_cpu32 (de
[inode
->dir
.header
.sorted
[0]].ex
.blk2
);
413 blk
<<= (grub_le_to_cpu16 (data
->sblock
.log2_blksz
) - GRUB_DISK_SECTOR_BITS
);
415 /* Read in the nodes until we are on the leaf node level. */
419 if (grub_disk_read (data
->disk
, blk
, 0,
420 grub_le_to_cpu32 (data
->sblock
.blksz
),
421 diro
->dirpage
->sorted
))
423 grub_free (diro
->dirpage
);
428 de
= (struct grub_jfs_internal_dirent
*) diro
->dirpage
->dirent
;
429 index
= diro
->dirpage
->sorted
[diro
->dirpage
->header
.sindex
* 32];
430 blk
= (grub_le_to_cpu32 (de
[index
].ex
.blk2
)
431 << (grub_le_to_cpu16 (data
->sblock
.log2_blksz
)
432 - GRUB_DISK_SECTOR_BITS
));
433 } while (!(diro
->dirpage
->header
.flags
& GRUB_JFS_TREE_LEAF
));
435 diro
->leaf
= diro
->dirpage
->dirent
;
436 diro
->next_leaf
= diro
->dirpage
->next_dirent
;
437 diro
->sorted
= &diro
->dirpage
->sorted
[diro
->dirpage
->header
.sindex
* 32];
438 diro
->count
= diro
->dirpage
->header
.count
;
445 grub_jfs_closedir (struct grub_jfs_diropen
*diro
)
449 grub_free (diro
->dirpage
);
454 /* Read in the next dirent from the directory described by DIRO. */
456 grub_jfs_getent (struct grub_jfs_diropen
*diro
)
459 struct grub_jfs_leaf_dirent
*leaf
;
460 struct grub_jfs_leaf_next_dirent
*next_leaf
;
463 grub_uint16_t filename
[255];
465 auto void addstr (grub_uint16_t
*uname
, int ulen
);
467 /* Add the unicode string to the utf16 filename buffer. */
468 void addstr (grub_uint16_t
*name
, int ulen
)
471 filename
[strpos
++] = *(name
++);
474 /* The last node, read in more. */
475 if (diro
->index
== diro
->count
)
479 /* If the inode contains the entry tree or if this was the last
480 node, there is nothing to read. */
481 if ((diro
->inode
->file
.tree
.flags
& GRUB_JFS_TREE_LEAF
)
482 || !grub_le_to_cpu64 (diro
->dirpage
->header
.nextb
))
483 return GRUB_ERR_OUT_OF_RANGE
;
485 next
= grub_le_to_cpu64 (diro
->dirpage
->header
.nextb
);
486 next
<<= (grub_le_to_cpu16 (diro
->data
->sblock
.log2_blksz
)
487 - GRUB_DISK_SECTOR_BITS
);
489 if (grub_disk_read (diro
->data
->disk
, next
, 0,
490 grub_le_to_cpu32 (diro
->data
->sblock
.blksz
),
491 diro
->dirpage
->sorted
))
494 diro
->leaf
= diro
->dirpage
->dirent
;
495 diro
->next_leaf
= diro
->dirpage
->next_dirent
;
496 diro
->sorted
= &diro
->dirpage
->sorted
[diro
->dirpage
->header
.sindex
* 32];
497 diro
->count
= diro
->dirpage
->header
.count
;
501 leaf
= &diro
->leaf
[(int) diro
->sorted
[diro
->index
]];
502 next_leaf
= &diro
->next_leaf
[diro
->index
];
508 return grub_jfs_getent (diro
);
511 addstr (leaf
->namepart
, len
< 11 ? len
: 11);
512 diro
->ino
= grub_le_to_cpu32 (leaf
->inode
);
515 /* Move down to the leaf level. */
516 nextent
= leaf
->next
;
517 if (leaf
->next
!= 255)
520 next_leaf
= &diro
->next_leaf
[nextent
];
521 addstr (next_leaf
->namepart
, len
< 15 ? len
: 15 );
524 nextent
= next_leaf
->next
;
525 } while (next_leaf
->next
!= 255 && len
> 0);
529 /* Convert the temporary UTF16 filename to UTF8. */
530 *grub_utf16_to_utf8 ((grub_uint8_t
*) (diro
->name
), filename
, strpos
) = '\0';
536 /* Read LEN bytes from the file described by DATA starting with byte
537 POS. Return the amount of read bytes in READ. */
539 grub_jfs_read_file (struct grub_jfs_data
*data
,
540 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
541 unsigned offset
, unsigned length
),
542 int pos
, grub_size_t len
, char *buf
)
547 blockcnt
= ((len
+ pos
+ grub_le_to_cpu32 (data
->sblock
.blksz
) - 1)
548 / grub_le_to_cpu32 (data
->sblock
.blksz
));
550 for (i
= pos
/ grub_le_to_cpu32 (data
->sblock
.blksz
); i
< blockcnt
; i
++)
553 int blockoff
= pos
% grub_le_to_cpu32 (data
->sblock
.blksz
);
554 int blockend
= grub_le_to_cpu32 (data
->sblock
.blksz
);
558 blknr
= grub_jfs_blkno (data
, &data
->currinode
, i
);
563 if (i
== blockcnt
- 1)
565 blockend
= (len
+ pos
) % grub_le_to_cpu32 (data
->sblock
.blksz
);
568 blockend
= grub_le_to_cpu32 (data
->sblock
.blksz
);
572 if (i
== (pos
/ (int) grub_le_to_cpu32 (data
->sblock
.blksz
)))
574 skipfirst
= blockoff
;
575 blockend
-= skipfirst
;
578 data
->disk
->read_hook
= read_hook
;
579 grub_disk_read (data
->disk
,
580 blknr
<< (grub_le_to_cpu16 (data
->sblock
.log2_blksz
)
581 - GRUB_DISK_SECTOR_BITS
),
582 skipfirst
, blockend
, buf
);
584 data
->disk
->read_hook
= 0;
588 buf
+= grub_le_to_cpu32 (data
->sblock
.blksz
) - skipfirst
;
595 /* Find the file with the pathname PATH on the filesystem described by
598 grub_jfs_find_file (struct grub_jfs_data
*data
, const char *path
)
600 char fpath
[grub_strlen (path
)];
603 unsigned int pos
= 0;
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
);
669 next
= grub_strchr (name
, '/');
676 /* Open this directory for reading dirents. */
677 diro
= grub_jfs_opendir (data
, &data
->currinode
);
685 grub_jfs_closedir (diro
);
686 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
692 grub_jfs_lookup_symlink (struct grub_jfs_data
*data
, int ino
)
694 int size
= grub_le_to_cpu64 (data
->currinode
.size
);
695 char symlink
[size
+ 1];
697 if (++data
->linknest
> GRUB_JFS_MAX_SYMLNK_CNT
)
698 return grub_error (GRUB_ERR_SYMLINK_LOOP
, "too deep nesting of symlinks");
701 grub_strncpy (symlink
, (char *) (data
->currinode
.symlink
.path
), 128);
702 else if (grub_jfs_read_file (data
, 0, 0, size
, symlink
) < 0)
705 symlink
[size
] = '\0';
707 /* The symlink is an absolute path, go back to the root inode. */
708 if (symlink
[0] == '/')
711 /* Now load in the old inode. */
712 if (grub_jfs_read_inode (data
, ino
, &data
->currinode
))
715 grub_jfs_find_file (data
, symlink
);
717 grub_error (grub_errno
, "Can not follow symlink `%s'.", symlink
);
724 grub_jfs_dir (grub_device_t device
, const char *path
,
725 int (*hook
) (const char *filename
,
726 const struct grub_dirhook_info
*info
))
728 struct grub_jfs_data
*data
= 0;
729 struct grub_jfs_diropen
*diro
= 0;
731 grub_dl_ref (my_mod
);
733 data
= grub_jfs_mount (device
->disk
);
737 if (grub_jfs_find_file (data
, path
))
740 diro
= grub_jfs_opendir (data
, &data
->currinode
);
744 /* Iterate over the dirents in the directory that was found. */
745 while (grub_jfs_getent (diro
) != GRUB_ERR_OUT_OF_RANGE
)
747 struct grub_jfs_inode inode
;
748 struct grub_dirhook_info info
;
749 grub_memset (&info
, 0, sizeof (info
));
751 if (grub_jfs_read_inode (data
, diro
->ino
, &inode
))
754 info
.dir
= (grub_le_to_cpu32 (inode
.mode
)
755 & GRUB_JFS_FILETYPE_MASK
) == GRUB_JFS_FILETYPE_DIR
;
756 if (hook (diro
->name
, &info
))
760 /* XXX: GRUB_ERR_OUT_OF_RANGE is used for the last dirent. */
761 if (grub_errno
== GRUB_ERR_OUT_OF_RANGE
)
765 grub_jfs_closedir (diro
);
768 grub_dl_unref (my_mod
);
774 /* Open a file named NAME and initialize FILE. */
776 grub_jfs_open (struct grub_file
*file
, const char *name
)
778 struct grub_jfs_data
*data
;
780 grub_dl_ref (my_mod
);
782 data
= grub_jfs_mount (file
->device
->disk
);
786 grub_jfs_find_file (data
, name
);
790 /* It is only possible for open regular files. */
791 if (! ((grub_le_to_cpu32 (data
->currinode
.mode
)
792 & GRUB_JFS_FILETYPE_MASK
) == GRUB_JFS_FILETYPE_REG
))
794 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a regular file");
799 file
->size
= grub_le_to_cpu64 (data
->currinode
.size
);
805 grub_dl_unref (my_mod
);
814 grub_jfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
816 struct grub_jfs_data
*data
=
817 (struct grub_jfs_data
*) file
->data
;
819 return grub_jfs_read_file (data
, file
->read_hook
, file
->offset
, len
, buf
);
824 grub_jfs_close (grub_file_t file
)
826 grub_free (file
->data
);
828 grub_dl_unref (my_mod
);
830 return GRUB_ERR_NONE
;
834 grub_jfs_uuid (grub_device_t device
, char **uuid
)
836 struct grub_jfs_data
*data
;
837 grub_disk_t disk
= device
->disk
;
839 grub_dl_ref (my_mod
);
841 data
= grub_jfs_mount (disk
);
844 *uuid
= grub_malloc (40 + sizeof ('\0'));
846 grub_sprintf (*uuid
, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
847 data
->sblock
.uuid
[0], data
->sblock
.uuid
[1],
848 data
->sblock
.uuid
[2], data
->sblock
.uuid
[3],
849 data
->sblock
.uuid
[4], data
->sblock
.uuid
[5],
850 data
->sblock
.uuid
[6], data
->sblock
.uuid
[7],
851 data
->sblock
.uuid
[8], data
->sblock
.uuid
[9],
852 data
->sblock
.uuid
[10], data
->sblock
.uuid
[11],
853 data
->sblock
.uuid
[12], data
->sblock
.uuid
[13],
854 data
->sblock
.uuid
[14], data
->sblock
.uuid
[15]);
859 grub_dl_unref (my_mod
);
867 grub_jfs_label (grub_device_t device
, char **label
)
869 struct grub_jfs_data
*data
;
870 data
= grub_jfs_mount (device
->disk
);
873 *label
= grub_strndup ((char *) (data
->sblock
.volname
), 11);
881 static struct grub_fs grub_jfs_fs
=
885 .open
= grub_jfs_open
,
886 .read
= grub_jfs_read
,
887 .close
= grub_jfs_close
,
888 .label
= grub_jfs_label
,
889 .uuid
= grub_jfs_uuid
,
895 grub_fs_register (&grub_jfs_fs
);
901 grub_fs_unregister (&grub_jfs_fs
);