All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marco Gerards <metgerards@student.han.nl>
To: The development of GRUB 2 <grub-devel@gnu.org>
Subject: Re: JFS support (PATCH) and filesystem improvements
Date: Sat, 28 Aug 2004 17:47:10 +0000	[thread overview]
Message-ID: <87zn4f17fl.fsf@marco.marco-g.com> (raw)
In-Reply-To: <87vffat3zq.fsf@marco.marco-g.com> (Marco Gerards's message of "Mon, 23 Aug 2004 12:48:09 +0000")

Marco Gerards <metgerards@student.han.nl> writes:

> 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.

Committed.

> 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.

And here is a patch for that.

To avoid code duplication I wrote kern/fshelp.c, which has the
following two functions:

- grub_fshelp_find_file

  A function that can be used to lookup nodes using a path.  This code
  is required in almost any filesystem.  It can also handle symlinks
  and check for many errors.

- grub_fshelp_read_file

  This function takes a number of a offset in a file and the length to
  read and read that part of the file into a buffer supplied by the
  filesystem.

These two functions are helper functions for filesystems.  It is used
to avoid bugs and code duplication and making my live easier :).  The
filesystem has to define grub_fshelp_node which describes a single
filesystem node and a few callback functions to help the functions I
described above.

I would really appreciate it if someone takes a look at the code and
especially fshelp.c and fshelp.h to see if the interfaces are right,
etc.  Of course I have tested the code and I think it is ready to be
committed, but I don't want to do that silently, so please reply...

When this patch is in I will make these changes for other filesystems
as well.

Thanks,
Marco

2004-08-28  Marco Gerards  <metgerards@student.han.nl>

	* conf/i386-pc.rmk (kernel_img_SOURCES): Added kern/fshelp.c.
	(grub_mkimage_LDFLAGS): Likewise.
	(grub_emu_SOURCES): Likewise.
	(kernel_img_HEADERS): Added fshelp.h.
	* fs/ext2.c: Include <grub/fshelp.h>.
	(FILETYPE_REG): New macro.
	(FILETYPE_INO_REG): Likewise.
	(grub_ext_sblock): Renamed to `grub_ext2_sblock'.
	Changed all users.
	(ext2_block_group): Renamed to `grub_ext2_block_group'.  Changed
	all users.
	(grub_fshelp_node): New struct.
	(grub_ext2_data): Added member `diropen'.  Changed member `inode'
	to a pointer.
	(grub_ext2_get_file_block): Removed function.
	(grub_ext2_read_block): New function.
	(grub_ext2_read_file): Replaced parameter `data' by `node'.
	This function was written.
	(grub_ext2_mount): Read the root inode.  Create a diropen struct.
	(grub_ext2_find_file): Removed function.
	(grub_ext2_read_symlink): New function.
	(grub_ext2_iterate_dir): Likewise.
	(grub_ext2_open): Rewritten.
	(grub_ext2_dir): Rewritten.
	* include/grub/fshelp.h: New file.
	* kern/fshelp.c: Likewise.
	

Index: conf/i386-pc.rmk
===================================================================
RCS file: /cvsroot/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.17
diff -u -p -u -p -r1.17 i386-pc.rmk
--- conf/i386-pc.rmk	28 Aug 2004 13:14:29 -0000	1.17
+++ conf/i386-pc.rmk	28 Aug 2004 17:05:00 -0000
@@ -23,7 +23,7 @@ diskboot_img_LDFLAGS = -nostdlib -Wl,-N,
 
 # For kernel.img.
 kernel_img_SOURCES = kern/i386/pc/startup.S kern/main.c kern/device.c \
-	kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \
+	kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c kern/fshelp.c \
 	kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \
 	kern/i386/dl.c kern/i386/pc/init.c disk/i386/pc/partition.c \
 	kern/env.c disk/i386/pc/biosdisk.c \
@@ -33,7 +33,7 @@ kernel_img_HEADERS = boot.h device.h dis
 	file.h fs.h kernel.h loader.h misc.h mm.h net.h rescue.h symbol.h \
 	term.h types.h machine/biosdisk.h machine/boot.h \
 	machine/console.h machine/init.h machine/memory.h \
-	machine/loader.h machine/partition.h machine/vga.h arg.h env.h
+	machine/loader.h machine/partition.h machine/vga.h arg.h env.h fshelp.h
 kernel_img_CFLAGS = $(COMMON_CFLAGS)
 kernel_img_ASFLAGS = $(COMMON_ASFLAGS)
 kernel_img_LDFLAGS = -nostdlib -Wl,-N,-Ttext,8200
@@ -61,10 +61,10 @@ 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 fs/jfs.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 kern/fshelp.c
 
 # For grub
-grub_emu_SOURCES = kern/main.c kern/device.c				\
+grub_emu_SOURCES = kern/main.c kern/device.c  kern/fshelp.c		\
 	kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c		\
         kern/misc.c kern/loader.c kern/rescue.c kern/term.c		\
 	disk/i386/pc/partition.c kern/env.c commands/ls.c		\
