All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marco Gerards <metgerards@student.han.nl>
To: grub-devel@gnu.org
Subject: JFS support (PATCH) and filesystem improvements
Date: Mon, 23 Aug 2004 12:48:09 +0000	[thread overview]
Message-ID: <87vffat3zq.fsf@marco.marco-g.com> (raw)

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  <metgerards@student.han.nl>

	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 <grub/err.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/types.h>
+
+#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
+\f
+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;
+}
+\f
+
+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;
+}
+\f
+
+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 ();




             reply	other threads:[~2004-08-23 12:52 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-08-23 12:48 Marco Gerards [this message]
2004-08-28 17:47 ` JFS support (PATCH) and filesystem improvements Marco Gerards
2004-08-29 23:00   ` Tomas Ebenlendr
2004-09-09 21:02     ` Marco Gerards
2004-09-10 18:06       ` Yoshinori K. Okuji
2004-08-30 10:22   ` Yoshinori K. Okuji
2004-08-30 15:49     ` Marco Gerards
2004-08-31 15:23       ` Tomas Ebenlendr

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87vffat3zq.fsf@marco.marco-g.com \
    --to=metgerards@student.han.nl \
    --cc=grub-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.