From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with archive (Exim 4.33) id 1BzEJO-0004Y3-QF for mharc-grub-devel@gnu.org; Mon, 23 Aug 2004 08:52:34 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.33) id 1BzEJM-0004Xx-VR for grub-devel@gnu.org; Mon, 23 Aug 2004 08:52:33 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.33) id 1BzEJL-0004Xl-KQ for grub-devel@gnu.org; Mon, 23 Aug 2004 08:52:32 -0400 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1BzEJL-0004Xi-HQ for grub-devel@gnu.org; Mon, 23 Aug 2004 08:52:31 -0400 Received: from [145.74.66.11] (helo=mail-cn.han.nl) by monty-python.gnu.org with esmtp (Exim 4.34) id 1BzEEk-0006x5-O0 for grub-devel@gnu.org; Mon, 23 Aug 2004 08:47:47 -0400 Received: from localhost (charlie.han.nl [145.74.66.9]) by mail-cn.han.nl (Postfix) with ESMTP id 1283580E0 for ; Mon, 23 Aug 2004 14:47:45 +0200 (CEST) Received: from mail-cn.han.nl ([145.74.66.11]) by localhost (charlie.han.nl [145.74.66.9]) (amavisd-new, port 10024) with ESMTP id 12090-03 for ; Mon, 23 Aug 2004 14:47:41 +0200 (CEST) Received: from mail1.han.nl (mail1.han.nl [145.74.103.11]) by mail-cn.han.nl (Postfix) with ESMTP id C27DB8080 for ; Mon, 23 Aug 2004 14:47:41 +0200 (CEST) Received: from marco.marco-g.com (a82-92-27-129.adsl.xs4all.nl [82.92.27.129]) by mail1.han.nl (Postfix) with ESMTP id 75C1CC046 for ; Mon, 23 Aug 2004 13:47:42 +0200 (CEST) Mail-Copies-To: metgerards@student.han.nl To: grub-devel@gnu.org From: Marco Gerards Date: Mon, 23 Aug 2004 12:48:09 +0000 Message-ID: <87vffat3zq.fsf@marco.marco-g.com> User-Agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Virus-Scanned: by amavisd-new@vscan-cn.han.nl Subject: JFS support (PATCH) and filesystem improvements X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: The development of GRUB 2 List-Id: The development of GRUB 2 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 23 Aug 2004 12:52:33 -0000 Hi, Here is a patch to add JFS support to GRUB 2. It has all features you might expect from it, except support for journaling and HUGE filesystems. I was not able to test this on filesystems with a blocksize other than 4096 bytes, so that might not work either. Another thing I have not implemented is looking up files using the B+ tree, if that is really a big problem for someone, please tell me. I have tested this implementation with a huge directory and I think the slowdown is less important than the amount of code I have to write for this. Because filenames are stored in UTF-16, I had to do a conversion to UTF-8/ Okuji wrote some code for this for fat.c. I have moved this to kern/misc.c so ufs.c can use this as well. Please read the code or test it and send in suggestions for improvements or bug reports. If I do not hear anything before Friday I will commit this patch Friday. The next filesystem on my todo is iso9660fs. I moved it up on my todo because we already have CDROM support (for free! :)) for the PPC port. Before I will do that, I want to have a close look at the filesystems currently implemented. There are some (minor) mistakes I made because of a lack of experience, I want to fix those first. And there is a lot of code duplication for some tasks. The two main functions that are more or less duplicated every time is the function to read in a file and the one to lookup files by the path. The best way to implement this properly is writing some filesystem helper functions (kern/fshelp.c or so). Most filesystems can use those functions to avoid code duplication and thus bugs. Another way to solve this problem is changing the interfaces for the filesystems. But I do not want to do that because I like the way things are, filesystems are in control and know what is best (some filesystems can lookup files in B+ trees, for example). If someone does not agree with this improvement, please tell me. Thanks, Marco 2004-08-23 Marco Gerards Add support for the JFS filesystem. * fs/jfs.c: New file. * include/grub/fs.h (grub_hfs_init): New prototype. (grub_hfs_fini): New prototype. (grub_jfs_init): New prototype. (grub_jfs_fini): New prototype. * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/jfs.c. (grub_emu_SOURCES): Likewise. (pkgdata_MODULES): Add jfs.mod. (jfs_mod_SOURCES): New variable. (jfs_mod_CFLAGS): Likewise. * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add fs.jfs.c. (grubof_SOURCES): Likewise. * util/grub-emu.c (main): Initialize and deinitialize JFS support. * fs/fat.c (grub_fat_find_dir): Convert the filename little endian to the host endian. (grub_fat_utf16_to_utf8): Move function from there... * kern/misc.c (grub_utf16_to_utf8): ...to here. Do not convert the endianess of the source string anymore. * include/grub/misc.h (grub_utf16_to_utf8): New prototype. Index: conf/i386-pc.rmk =================================================================== RCS file: /cvsroot/grub/grub2/conf/i386-pc.rmk,v retrieving revision 1.16 diff -u -p -u -p -r1.16 i386-pc.rmk --- conf/i386-pc.rmk 18 Aug 2004 09:00:01 -0000 1.16 +++ conf/i386-pc.rmk 23 Aug 2004 12:23:55 -0000 @@ -61,7 +61,7 @@ grub_mkimage_LDFLAGS = -llzo grub_setup_SOURCES = util/i386/pc/grub-setup.c util/i386/pc/biosdisk.c \ util/misc.c util/i386/pc/getroot.c kern/device.c kern/disk.c \ kern/err.c kern/misc.c disk/i386/pc/partition.c fs/fat.c fs/ext2.c \ - fs/ufs.c fs/minix.c fs/hfs.c kern/file.c kern/fs.c kern/env.c + fs/ufs.c fs/minix.c fs/hfs.c fs/jfs.c kern/file.c kern/fs.c kern/env.c # For grub grub_emu_SOURCES = kern/main.c kern/device.c \ @@ -69,7 +69,7 @@ grub_emu_SOURCES = kern/main.c kern/devi kern/misc.c kern/loader.c kern/rescue.c kern/term.c \ disk/i386/pc/partition.c kern/env.c commands/ls.c \ commands/terminal.c commands/boot.c commands/cmp.c commands/cat.c \ - util/i386/pc/biosdisk.c fs/fat.c fs/ext2.c fs/ufs.c fs/minix.c fs/hfs.c \ + util/i386/pc/biosdisk.c fs/fat.c fs/ext2.c fs/ufs.c fs/minix.c fs/hfs.c fs/jfs.c\ normal/cmdline.c normal/command.c normal/main.c normal/menu.c normal/arg.c \ util/console.c util/grub-emu.c util/misc.c util/i386/pc/getroot.c grub_emu_LDFLAGS = -lncurses @@ -79,7 +79,7 @@ genmoddep_SOURCES = util/genmoddep.c # Modules. pkgdata_MODULES = _chain.mod _linux.mod fat.mod ufs.mod ext2.mod minix.mod \ - hfs.mod normal.mod hello.mod vga.mod font.mod _multiboot.mod ls.mod \ + hfs.mod jfs.mod normal.mod hello.mod vga.mod font.mod _multiboot.mod ls.mod \ boot.mod cmp.mod cat.mod terminal.mod # For _chain.mod. @@ -106,6 +106,10 @@ minix_mod_CFLAGS = $(COMMON_CFLAGS) hfs_mod_SOURCES = fs/hfs.c hfs_mod_CFLAGS = $(COMMON_CFLAGS) +# For jfs.mod. +jfs_mod_SOURCES = fs/jfs.c +jfs_mod_CFLAGS = $(COMMON_CFLAGS) + # For _linux.mod. _linux_mod_SOURCES = loader/i386/pc/linux.c _linux_mod_CFLAGS = $(COMMON_CFLAGS) Index: conf/powerpc-ieee1275.rmk =================================================================== RCS file: /cvsroot/grub/grub2/conf/powerpc-ieee1275.rmk,v retrieving revision 1.6 diff -u -p -u -p -r1.6 powerpc-ieee1275.rmk --- conf/powerpc-ieee1275.rmk 18 Aug 2004 09:00:01 -0000 1.6 +++ conf/powerpc-ieee1275.rmk 23 Aug 2004 12:23:55 -0000 @@ -26,7 +26,7 @@ grub_emu_SOURCES = kern/main.c kern/devi kern/misc.c kern/loader.c kern/rescue.c kern/term.c \ disk/powerpc/ieee1275/partition.c \ util/i386/pc/biosdisk.c fs/fat.c fs/ext2.c fs/ufs.c fs/minix.c fs/hfs.c \ - normal/cmdline.c normal/command.c normal/main.c normal/menu.c \ + fs/jfs.c normal/cmdline.c normal/command.c normal/main.c normal/menu.c \ normal/arg.c \ util/console.c util/grub-emu.c util/misc.c util/i386/pc/getroot.c \ kern/env.c commands/ls.c \ @@ -39,7 +39,7 @@ grubof_SOURCES = boot/powerpc/ieee1275/c kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \ kern/powerpc/ieee1275/init.c term/powerpc/ieee1275/ofconsole.c \ kern/powerpc/ieee1275/openfw.c fs/ext2.c fs/ufs.c fs/minix.c fs/hfs.c \ - normal/cmdline.c normal/command.c normal/main.c normal/menu.c \ + fs/jfs.c normal/cmdline.c normal/command.c normal/main.c normal/menu.c \ disk/powerpc/ieee1275/ofdisk.c disk/powerpc/ieee1275/partition.c \ kern/env.c normal/arg.c loader/powerpc/ieee1275/linux.c \ loader/powerpc/ieee1275/linux_normal.c commands/boot.c Index: fs/fat.c =================================================================== RCS file: /cvsroot/grub/grub2/fs/fat.c,v retrieving revision 1.8 diff -u -p -u -p -r1.8 fat.c --- fs/fat.c 4 Apr 2004 13:46:00 -0000 1.8 +++ fs/fat.c 23 Aug 2004 12:23:55 -0000 @@ -304,68 +304,6 @@ grub_fat_mount (grub_disk_t disk) return 0; } -/* Convert UTF-16 (little endian) to UTF8. */ -static grub_uint8_t * -grub_fat_utf16_to_utf8 (grub_uint8_t *dest, grub_uint16_t *src, - grub_size_t size) -{ - grub_uint32_t code_high = 0; - - while (size--) - { - grub_uint32_t code = grub_le_to_cpu16 (*src++); - - if (code_high) - { - if (code >= 0xDC00 && code <= 0xDFFF) - { - /* Surrogate pair. */ - code = ((code_high - 0xD800) << 12) + (code - 0xDC00) + 0x10000; - - *dest++ = (code >> 18) | 0xF0; - *dest++ = ((code >> 12) & 0x3F) | 0x80; - *dest++ = ((code >> 6) & 0x3F) | 0x80; - *dest++ = (code & 0x3F) | 0x80; - } - else - { - /* Error... */ - *dest++ = '?'; - } - - code_high = 0; - } - else - { - if (code <= 0x007F) - *dest++ = code; - else if (code <= 0x07FF) - { - *dest++ = (code >> 6) | 0xC0; - *dest++ = (code & 0x3F) | 0x80; - } - else if (code >= 0xD800 && code <= 0xDBFF) - { - code_high = code; - continue; - } - else if (code >= 0xDC00 && code <= 0xDFFF) - { - /* Error... */ - *dest++ = '?'; - } - else - { - *dest++ = (code >> 16) | 0xE0; - *dest++ = ((code >> 12) & 0x3F) | 0x80; - *dest++ = (code & 0x3F) | 0x80; - } - } - } - - return dest; -} - static grub_ssize_t grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data, void (*read_hook) (unsigned long sector, @@ -610,7 +548,12 @@ grub_fat_find_dir (grub_disk_t disk, str if (sum == checksum) { - *grub_fat_utf16_to_utf8 (filename, unibuf, slots * 13) = '\0'; + int u; + + for (u = 0; u < slots * 13; u++) + unibuf[u] = grub_le_to_cpu16 (unibuf[u]); + + *grub_utf16_to_utf8 (filename, unibuf, slots * 13) = '\0'; if (*dirname == '\0' && call_hook) { Index: fs/jfs.c =================================================================== RCS file: fs/jfs.c diff -N fs/jfs.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fs/jfs.c 23 Aug 2004 12:23:55 -0000 @@ -0,0 +1,892 @@ +/* jfs.c - JFS. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define GRUB_JFS_MAX_SYMLNK_CNT 8 +#define GRUB_JFS_FILETYPE_MASK 0170000 +#define GRUB_JFS_FILETYPE_REG 0100000 +#define GRUB_JFS_FILETYPE_LNK 0120000 +#define GRUB_JFS_FILETYPE_DIR 0040000 + +#define GRUB_JFS_SBLOCK 64 +#define GRUB_JFS_AGGR_INODE 2 +#define GRUB_JFS_FS1_INODE_BLK 104 + +#define GRUB_JFS_TREE_LEAF 2 + +struct grub_jfs_sblock +{ + /* The magic for JFS. It should contain the string "JFS1". */ + grub_uint8_t magic[4]; + grub_uint32_t version; + grub_uint64_t ag_size; + + /* The size of a filesystem block in bytes. XXX: currently only + 4096 was tested. */ + grub_uint32_t blksz; + grub_uint16_t log2_blksz; + + grub_uint8_t unused[71]; + grub_uint8_t volname[11]; +}; + +struct grub_jfs_extent +{ + /* The length of the extent in filesystem blocks. */ + grub_uint16_t length; + grub_uint8_t length2; + + /* The physical offset of the first block on the disk. */ + grub_uint8_t blk1; + grub_uint32_t blk2; +} __attribute__ ((packed)); + +struct grub_jfs_iag +{ + grub_uint8_t unused[3072]; + struct grub_jfs_extent inodes[128]; +} __attribute__ ((packed)); + + +/* The head of the tree used to find extents. */ +struct grub_jfs_treehead +{ + grub_uint64_t next; + grub_uint64_t prev; + + grub_uint8_t flags; + grub_uint8_t unused; + + grub_uint16_t count; + grub_uint16_t max; + grub_uint8_t unused2[10]; +} __attribute__ ((packed)); + +/* A node in the extent tree. */ +struct grub_jfs_tree_extent +{ + grub_uint8_t flags; + grub_uint16_t unused; + + /* The offset is the key used to lookup an extent. */ + grub_uint8_t offset1; + grub_uint32_t offset2; + + struct grub_jfs_extent extent; +} __attribute__ ((packed)); + +/* The tree of directory entries. */ +struct grub_jfs_tree_dir +{ + /* Pointers to the previous and next tree headers of other nodes on + this level. */ + grub_uint64_t nextb; + grub_uint64_t prevb; + + grub_uint8_t flags; + + /* The amount of dirents in this node. */ + grub_uint8_t count; + grub_uint8_t freecnt; + grub_uint8_t freelist; + grub_uint8_t maxslot; + + /* The location of the sorted array of pointers to dirents. */ + grub_uint8_t sindex; + grub_uint8_t unused[10]; +} __attribute__ ((packed)); + +/* An internal node in the dirents tree. */ +struct grub_jfs_internal_dirent +{ + struct grub_jfs_extent ex; + grub_uint8_t next; + grub_uint8_t len; + grub_uint16_t namepart[11]; +} __attribute__ ((packed)); + +/* A leaf node in the dirents tree. */ +struct grub_jfs_leaf_dirent +{ + /* The inode for this dirent. */ + grub_uint32_t inode; + grub_uint8_t next; + + /* The size of the name. */ + grub_uint8_t len; + grub_uint16_t namepart[11]; + grub_uint32_t index; +} __attribute__ ((packed)); + +/* A leaf in the dirents tree. This one is used if the previously + dirent was not big enough to store the name. */ +struct grub_jfs_leaf_next_dirent +{ + grub_uint8_t next; + grub_uint8_t len; + grub_uint16_t namepart[15]; +} __attribute__ ((packed)); + +struct grub_jfs_inode +{ + grub_uint32_t stamp; + grub_uint32_t fileset; + grub_uint32_t inode; + grub_uint8_t unused[12]; + grub_uint64_t size; + grub_uint8_t unused2[20]; + grub_uint32_t mode; + grub_uint8_t unused3[72]; + grub_uint8_t unused4[96]; + + union + { + /* The tree describing the extents of the file. */ + struct + { + struct grub_jfs_treehead tree; + struct grub_jfs_tree_extent extents[16]; + } file __attribute__ ((packed)); + union + { + /* The tree describing the dirents. */ + struct + { + grub_uint8_t unused[16]; + grub_uint8_t flags; + + /* Amount of dirents in this node. */ + grub_uint8_t count; + grub_uint8_t freecnt; + grub_uint8_t freelist; + grub_uint32_t idotdot; + grub_uint8_t sorted[8]; + } header; + struct grub_jfs_leaf_dirent dirents[8]; + } dir __attribute__ ((packed)); + /* Fast symlink. */ + struct + { + grub_uint8_t unused[32]; + grub_uint8_t path[128]; + } symlink; + } __attribute__ ((packed)); +} __attribute__ ((packed)); + +struct grub_jfs_data +{ + struct grub_jfs_sblock sblock; + grub_disk_t disk; + struct grub_jfs_inode fileset; + struct grub_jfs_inode currinode; + int pos; + int linknest; +} __attribute__ ((packed)); + +struct grub_jfs_diropen +{ + int index; + union + { + struct grub_jfs_tree_dir header; + struct grub_jfs_leaf_dirent dirent[0]; + struct grub_jfs_leaf_next_dirent next_dirent[0]; + char sorted[0]; + } *dirpage __attribute__ ((packed)); + struct grub_jfs_data *data; + struct grub_jfs_inode *inode; + int count; + char *sorted; + struct grub_jfs_leaf_dirent *leaf; + struct grub_jfs_leaf_next_dirent *next_leaf; + + /* The filename and inode of the last read dirent. */ + char name[255]; + grub_uint32_t ino; +} __attribute__ ((packed)); + + +#ifndef GRUB_UTIL +static grub_dl_t my_mod; +#endif + +static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino); + +/* Get the block number for the block BLK in the node INODE in the + mounted filesystem DATA. */ +static int +grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode, + unsigned int blk) +{ + auto int getblk (struct grub_jfs_treehead *treehead, + struct grub_jfs_tree_extent *extents); + + int getblk (struct grub_jfs_treehead *treehead, + struct grub_jfs_tree_extent *extents) + { + int found = -1; + int i; + + for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2; i++) + { + if (treehead->flags & GRUB_JFS_TREE_LEAF) + { + /* Read the leafnode. */ + if (grub_le_to_cpu32 (extents[i].offset2) <= blk + && ((grub_le_to_cpu16 (extents[i].extent.length)) + + (extents[i].extent.length2 << 8) + + grub_le_to_cpu32 (extents[i].offset2)) > blk) + return (blk - grub_le_to_cpu32 (extents[i].offset2) + + grub_le_to_cpu32 (extents[i].extent.blk2)); + } + else + if (blk >= grub_le_to_cpu32 (extents[i].offset2)) + found = i; + } + + if (found != -1) + { + struct + { + struct grub_jfs_treehead treehead; + struct grub_jfs_tree_extent extents[254]; + } tree; + + if (grub_disk_read (data->disk, + grub_le_to_cpu32 (extents[found].extent.blk2) + << (grub_le_to_cpu16 (data->sblock.log2_blksz) + - GRUB_DISK_SECTOR_BITS), 0, + sizeof (tree), (char *) &tree)) + return -1; + + return getblk (&tree.treehead, &tree.extents[0]); + } + + return -1; + } + + return getblk (&inode->file.tree, &inode->file.extents[0]); +} + + +static grub_err_t +grub_jfs_read_inode (struct grub_jfs_data *data, int ino, + struct grub_jfs_inode *inode) +{ + struct grub_jfs_iag iag; + int iagnum = ino / 4096; + int inoext = (ino % 4096) / 32; + int inonum = (ino % 4096) % 32; + grub_uint32_t iagblk; + grub_uint32_t inoblk; + + iagblk = grub_jfs_blkno (data, &data->fileset, iagnum + 1); + if (grub_errno) + return grub_errno; + + /* Read in the IAG. */ + if (grub_disk_read (data->disk, + iagblk << (grub_le_to_cpu16 (data->sblock.log2_blksz) + - GRUB_DISK_SECTOR_BITS), 0, + sizeof (struct grub_jfs_iag), (char *) &iag)) + return grub_errno; + + inoblk = grub_le_to_cpu32 (iag.inodes[inoext].blk2); + inoblk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) + - GRUB_DISK_SECTOR_BITS); + inoblk += inonum; + + if (grub_disk_read (data->disk, inoblk, 0, + sizeof (struct grub_jfs_inode), (char *) inode)) + return grub_errno; + + return 0; +} + + +static struct grub_jfs_data * +grub_jfs_mount (grub_disk_t disk) +{ + struct grub_jfs_data *data = 0; + + data = grub_malloc (sizeof (struct grub_jfs_data)); + if (!data) + return 0; + + /* Read the superblock. */ + if (grub_disk_read (disk, GRUB_JFS_SBLOCK, 0, + sizeof (struct grub_jfs_sblock), (char *) &data->sblock)) + goto fail; + + if (grub_strncmp (data->sblock.magic, "JFS1", 4)) + { + grub_error (GRUB_ERR_BAD_FS, "not a jfs filesystem"); + goto fail; + } + + data->disk = disk; + data->pos = 0; + data->linknest = 0; + + /* Read the inode of the first fileset. */ + if (grub_disk_read (data->disk, GRUB_JFS_FS1_INODE_BLK, 0, + sizeof (struct grub_jfs_inode), (char *) &data->fileset)) + goto fail; + + return data; + + fail: + grub_free (data); + return 0; +} + + +static struct grub_jfs_diropen * +grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) +{ + struct grub_jfs_internal_dirent *de; + struct grub_jfs_diropen *diro; + int blk; + + de = (struct grub_jfs_internal_dirent *) inode->dir.dirents; + + if (!((grub_le_to_cpu32 (inode->mode) + & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); + return 0; + } + + diro = grub_malloc (sizeof (struct grub_jfs_diropen)); + if (!diro) + return 0; + + diro->index = 0; + diro->data = data; + diro->inode = inode; + + /* Check if the entire tree is contained within the inode. */ + if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF) + { + diro->leaf = inode->dir.dirents; + diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de; + diro->sorted = inode->dir.header.sorted; + diro->count = inode->dir.header.count; + diro->dirpage = 0; + + return diro; + } + + diro->dirpage = grub_malloc (grub_le_to_cpu32 (data->sblock.blksz)); + if (!diro->dirpage) + { + grub_free (diro); + return 0; + } + + blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2); + blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS); + + /* Read in the nodes until we are on the leaf node level. */ + do + { + int index; + if (grub_disk_read (data->disk, blk, 0, + grub_le_to_cpu32 (data->sblock.blksz), + diro->dirpage->sorted)) + { + grub_free (diro->dirpage); + grub_free (diro); + return 0; + } + + de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent; + index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32]; + blk = (grub_le_to_cpu32 (de[index].ex.blk2) + << (grub_le_to_cpu16 (data->sblock.log2_blksz) + - GRUB_DISK_SECTOR_BITS)); + } while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF)); + + diro->leaf = diro->dirpage->dirent; + diro->next_leaf = diro->dirpage->next_dirent; + diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32]; + diro->count = diro->dirpage->header.count; + + return diro; +} + + +static void +grub_jfs_closedir (struct grub_jfs_diropen *diro) +{ + if (!diro) + return; + grub_free (diro->dirpage); + grub_free (diro); +} + + +/* Read in the next dirent from the directory described by DIRO. */ +static grub_err_t +grub_jfs_getent (struct grub_jfs_diropen *diro) +{ + int strpos = 0; + struct grub_jfs_leaf_dirent *leaf; + struct grub_jfs_leaf_next_dirent *next_leaf; + int len; + int nextent; + grub_uint16_t filename[255]; + + auto void addstr (grub_uint16_t *uname, int ulen); + + /* Add the unicode string to the utf16 filename buffer. */ + void addstr (grub_uint16_t *name, int ulen) + { + while (ulen--) + filename[strpos++] = *(name++); + } + + /* The last node, read in more. */ + if (diro->index == diro->count) + { + unsigned int next; + + /* If the inode contains the entrie tree or if this was the last + node, there is nothing to read. */ + if ((diro->inode->file.tree.flags & GRUB_JFS_TREE_LEAF) + || !grub_le_to_cpu64 (diro->dirpage->header.nextb)) + return GRUB_ERR_OUT_OF_RANGE; + + next = grub_le_to_cpu64 (diro->dirpage->header.nextb); + next <<= (grub_le_to_cpu16 (diro->data->sblock.log2_blksz) + - GRUB_DISK_SECTOR_BITS); + + if (grub_disk_read (diro->data->disk, next, 0, + grub_le_to_cpu32 (diro->data->sblock.blksz), + diro->dirpage->sorted)) + return grub_errno; + + diro->leaf = diro->dirpage->dirent; + diro->next_leaf = diro->dirpage->next_dirent; + diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32]; + diro->count = diro->dirpage->header.count; + diro->index = 0; + } + + leaf = &diro->leaf[(int) diro->sorted[diro->index]]; + next_leaf = &diro->next_leaf[diro->index]; + + len = leaf->len; + if (!len) + { + diro->index++; + return grub_jfs_getent (diro); + } + + addstr (leaf->namepart, len < 11 ? len : 11); + diro->ino = grub_le_to_cpu32 (leaf->inode); + len -= 11; + + /* Move down to the leaf level. */ + nextent = leaf->next; + if (leaf->next != 255) + do + { + next_leaf = &diro->next_leaf[nextent]; + addstr (next_leaf->namepart, len < 15 ? len : 15 ); + + len -= 15; + nextent = next_leaf->next; + } while (next_leaf->next != 255 && len > 0); + + diro->index++; + + /* Convert the temporary UTF16 filename to UTF8. */ + *grub_utf16_to_utf8 (diro->name, filename, strpos) = '\0'; + + return 0; +} + + +/* Read LEN bytes from the file described by DATA starting with byte + POS. Return the amount of read bytes in READ. */ +static grub_ssize_t +grub_jfs_read_file (struct grub_jfs_data *data, + void (*read_hook) (unsigned long sector, + unsigned offset, unsigned length), + int pos, unsigned int len, char *buf) +{ + int i; + int blockcnt; + + /* Adjust len so it we can't read past the end of the file. */ + if (len > data->currinode.size) + len = data->currinode.size; + + blockcnt = ((len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1) + / grub_le_to_cpu32 (data->sblock.blksz)); + + for (i = pos / grub_le_to_cpu32 (data->sblock.blksz); i < blockcnt; i++) + { + int blknr; + int blockoff = pos % grub_le_to_cpu32 (data->sblock.blksz); + int blockend = grub_le_to_cpu32 (data->sblock.blksz); + + int skipfirst = 0; + + blknr = grub_jfs_blkno (data, &data->currinode, i); + if (grub_errno) + return -1; + + /* Last block. */ + if (i == blockcnt - 1) + { + blockend = (len + pos) % grub_le_to_cpu32 (data->sblock.blksz); + + if (!blockend) + blockend = grub_le_to_cpu32 (data->sblock.blksz); + } + + /* First block. */ + if (i == (pos / (int) grub_le_to_cpu32 (data->sblock.blksz))) + { + skipfirst = blockoff; + blockend -= skipfirst; + } + + data->disk->read_hook = read_hook; + grub_disk_read (data->disk, + blknr << (grub_le_to_cpu16 (data->sblock.log2_blksz) + - GRUB_DISK_SECTOR_BITS), + skipfirst, blockend, buf); + + data->disk->read_hook = 0; + if (grub_errno) + return -1; + + buf += grub_le_to_cpu32 (data->sblock.blksz) - skipfirst; + } + + return len; +} + + +/* Find the file with the pathname PATH on the filesystem described by + DATA. */ +static grub_err_t +grub_jfs_find_file (struct grub_jfs_data *data, const char *path) +{ + char fpath[grub_strlen (path)]; + char *name = fpath; + char *next; + unsigned int pos = 0; + struct grub_jfs_diropen *diro; + + grub_strncpy (fpath, path, grub_strlen (path) + 1); + + if (grub_jfs_read_inode (data, GRUB_JFS_AGGR_INODE, &data->currinode)) + return grub_errno; + + /* Skip the first slash. */ + if (name[0] == '/') + { + name++; + if (!*name) + return 0; + } + + /* Extract the actual part from the pathname. */ + next = grub_strchr (name, '/'); + if (next) + { + next[0] = '\0'; + next++; + } + + diro = grub_jfs_opendir (data, &data->currinode); + if (!diro) + return grub_errno; + + for (;;) + { + if (grub_strlen (name) == 0) + return GRUB_ERR_NONE; + + if (grub_jfs_getent (diro) == GRUB_ERR_OUT_OF_RANGE) + break; + + /* Check if the current direntry matches the current part of the + pathname. */ + if (!grub_strcmp (name, diro->name)) + { + int ino = diro->ino; + int dirino = grub_le_to_cpu32 (data->currinode.inode); + + grub_jfs_closedir (diro); + diro = 0; + + if (grub_jfs_read_inode (data, ino, &data->currinode)) + break; + + /* Check if this is a symlink. */ + if ((grub_le_to_cpu32 (data->currinode.mode) + & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_LNK) + { + grub_jfs_lookup_symlink (data, dirino); + if (grub_errno) + return grub_errno; + } + + if (!next) + return 0; + + pos = 0; + + name = next; + next = grub_strchr (name, '/'); + if (next) + { + next[0] = '\0'; + next++; + } + + /* Open this directory for reading dirents. */ + diro = grub_jfs_opendir (data, &data->currinode); + if (!diro) + return grub_errno; + + continue; + } + } + + grub_jfs_closedir (diro); + grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + return grub_errno; +} + + +static grub_err_t +grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino) +{ + int size = grub_le_to_cpu64 (data->currinode.size); + char symlink[size + 1]; + + if (++data->linknest > GRUB_JFS_MAX_SYMLNK_CNT) + return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks"); + + if (size <= 128) + grub_strncpy (symlink, data->currinode.symlink.path, 128); + else if (grub_jfs_read_file (data, 0, 0, size, symlink) < 0) + return grub_errno; + + symlink[size] = '\0'; + + /* The symlink is an absolute path, go back to the root inode. */ + if (symlink[0] == '/') + ino = 2; + + /* Now load in the old inode. */ + if (grub_jfs_read_inode (data, ino, &data->currinode)) + return grub_errno; + + grub_jfs_find_file (data, symlink); + if (grub_errno) + grub_error (grub_errno, "Can not follow symlink `%s'.", symlink); + + return grub_errno; +} + + +static grub_err_t +grub_jfs_dir (grub_device_t device, const char *path, + int (*hook) (const char *filename, int dir)) +{ + struct grub_jfs_data *data = 0; + struct grub_jfs_diropen *diro = 0; + +#ifndef GRUB_UTIL + grub_dl_ref (my_mod); +#endif + + data = grub_jfs_mount (device->disk); + if (!data) + goto fail; + + if (grub_jfs_find_file (data, path)) + goto fail; + + diro = grub_jfs_opendir (data, &data->currinode); + if (!diro) + goto fail; + + /* Iterate over the dirents in the directory that was found. */ + while (grub_jfs_getent (diro) != GRUB_ERR_OUT_OF_RANGE) + { + struct grub_jfs_inode inode; + int isdir; + + if (grub_jfs_read_inode (data, diro->ino, &inode)) + goto fail; + + isdir = (grub_le_to_cpu32 (inode.mode) + & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR; + if (hook (diro->name, isdir)) + goto fail; + } + + /* XXX: GRUB_ERR_OUT_OF_RANGE is used for the last dirent. */ + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + grub_errno = 0; + + fail: + grub_jfs_closedir (diro); + grub_free (data); + +#ifndef GRUB_UTIL + grub_dl_unref (my_mod); +#endif + + return grub_errno; +} + + +/* Open a file named NAME and initialize FILE. */ +static grub_err_t +grub_jfs_open (struct grub_file *file, const char *name) +{ + struct grub_jfs_data *data; + +#ifndef GRUB_UTIL + grub_dl_ref (my_mod); +#endif + + data = grub_jfs_mount (file->device->disk); + if (!data) + goto fail; + + grub_jfs_find_file (data, name); + if (grub_errno) + goto fail; + + /* It is only possible for open regular files. */ + if (! ((grub_le_to_cpu32 (data->currinode.mode) + & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_REG)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); + goto fail; + } + + file->data = data; + file->size = grub_le_to_cpu64 (data->currinode.size); + + return 0; + + fail: + +#ifndef GRUB_UTIL + grub_dl_unref (my_mod); +#endif + + grub_free (data); + + return grub_errno;; +} + + +static grub_ssize_t +grub_jfs_read (grub_file_t file, char *buf, grub_ssize_t len) +{ + struct grub_jfs_data *data = + (struct grub_jfs_data *) file->data; + + return grub_jfs_read_file (data, file->read_hook, file->offset, len, buf); +} + + +static grub_err_t +grub_jfs_close (grub_file_t file) +{ + grub_free (file->data); + +#ifndef GRUB_UTIL + grub_dl_unref (my_mod); +#endif + + return GRUB_ERR_NONE; +} + + +static grub_err_t +grub_jfs_label (grub_device_t device, char **label) +{ + struct grub_jfs_data *data; + data = grub_jfs_mount (device->disk); + + if (data) + *label = grub_strndup (data->sblock.volname, 11); + else + *label = 0; + + return grub_errno; +} + + +static struct grub_fs grub_jfs_fs = + { + .name = "jfs", + .dir = grub_jfs_dir, + .open = grub_jfs_open, + .read = grub_jfs_read, + .close = grub_jfs_close, + .label = grub_jfs_label, + .next = 0 + }; + +#ifdef GRUB_UTIL +void +grub_jfs_init (void) +{ + grub_fs_register (&grub_jfs_fs); +} + +void +grub_jfs_fini (void) +{ + grub_fs_unregister (&grub_jfs_fs); +} +#else /* ! GRUB_UTIL */ +GRUB_MOD_INIT +{ + grub_fs_register (&grub_jfs_fs); + my_mod = mod; +} + +GRUB_MOD_FINI +{ + grub_fs_unregister (&grub_jfs_fs); +} +#endif /* ! GRUB_UTIL */ Index: include/grub/fs.h =================================================================== RCS file: /cvsroot/grub/grub2/include/grub/fs.h,v retrieving revision 1.7 diff -u -p -u -p -r1.7 fs.h --- include/grub/fs.h 24 May 2004 21:32:21 -0000 1.7 +++ include/grub/fs.h 23 Aug 2004 12:23:55 -0000 @@ -74,6 +74,10 @@ void grub_ufs_init (void); void grub_ufs_fini (void); void grub_minix_init (void); void grub_minix_fini (void); +void grub_hfs_init (void); +void grub_hfs_fini (void); +void grub_jfs_init (void); +void grub_jfs_fini (void); #endif /* GRUB_UTIL */ #endif /* ! GRUB_FS_HEADER */ Index: include/grub/misc.h =================================================================== RCS file: /cvsroot/grub/grub2/include/grub/misc.h,v retrieving revision 1.9 diff -u -p -u -p -r1.9 misc.h --- include/grub/misc.h 18 Aug 2004 09:00:01 -0000 1.9 +++ include/grub/misc.h 23 Aug 2004 12:23:56 -0000 @@ -58,6 +58,10 @@ int EXPORT_FUNC(grub_vprintf) (const cha int EXPORT_FUNC(grub_sprintf) (char *str, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); int EXPORT_FUNC(grub_vsprintf) (char *str, const char *fmt, va_list args); void EXPORT_FUNC(grub_stop) (void) __attribute__ ((noreturn)); +grub_uint8_t *EXPORT_FUNC(grub_utf16_to_utf8) (grub_uint8_t *dest, + grub_uint16_t *src, + grub_size_t size); + grub_err_t EXPORT_FUNC(grub_split_cmdline) (const char *str, grub_err_t (* getline) (char **), int *argc, char ***argv); Index: kern/misc.c =================================================================== RCS file: /cvsroot/grub/grub2/kern/misc.c,v retrieving revision 1.13 diff -u -p -u -p -r1.13 misc.c --- kern/misc.c 18 Aug 2004 09:00:01 -0000 1.13 +++ kern/misc.c 23 Aug 2004 12:23:56 -0000 @@ -647,6 +647,68 @@ grub_sprintf (char *str, const char *fmt return ret; } +/* Convert UTF-16 to UTF8. */ +grub_uint8_t * +grub_utf16_to_utf8 (grub_uint8_t *dest, grub_uint16_t *src, + grub_size_t size) +{ + grub_uint32_t code_high = 0; + + while (size--) + { + grub_uint32_t code = *src++; + + if (code_high) + { + if (code >= 0xDC00 && code <= 0xDFFF) + { + /* Surrogate pair. */ + code = ((code_high - 0xD800) << 12) + (code - 0xDC00) + 0x10000; + + *dest++ = (code >> 18) | 0xF0; + *dest++ = ((code >> 12) & 0x3F) | 0x80; + *dest++ = ((code >> 6) & 0x3F) | 0x80; + *dest++ = (code & 0x3F) | 0x80; + } + else + { + /* Error... */ + *dest++ = '?'; + } + + code_high = 0; + } + else + { + if (code <= 0x007F) + *dest++ = code; + else if (code <= 0x07FF) + { + *dest++ = (code >> 6) | 0xC0; + *dest++ = (code & 0x3F) | 0x80; + } + else if (code >= 0xD800 && code <= 0xDBFF) + { + code_high = code; + continue; + } + else if (code >= 0xDC00 && code <= 0xDFFF) + { + /* Error... */ + *dest++ = '?'; + } + else + { + *dest++ = (code >> 16) | 0xE0; + *dest++ = ((code >> 12) & 0x3F) | 0x80; + *dest++ = (code & 0x3F) | 0x80; + } + } + } + + return dest; +} + grub_err_t grub_split_cmdline (const char *cmdline, grub_err_t (* getline) (char **), int *argc, char ***argv) { Index: util/grub-emu.c =================================================================== RCS file: /cvsroot/grub/grub2/util/grub-emu.c,v retrieving revision 1.6 diff -u -p -u -p -r1.6 grub-emu.c --- util/grub-emu.c 18 Aug 2004 09:00:01 -0000 1.6 +++ util/grub-emu.c 23 Aug 2004 12:23:56 -0000 @@ -161,6 +161,7 @@ main (int argc, char *argv[]) grub_ufs_init (); grub_minix_init (); grub_hfs_init (); + grub_jfs_init (); grub_ls_init (); grub_boot_init (); grub_cmp_init (); @@ -179,6 +180,7 @@ main (int argc, char *argv[]) grub_ext2_fini (); grub_minix_fini (); grub_hfs_fini (); + grub_jfs_fini (); grub_fat_fini (); grub_boot_fini (); grub_cmp_fini ();