Index: fs/ext2.c
===================================================================
RCS file: /cvsroot/grub/grub2/fs/ext2.c,v
retrieving revision 1.8
diff -u -p -u -p -r1.8 ext2.c
--- fs/ext2.c	13 Aug 2004 22:33:35 -0000	1.8
+++ fs/ext2.c	28 Aug 2004 17:05:00 -0000
@@ -29,11 +29,13 @@
 
 /* Filetype used in directory entry.  */
 #define	FILETYPE_UNKNOWN	0
+#define	FILETYPE_REG		1
 #define	FILETYPE_DIRECTORY	2
 #define	FILETYPE_SYMLINK	7
 
 /* Filetype information as used in inodes.  */
 #define FILETYPE_INO_MASK	0170000
+#define FILETYPE_INO_REG	0100000
 #define FILETYPE_INO_DIRECTORY	0040000
 #define FILETYPE_INO_SYMLINK	0120000
 
@@ -44,6 +46,7 @@
 #include <grub/disk.h>
 #include <grub/dl.h>
 #include <grub/types.h>
+#include <grub/fshelp.h>
 
 /* Log2 size of ext2 block in 512 blocks.  */
 #define LOG2_EXT2_BLOCK_SIZE(data)			\
@@ -57,7 +60,7 @@
 #define EXT2_BLOCK_SIZE(data)		(1 << LOG2_BLOCK_SIZE(data))
 
 /* The ext2 superblock.  */
-struct grub_ext_sblock
+struct grub_ext2_sblock
 {
   grub_uint32_t total_inodes;
   grub_uint32_t total_blocks;
@@ -97,7 +100,7 @@ struct grub_ext_sblock
 };
 
 /* The ext2 blockgroup.  */
-struct ext2_block_group
+struct grub_ext2_block_group
 {
   grub_uint32_t block_id;
   grub_uint32_t inode_id;
@@ -150,12 +153,21 @@ struct ext2_dirent
   grub_uint8_t filetype;
 };
 
+struct grub_fshelp_node
+{
+  struct grub_ext2_data *data;
+  struct grub_ext2_inode inode;
+  int ino;
+  int inode_read;
+};
+
 /* Information about a "mounted" ext2 filesystem.  */
 struct grub_ext2_data
 {
-  struct grub_ext_sblock sblock;
+  struct grub_ext2_sblock sblock;
   grub_disk_t disk;
-  struct grub_ext2_inode inode;
+  struct grub_ext2_inode *inode;
+  struct grub_fshelp_node diropen;
 };
 
 #ifndef GRUB_UTIL
@@ -166,60 +178,59 @@ static grub_dl_t my_mod;
    the mounted filesystem DATA.  */
 inline static grub_err_t
 grub_ext2_blockgroup (struct grub_ext2_data *data, int group, 
-		      struct ext2_block_group *blkgrp)
+		      struct grub_ext2_block_group *blkgrp)
 {
-  return grub_disk_read (data->disk, 
+  return grub_disk_read (data->disk,
 			 ((grub_le_to_cpu32 (data->sblock.first_data_block) + 1)
 			  << LOG2_EXT2_BLOCK_SIZE (data)),
-			 group * sizeof (struct ext2_block_group), 
-			 sizeof (struct ext2_block_group), (char *) blkgrp);
+			 group * sizeof (struct grub_ext2_block_group), 
+			 sizeof (struct grub_ext2_block_group), (char *) blkgrp);
 }
 
-/* Return in BLOCK the on disk block number of block FILEBLOCK in the
-   opened file descibed by DATA.  If this block is not stored on disk
-   in case of a sparse file return 0.  */
-static grub_err_t
-grub_ext2_get_file_block (struct grub_ext2_data *data,
-			  int fileblock, int *block)
+
+static int
+grub_ext2_read_block (grub_fshelp_node_t node, int fileblock)
 {
+  struct grub_ext2_data *data = node->data;
+  struct grub_ext2_inode *inode = &node->inode;
   int blknr;
-  struct grub_ext2_inode *inode = &data->inode;
-
+  int blksz = EXT2_BLOCK_SIZE (data);
+  int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
+  
   /* Direct blocks.  */
   if (fileblock < INDIRECT_BLOCKS)
     blknr = grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]);
   /* Indirect.  */
-  else if (fileblock < INDIRECT_BLOCKS + EXT2_BLOCK_SIZE (data) / 4)
+  else if (fileblock < INDIRECT_BLOCKS + blksz / 4)
     {
-      grub_uint32_t indir[EXT2_BLOCK_SIZE (data) / 4];
+      grub_uint32_t indir[blksz / 4];
 
       if (grub_disk_read (data->disk, 
 			  grub_le_to_cpu32 (inode->blocks.indir_block)
-			  << LOG2_EXT2_BLOCK_SIZE (data),
-			  0, EXT2_BLOCK_SIZE (data), (char *) indir))
+			  << log2_blksz,
+			  0, blksz, (char *) indir))
 	return grub_errno;
 	  
       blknr = grub_le_to_cpu32 (indir[fileblock - INDIRECT_BLOCKS]);
     }
   /* Double indirect.  */
-  else if (fileblock < INDIRECT_BLOCKS + EXT2_BLOCK_SIZE (data) / 4 
-	   * (EXT2_BLOCK_SIZE (data)  / 4 + 1))
+  else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz  / 4 + 1))
     {
-      unsigned int perblock = EXT2_BLOCK_SIZE (data) / 4;
+      unsigned int perblock = blksz / 4;
       unsigned int rblock = fileblock - (INDIRECT_BLOCKS 
-					 + EXT2_BLOCK_SIZE (data) / 4);
-      grub_uint32_t indir[EXT2_BLOCK_SIZE (data) / 4];
+					 + blksz / 4);
+      grub_uint32_t indir[blksz / 4];
 
       if (grub_disk_read (data->disk, 
 			  grub_le_to_cpu32 (inode->blocks.double_indir_block) 
-			  << LOG2_EXT2_BLOCK_SIZE (data),
-			  0, EXT2_BLOCK_SIZE (data), (char *) indir))
+			  << log2_blksz,
+			  0, blksz, (char *) indir))
 	return grub_errno;
 
       if (grub_disk_read (data->disk,
 			  grub_le_to_cpu32 (indir[rblock / perblock])
-			  << LOG2_EXT2_BLOCK_SIZE (data),
-			  0, EXT2_BLOCK_SIZE (data), (char *) indir))
+			  << log2_blksz,
+			  0, blksz, (char *) indir))
 	return grub_errno;
 
       
@@ -233,78 +244,23 @@ grub_ext2_get_file_block (struct grub_ex
       return grub_errno;
     }
 
-  *block = blknr;
-
-  return 0;
+  return blknr;
 }
 
+
 /* 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_ext2_read_file (struct grub_ext2_data *data,
+grub_ext2_read_file (grub_fshelp_node_t node,
 		     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 > grub_le_to_cpu32 (data->inode.size))
-    len = grub_le_to_cpu32 (data->inode.size);
-
-  blockcnt = ((len + pos) 
-	      + EXT2_BLOCK_SIZE (data) - 1) / EXT2_BLOCK_SIZE (data);
-
-  for (i = pos / EXT2_BLOCK_SIZE (data); i < blockcnt; i++)
-    {
-      int blknr;
-      int blockoff = pos % EXT2_BLOCK_SIZE (data);
-      int blockend = EXT2_BLOCK_SIZE (data);
-
-      int skipfirst = 0;
-
-      grub_ext2_get_file_block (data, i, &blknr);
-      if (grub_errno)
-	return -1;
-      
-      blknr = blknr << LOG2_EXT2_BLOCK_SIZE (data);
-
-      /* Last block.  */
-      if (i == blockcnt - 1)
-	{
-	  blockend = (len + pos) % EXT2_BLOCK_SIZE (data);
-	  
-	  /* The last portion is exactly EXT2_BLOCK_SIZE (data).  */
-	  if (!blockend)
-	    blockend = EXT2_BLOCK_SIZE (data);
-	}
-
-      /* First block.  */
-      if (i == pos / EXT2_BLOCK_SIZE (data))
-	{
-	  skipfirst = blockoff;
-	  blockend -= skipfirst;
-	}
-
-      /* If the block number is 0 this block is not stored on disk but
-	 is zero filled instead.  */
-      if (blknr)
-	{
-	  data->disk->read_hook = read_hook;	  
-	  grub_disk_read (data->disk, blknr, skipfirst,
-			  blockend, buf);
-	  data->disk->read_hook = 0;
-	  if (grub_errno)
-	    return -1;
-	}
-      else
-	grub_memset (buf, EXT2_BLOCK_SIZE (data) - skipfirst, 0);
-
-      buf += EXT2_BLOCK_SIZE (data) - skipfirst;
-    }
-
-  return len;
+  return grub_fshelp_read_file (node->data->disk, node, read_hook,
+				pos, len, buf, grub_ext2_read_block,
+				node->inode.size,
+				LOG2_EXT2_BLOCK_SIZE (node->data));
+    
 }
 
 
@@ -313,8 +269,8 @@ static grub_err_t
 grub_ext2_read_inode (struct grub_ext2_data *data,
 		      int ino, struct grub_ext2_inode *inode)
 {
-  struct ext2_block_group blkgrp;
-  struct grub_ext_sblock *sblock = &data->sblock;
+  struct grub_ext2_block_group blkgrp;
+  struct grub_ext2_sblock *sblock = &data->sblock;
   int inodes_per_block;
   
   unsigned int blkno;
@@ -355,7 +311,7 @@ grub_ext2_mount (grub_disk_t disk)
     return 0;
 
   /* Read the superblock.  */
-  grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_ext_sblock),
+  grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_ext2_sblock),
 			(char *) &data->sblock);
   if (grub_errno)
     goto fail;
@@ -364,7 +320,17 @@ grub_ext2_mount (grub_disk_t disk)
   if (grub_le_to_cpu16 (data->sblock.magic) != EXT2_MAGIC)
     goto fail;
   
+  data->diropen.data = data;
+  data->diropen.ino = 2;
+  data->diropen.inode_read = 1;
+
+  data->inode = &data->diropen.inode;
   data->disk = disk;
+
+  grub_ext2_read_inode (data, 2, data->inode);
+  if (grub_errno)
+    goto fail;
+  
   return data;
 
  fail:
@@ -373,198 +339,136 @@ grub_ext2_mount (grub_disk_t disk)
   return 0;
 }
 
-/* Find the file with the pathname PATH on the filesystem described by
-   DATA.  Return its inode number in INO.  */
-static grub_err_t
-grub_ext2_find_file (struct grub_ext2_data *data, const char *path, int *ino)
+static char *
+grub_ext2_read_symlink (grub_fshelp_node_t node)
 {
-  int blocksize = EXT2_BLOCK_SIZE (data)
-    << grub_le_to_cpu32 (data->sblock.log2_block_size);
-  struct grub_ext2_inode *inode = &data->inode;
-  int currinode = 2;
-  int symlinkcnt = 0;
-
-  char fpath[EXT2_PATH_MAX];
-  char *name = fpath;
-
-  grub_strncpy (fpath, path, EXT2_PATH_MAX);
-
-  if (!name || name[0] != '/')
-    {
-      grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
-      return grub_errno;
-    }
-
-  /* Skip the first slash.  */
-  name++;
-  if (!*name)
+  char *symlink;
+  struct grub_fshelp_node *diro = node;
+  
+  if (!diro->inode_read)
     {
-      *ino = 2;
-      return 0;
+      grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
+      if (grub_errno)
+	return 0;
     }
-
-  /* Remove trailing "/".  */
-  if (name[grub_strlen (name) - 1] =='/')
-    name[grub_strlen (name) - 1] = '\0';
-
-  while (*name)
+  
+  symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1);
+  if (!symlink)
+    return 0;
+  
+  /* If the filesize of the symlink is bigger than
+     60 the symlink is stored in a separate block,
+     otherwise it is stored in the inode.  */
+  if (grub_le_to_cpu32 (diro->inode.size) <= 60)
+    grub_strncpy (symlink, 
+		  diro->inode.symlink,
+		  grub_le_to_cpu32 (diro->inode.size));
+  else
     {
-      unsigned int fpos = 0;
-      char *next;
-      int namesize;
-
-      /* Extract the actual part from the pathname.  */
-      next = grub_strchr (name, '/');
-      if (next)
+      grub_ext2_read_file (diro, 0, 0,
+			   grub_le_to_cpu32 (diro->inode.size),
+			   symlink);
+      if (grub_errno)
 	{
-	  next[0] = '\0';
-	  next++;
+	  grub_free (symlink);
+	  return 0;
 	}
+    }
+  
+  symlink[grub_le_to_cpu32 (diro->inode.size)] = '\0';
+  return symlink;
+}
 
-      namesize = grub_strlen (name);
+static int
+grub_ext2_iterate_dir (grub_fshelp_node_t dir,
+		       int (*hook) (const char *filename,
+				    enum grub_fshelp_filetype filetype,
+				    grub_fshelp_node_t node))
+{
+  unsigned int fpos = 0;
+  struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
+  
+  if (!diro->inode_read)
+    {
+      grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
+      if (grub_errno)
+	return 0;
+    }
+  
+  /* Search the file.  */
+  while (fpos < grub_le_to_cpu32 (diro->inode.size))
+    {
+      struct ext2_dirent dirent;
 
-      /* Open the file.  */
-      grub_ext2_read_inode (data, currinode, inode);
+      grub_ext2_read_file (diro, 0, fpos, sizeof (struct ext2_dirent),
+			   (char *) &dirent);
       if (grub_errno)
-	goto fail;
+	return 0;
       
-      /* Search the file.  */
-      while (fpos < grub_le_to_cpu32 (inode->size))
+      if (dirent.namelen != 0)
 	{
-	  struct ext2_dirent dirent;
-
-	  /* Read the directory entry.  */
-	  grub_ext2_read_file (data, 0, fpos, sizeof (struct ext2_dirent),
-			       (char *) &dirent);
+	  char filename[dirent.namelen + 1];
+	  struct grub_fshelp_node *fdiro;
+	  enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
+	  
+	  grub_ext2_read_file (diro, 0, fpos + sizeof (struct ext2_dirent),
+			       dirent.namelen, filename);
 	  if (grub_errno)
-	    goto fail;
+	    return 0;
+	  
+	  fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
+	  if (!fdiro)
+	    return 0;
+	  
+	  fdiro->data = diro->data;
+	  fdiro->ino = grub_le_to_cpu32 (dirent.inode);
+	  
+	  filename[dirent.namelen] = '\0';
 
-	  if (dirent.namelen != 0)
+	  if (dirent.filetype != FILETYPE_UNKNOWN)
 	    {
-	      char filename[dirent.namelen + 1];
+	      fdiro->inode_read = 0;
 
-	      /* Read the filename part of this directory entry.  */
-	      grub_ext2_read_file (data, 0, fpos 
-				   + sizeof (struct ext2_dirent),
-				   dirent.namelen, filename);
+	      if (dirent.filetype == FILETYPE_DIRECTORY)
+		type = GRUB_FSHELP_DIR;
+	      else if (dirent.filetype == FILETYPE_SYMLINK)
+		type = GRUB_FSHELP_SYMLINK;
+	      else if (dirent.filetype == FILETYPE_REG)
+		type = GRUB_FSHELP_REG;
+	    }
+	  else
+	    {
+	      /* The filetype can not be read from the dirent, read
+		 the inode to get more information.  */
+	      grub_ext2_read_inode (diro->data, grub_le_to_cpu32 (dirent.inode),
+				    &fdiro->inode);
 	      if (grub_errno)
-		goto fail;
-	  
-	      filename[dirent.namelen] = '\0';
-
-	      /* Check if the current directory entry described the
-		 file we are looking for.  */
-	      if (dirent.namelen == namesize 
-		  && !grub_strncmp (name, filename, namesize))
 		{
-		  /* Stat the inode.  */
-		  grub_ext2_read_inode (data, 
-					grub_le_to_cpu32 (dirent.inode),
-					inode);
-
-		  /* If this is a symlink, follow it.  */
-		  if ((grub_le_to_cpu16 (data->inode.mode) 
-		       & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK)
-		    {
-		      /* XXX: Use malloc instead?  */
-		      char symlink[blocksize];
-
-		      if (++symlinkcnt == EXT2_MAX_SYMLINKCNT)
-			{
-			  grub_error (GRUB_ERR_SYMLINK_LOOP,
-				      "too deep nesting of symlinks");
-			  goto fail;
-			}
-
-		      /* If the filesize of the symlink is bigger than
-			 60 the symlink is stored in a separate block,
-			 otherwise it is stored in the inode.  */
-		      if (grub_le_to_cpu32 (inode->size) <= 60)
-			grub_strncpy (symlink, 
-				      inode->symlink,
-				      grub_le_to_cpu32 (inode->size));
-		      else
-			{
-			  grub_ext2_read_file (data, 0, 0,
-					       grub_le_to_cpu32 (inode->size),
-					       symlink);
-			  if (grub_errno)
-			    goto fail;
-			}
-
-		      symlink[grub_le_to_cpu32 (inode->size)] = '\0';
-	  
-		      /* Check if the symlink is absolute or relative.  */
-		      if (symlink[0] == '/')
-			{
-			  grub_strncpy (fpath, symlink + 1, EXT2_PATH_MAX);
-			  name = fpath;
-			  currinode = 2;
-			}
-		      else
-			{
-			  char *bak = 0;
-
-			  if (next)
-			    {
-			      bak = grub_strdup (next);
-			      if (!bak)
-				goto fail;
-			    }
-		      
-			  /* Relative symlink, construct the new path.  */
-			  grub_strcpy (fpath, symlink);
-			  name = fpath;
-		      
-			  if (next)
-			    {
-			      grub_strcat (name, "/");
-			      grub_strcat (name, bak);
-			      grub_free (bak);
-			    }
-			}
-
-		      fpos = 0;
-		      break;
-		    }
-
-		  if (next)
-		    {
-		      currinode = grub_le_to_cpu32 (dirent.inode);
-		      name = next;
-
-		      if ((grub_le_to_cpu16 (data->inode.mode) 
-			   & FILETYPE_INO_MASK) != FILETYPE_INO_DIRECTORY)
-			{
-			  grub_error (GRUB_ERR_BAD_FILE_TYPE,
-				      "not a directory");
-			  goto fail;
-			}
-		      break;
-		    }
-		  else /* Found it!  */
-		    {
-		      *ino = grub_le_to_cpu32 (dirent.inode);
-		      return 0;
-		    }
+		  grub_free (fdiro);
+		  return 0;
 		}
+	      
+	      fdiro->inode_read = 1;
+	      
+	      if ((grub_le_to_cpu16 (fdiro->inode.mode)
+		   & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
+		type = GRUB_FSHELP_DIR;
+	      else if ((grub_le_to_cpu16 (fdiro->inode.mode)
+			& FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
+		type = GRUB_FSHELP_SYMLINK;
+	      else if ((grub_le_to_cpu16 (fdiro->inode.mode)
+			& FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
+		type = GRUB_FSHELP_REG;
 	    }
 
-	  /* Move to next directory entry.  */
-	  fpos += grub_le_to_cpu16 (dirent.direntlen);
+	  if (hook (filename, type, fdiro))
+	    return 1;
 	}
 
-      /* The complete directory was read and no matching file was
-	 found.  */
-      if (fpos >= grub_le_to_cpu32 (inode->size))
-	{
-	  grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
-	  goto fail;
-	}
+      fpos += grub_le_to_cpu16 (dirent.direntlen);
     }
-
- fail:
-  return grub_errno;
+  
+  return 0;
 }
 
 /* Open a file named NAME and initialize FILE.  */
@@ -572,40 +476,42 @@ static grub_err_t
 grub_ext2_open (struct grub_file *file, const char *name)
 {
   struct grub_ext2_data *data;
-  int ino;
-
+  struct grub_fshelp_node *fdiro = 0;
+  
 #ifndef GRUB_UTIL
   grub_dl_ref (my_mod);
 #endif
-
+  
   data = grub_ext2_mount (file->device->disk);
   if (!data)
     goto fail;
   
-  grub_ext2_find_file (data, name, &ino);
-  if (grub_errno)
-    goto fail;
-
-  grub_ext2_read_inode (data, ino, &data->inode);
+  grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_ext2_iterate_dir,
+			 grub_ext2_read_symlink, GRUB_FSHELP_REG);
   if (grub_errno)
     goto fail;
-
-  if (!(grub_le_to_cpu16 (data->inode.mode) & 0100000))
+  
+  if (!fdiro->inode_read)
     {
-      grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
-      goto fail;
+      grub_ext2_read_inode (data, fdiro->ino, &fdiro->inode);
+      if (grub_errno)
+	goto fail;
     }
   
-  file->size = grub_le_to_cpu32 (data->inode.size);
+  grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_ext2_inode));
+  grub_free (fdiro);
+
+  file->size = grub_le_to_cpu32 (data->inode->size);
   file->data = data;
   file->offset = 0;
 
   return 0;
 
  fail:
-
   grub_free (data);
-
+  if (fdiro != &data->diropen)
+    grub_free (fdiro);
+  
 #ifndef GRUB_UTIL
   grub_dl_unref (my_mod);
 #endif
@@ -632,7 +538,8 @@ grub_ext2_read (grub_file_t file, char *
   struct grub_ext2_data *data = 
     (struct grub_ext2_data *) file->data;
   
-  return grub_ext2_read_file (data, file->read_hook, file->offset, len, buf);
+  return grub_ext2_read_file (&data->diropen, file->read_hook,
+			      file->offset, len, buf);
 }
 
 
@@ -641,10 +548,23 @@ grub_ext2_dir (grub_device_t device, con
 	       int (*hook) (const char *filename, int dir))
 {
   struct grub_ext2_data *data = 0;;
-
-  int ino;
-  unsigned int fpos = 0;
+  struct grub_fshelp_node *fdiro = 0;
   
+  auto int iterate (const char *filename, enum grub_fshelp_filetype filetype,
+		    grub_fshelp_node_t node);
+
+  int iterate (const char *filename, enum grub_fshelp_filetype filetype,
+	       grub_fshelp_node_t node)
+    {
+      grub_free (node);
+      if (filetype == GRUB_FSHELP_DIR)
+	return hook (filename, 1);
+      else 
+	return hook (filename, 0);
+      
+      return 0;
+    }
+
 #ifndef GRUB_UTIL
   grub_dl_ref (my_mod);
 #endif
@@ -652,61 +572,18 @@ grub_ext2_dir (grub_device_t device, con
   data = grub_ext2_mount (device->disk);
   if (!data)
     goto fail;
-
-  grub_ext2_find_file (data, (char *) path, &ino);
-  if (grub_errno)
-    goto fail;
-
-  grub_ext2_read_inode (data, ino, &data->inode);
+  
+  grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_ext2_iterate_dir,
+			 grub_ext2_read_symlink, GRUB_FSHELP_DIR);
   if (grub_errno)
-    goto fail;
-
-  if ((grub_le_to_cpu16 (data->inode.mode)
-       & FILETYPE_INO_MASK) != FILETYPE_INO_DIRECTORY)
-    {
-      grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
-      goto fail;
-    }
-
-  /* Search the file.  */
-  while (fpos < grub_le_to_cpu32 (data->inode.size))
-    {
-      struct ext2_dirent dirent;
-	
-      grub_ext2_read_file (data, 0, fpos, sizeof (struct ext2_dirent),
-			   (char *) &dirent);
-      if (grub_errno)
-	goto fail;
-
-      if (dirent.namelen != 0)
-	{
-	  char filename[dirent.namelen + 1];
-
-	  grub_ext2_read_file (data, 0, fpos + sizeof (struct ext2_dirent),
-			       dirent.namelen, filename);
-	  if (grub_errno)
-	    goto fail;
-	  
-	  filename[dirent.namelen] = '\0';
-	  
-	  if (dirent.filetype != FILETYPE_UNKNOWN)
-	    hook (filename, dirent.filetype == FILETYPE_DIRECTORY);
-	  else
-	    {
-	      struct grub_ext2_inode inode;
-	      grub_ext2_read_inode (data, grub_le_to_cpu32 (dirent.inode), &inode);
-	      
-	      hook (filename, (grub_le_to_cpu16 (inode.mode)
-			       & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY);
-	    }
-	}
-
-      fpos += grub_le_to_cpu16 (dirent.direntlen);
-    }
-
+    return grub_errno;
+  
+  grub_ext2_iterate_dir (fdiro, iterate);
+  
  fail:
-
   grub_free (data);
+  if (fdiro != &data->diropen)
+    grub_free (fdiro);
 
 #ifndef GRUB_UTIL
   grub_dl_unref (my_mod);
Index: include/grub/fshelp.h
===================================================================
RCS file: include/grub/fshelp.h
diff -N include/grub/fshelp.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ include/grub/fshelp.h	28 Aug 2004 17:05:00 -0000
@@ -0,0 +1,59 @@
+/* fshelp.h -- Filesystem helper functions */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2004  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 GRUB; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef GRUB_FSHELP_HEADER
+#define GRUB_FSHELP_HEADER	1
+
+#include <grub/types.h>
+#include <grub/symbol.h>
+#include <grub/err.h>
+
+typedef struct grub_fshelp_node *grub_fshelp_node_t;
+
+enum grub_fshelp_filetype
+  {
+    GRUB_FSHELP_UNKNOWN,
+    GRUB_FSHELP_REG,
+    GRUB_FSHELP_DIR,
+    GRUB_FSHELP_SYMLINK
+  };
+
+grub_err_t
+EXPORT_FUNC(grub_fshelp_find_file) (const char *path,
+				    grub_fshelp_node_t rootnode,
+				    grub_fshelp_node_t *foundnode,
+				    int (*iterate_dir) (grub_fshelp_node_t dir,
+							int (*hook) (const char *filename,
+								     enum grub_fshelp_filetype filetype,
+								     grub_fshelp_node_t node)),
+				    char *(*read_symlink) (grub_fshelp_node_t node),
+				    enum grub_fshelp_filetype expect);
+
+grub_ssize_t
+EXPORT_FUNC(grub_fshelp_read_file) (grub_disk_t disk, grub_fshelp_node_t node,
+				    void (*read_hook) (unsigned long sector,
+						       unsigned offset,
+						       unsigned length),
+				    int pos, unsigned int len, char *buf,
+				    int (*get_block) (grub_fshelp_node_t node,
+						      int block),
+				    unsigned int filesize, int log2blocksize);
+     
+#endif /* ! GRUB_FSHELP_HEADER */
Index: kern/fshelp.c
===================================================================
RCS file: kern/fshelp.c
diff -N kern/fshelp.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ kern/fshelp.c	28 Aug 2004 17:05:00 -0000
@@ -0,0 +1,286 @@
+/* fshelp.c -- Filesystem helper functions */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2004  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 GRUB; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/fshelp.h>
+
+
+/* Lookup the node PATH.  The node ROOTNODE describes the root of the
+   directory tree.  The node found is returned in FOUNDNODE, which is
+   either a ROOTNODE or a new malloc'ed node.  ITERATE_DIR is used to
+   iterate over all directory entries in the current node.
+   READ_SYMLINK is used to read the symlink if a node is a symlink.
+   EXPECTTYPE is the type node that is expected by the called, an
+   error is generated if the node is not of the expected type.  */
+grub_err_t
+grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
+		       grub_fshelp_node_t *foundnode,
+		       int (*iterate_dir) (grub_fshelp_node_t dir,
+					   int (*hook)
+					   (const char *filename,
+					    enum grub_fshelp_filetype filetype,
+					    grub_fshelp_node_t node)),
+		       char *(*read_symlink) (grub_fshelp_node_t node),
+		       enum grub_fshelp_filetype expecttype)
+{
+  grub_err_t err;
+  enum grub_fshelp_filetype foundtype;
+  int symlinknest = 0;
+  
+  auto grub_err_t find_file (const char *currpath, grub_fshelp_node_t currroot,
+			     grub_fshelp_node_t *currfound);
+
+  grub_err_t find_file (const char *currpath, grub_fshelp_node_t currroot,
+			grub_fshelp_node_t *currfound)
+    {
+      char fpath[grub_strlen (currpath) + 1];
+      char *name = fpath;
+      char *next;
+      //  unsigned int pos = 0;
+      enum grub_fshelp_filetype type = GRUB_FSHELP_DIR;
+      grub_fshelp_node_t currnode = currroot;
+      grub_fshelp_node_t oldnode = currroot;
+
+      auto int iterate (const char *filename,
+			enum grub_fshelp_filetype filetype,
+			grub_fshelp_node_t node);
+
+      auto void free_node (grub_fshelp_node_t node);
+      
+      void free_node (grub_fshelp_node_t node)
+	{
+      	  if (node != rootnode && node != currroot)
+	    grub_free (node);
+	}
+      
+      int iterate (const char *filename, enum grub_fshelp_filetype filetype,
+		   grub_fshelp_node_t node)
+	{
+	  if (type == GRUB_FSHELP_UNKNOWN || grub_strcmp (name, filename))
+	    {
+	      grub_free (node);
+	      return 0;
+	    }
+
+	  /* The node is found, stop iterating over the nodes.  */
+	  type = filetype;
+	  oldnode = currnode == rootnode ? 0 : currnode;
+	  currnode = node;
+	  
+	  return 1;
+	}
+  
+      grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
+
+      if (*name == '/')
+	name++;
+  
+      /* Remove trailing "/".  */
+      if (name[grub_strlen (name) - 1] == '/')
+	name[grub_strlen (name) - 1] = '\0';
+  
+      if (!*name)
+	{
+	  *currfound = currnode;
+	  return 0;
+	}
+  
+      for (;;)
+	{
+	  int found;
+      
+	  /* Extract the actual part from the pathname.  */
+	  next = grub_strchr (name, '/');
+	  if (next)
+	    {
+	      next[0] = '\0';
+	      next++;
+	    }
+	  
+	  /* At this point it is expected that the current node is a
+	     directory, check if this is true.  */
+	  if (type != GRUB_FSHELP_DIR)
+	    {
+	      free_node (currnode);
+	      return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
+	    }
+	  
+	  /* Iterate over the directory.  */
+	  found = iterate_dir (currnode, iterate);
+	  if (!found)
+	    {
+	      free_node (oldnode);
+	      if (grub_errno)
+		return grub_errno;
+	      
+	      break;
+	    }
+	  
+	  /* Read in the symlink and follow it.  */
+	  if (type == GRUB_FSHELP_SYMLINK)
+	    {
+	      char *symlink;
+	      
+	      /* Test if the symlink does not loop.  */
+	      if (++symlinknest == 8)
+		{
+		  free_node (currnode);
+		  free_node (oldnode);
+		  return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
+		}
+	      
+	      symlink = read_symlink (currnode);
+	      free_node (currnode);
+	      
+	      if (!symlink)
+		{
+		  free_node (oldnode);
+		  return grub_errno;
+		}
+	      
+	      /* The symlink is an absolute path, go back to the root inode.  */
+	      if (symlink[0] == '/')
+		{
+		  free_node (oldnode);
+		  oldnode = rootnode;
+		}
+	      
+	      /* Lookup the node the symlink points to.  */
+	      find_file (symlink, oldnode, &currnode);
+	      if (grub_errno)
+		{
+		  free_node (oldnode);
+		  return grub_errno;
+		}
+	    }
+      
+	  free_node (oldnode);
+	  
+	  /* Fond the node!  */
+	  if (!next || *next == '\0')
+	    {
+	      *currfound = currnode;
+	      foundtype = type;
+	      return 0;
+	    }
+      
+	  name = next;
+	}
+      
+      return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
+    }
+
+  if (!path || path[0] != '/')
+    {
+      grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
+      return grub_errno;
+    }
+  
+  err = find_file (path, rootnode, foundnode);
+  if (err)
+    return err;
+  
+  /* Check if the node that was found was of the expected type.  */
+  if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
+    grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
+  else if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
+    grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
+
+  return 0;
+}
+
+
+/* Read LEN bytes from the file NODE on disk DISK into the buffer BUF,
+   beginning with the block POS.  READ_HOOK should be set before
+   reading a block from the file.  GET_BLOCK is used to translate file
+   blocks to disk blocks.  The file is FILESIZE bytes big and the
+   blocks have a size of LOG2BLOCKSIZE (in log2).  */
+grub_ssize_t
+grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node,
+		       void (*read_hook) (unsigned long sector,
+					  unsigned offset, unsigned length),
+		       int pos, unsigned int len, char *buf,
+		       int (*get_block) (grub_fshelp_node_t node, int block),
+		       unsigned int filesize, int log2blocksize)
+{
+  int i;
+  int blockcnt;
+  int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS);
+
+  /* Adjust len so it we can't read past the end of the file.  */
+  if (len > filesize)
+    len = filesize;
+
+  blockcnt = ((len + pos) 
+	      + blocksize - 1) / blocksize;
+
+  for (i = pos / blocksize; i < blockcnt; i++)
+    {
+      int blknr;
+      int blockoff = pos % blocksize;
+      int blockend = blocksize;
+
+      int skipfirst = 0;
+
+      blknr = get_block (node, i);
+      if (grub_errno)
+	return -1;
+      
+      blknr = blknr << log2blocksize;
+
+      /* Last block.  */
+      if (i == blockcnt - 1)
+	{
+	  blockend = (len + pos) % blocksize;
+	  
+	  /* The last portion is exactly blocksize.  */
+	  if (!blockend)
+	    blockend = blocksize;
+	}
+
+      /* First block.  */
+      if (i == pos / blocksize)
+	{
+	  skipfirst = blockoff;
+	  blockend -= skipfirst;
+	}
+      
+      /* If the block number is 0 this block is not stored on disk but
+	 is zero filled instead.  */
+      if (blknr)
+	{
+	  disk->read_hook = read_hook;	  
+	  grub_disk_read (disk, blknr, skipfirst,
+			  blockend, buf);
+	  disk->read_hook = 0;
+	  if (grub_errno)
+	    return -1;
+	}
+      else
+	grub_memset (buf, blocksize - skipfirst, 0);
+
+      buf += blocksize - skipfirst;
+    }
+
+  return len;
+}




  reply	other threads:[~2004-08-28 17:52 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-08-23 12:48 JFS support (PATCH) and filesystem improvements Marco Gerards
2004-08-28 17:47 ` Marco Gerards [this message]
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=87zn4f17fl.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.