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: Thu, 09 Sep 2004 21:02:40 +0000	[thread overview]
Message-ID: <87vfen5f67.fsf@marco.marco-g.com> (raw)
In-Reply-To: <20040829230044.GA7453@artax.karlin.mff.cuni.cz> (Tomas Ebenlendr's message of "Mon, 30 Aug 2004 01:00:44 +0200")

ebik@artax.karlin.mff.cuni.cz (Tomas Ebenlendr) writes:

> Only some comments of very small importance as proof that I have
> read the patch.
>
> --- slashes: ---

[...]

> --- :slashes end ---

Done.

> Please write somewhere that you expect correct behavior for '..'
> entry. (e.g., in fshelp.h). When someone will implement weird fs,
> he must know what this function expects.

If the filesystem supports it, the iterate function should support
"..".  Actually, it should support anything that is a part of a
symlink.  This is just filesystem dependent, it is not a concern of
fshelp.


>> +      /* First block.  */
>> +      if (i == pos / blocksize)
>> +	{
>> +	  skipfirst = blockoff;
>> +	  blockend -= skipfirst;
>> +	}
> blockend ... not a good name: blockbytes? (hmm, also not very good.)

Well, I just left it this way.  It is what it commonly used.

Here is the new patch.  I have moved fshelp.c to fs/.  I also changed
i386-pc.rmk so it makes fshelp.mod.

Okuji, I really like how you implemented module loading.  It is nice
to see grub-mkimage automatically includes fshelp.mod in the core
image when using ext2. :)

There is one nasty thing that I had to do and that is adding
`NESTED_FUNC_ATTR' to the iterate function because it is a nested
function normally.  But other than that all issues should be resolved.

So I assume this patch is ok.  To be sure, I will wait until Sunday
before I commit this patch.

My iso9660fs code depends on fshelp.  I will send it in after fshelp
is committed.

In the meanwhile I will work a bit more on the PPC port.  I want to
have a look at module loading because it is getting really important
now.

Thanks,
Marco

2004-09-09  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.
	* fs/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	9 Sep 2004 20:49:36 -0000
@@ -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 fs/fshelp.c
 
 # For grub
-grub_emu_SOURCES = kern/main.c kern/device.c				\
+grub_emu_SOURCES = kern/main.c kern/device.c fs/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		\
@@ -80,12 +80,16 @@ genmoddep_SOURCES = util/genmoddep.c
 # Modules.
 pkgdata_MODULES = _chain.mod _linux.mod fat.mod ufs.mod ext2.mod minix.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
+	boot.mod cmp.mod cat.mod terminal.mod fshelp.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
 _chain_mod_CFLAGS = $(COMMON_CFLAGS)
 
+# For fshelp.mod.
+fshelp_mod_SOURCES = fs/fshelp.c
+fshelp_mod_CFLAGS = $(COMMON_CFLAGS)
+
 # For fat.mod.
 fat_mod_SOURCES = fs/fat.c
 fat_mod_CFLAGS = $(COMMON_CFLAGS)
Index: conf/powerpc-ieee1275.rmk
===================================================================
RCS file: /cvsroot/grub/grub2/conf/powerpc-ieee1275.rmk,v
retrieving revision 1.7
diff -u -p -u -p -r1.7 powerpc-ieee1275.rmk
--- conf/powerpc-ieee1275.rmk	28 Aug 2004 13:14:29 -0000	1.7
+++ conf/powerpc-ieee1275.rmk	9 Sep 2004 20:49:36 -0000
@@ -24,7 +24,7 @@ noinst_UTILITIES = genmoddep
 grub_emu_SOURCES = kern/main.c kern/device.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/powerpc/ieee1275/partition.c 					\
+	disk/powerpc/ieee1275/partition.c fs/fshelp.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	\
@@ -35,7 +35,7 @@ grub_emu_LDFLAGS = -lncurses
 
 grubof_SOURCES = boot/powerpc/ieee1275/cmain.c boot/powerpc/ieee1275/ieee1275.c \
 	boot/powerpc/ieee1275/crt0.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 fs/fshelp.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 \
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	9 Sep 2004 20:49:36 -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,137 @@ 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 NESTED_FUNC_ATTR
+		       (*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 (diro->inode.mode)
+		   & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
+		type = GRUB_FSHELP_DIR;
+	      else if ((grub_le_to_cpu16 (diro->inode.mode)
+			& FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
+		type = GRUB_FSHELP_SYMLINK;
+	      else if ((grub_le_to_cpu16 (diro->inode.mode)
+			& FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY)
+		type = GRUB_FSHELP_REG;
 	    }
-
-	  /* Move to next directory entry.  */
-	  fpos += grub_le_to_cpu16 (dirent.direntlen);
-	}
-
-      /* 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;
+	  
+	  if (hook (filename, type, fdiro))
+	    return 1;
 	}
+      
+      fpos += grub_le_to_cpu16 (dirent.direntlen);
     }
-
- fail:
-  return grub_errno;
+  
+  return 0;
 }
 
 /* Open a file named NAME and initialize FILE.  */
@@ -572,40 +477,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 +539,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 +549,26 @@ 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 NESTED_FUNC_ATTR iterate (const char *filename,
+				     enum grub_fshelp_filetype filetype,
+				     grub_fshelp_node_t node);
+
+  int NESTED_FUNC_ATTR 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 +576,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: fs/fshelp.c
===================================================================
RCS file: fs/fshelp.c
diff -N fs/fshelp.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ fs/fshelp.c	9 Sep 2004 20:49:37 -0000
@@ -0,0 +1,292 @@
+/* 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.  Make
+   sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
+   because GCC has a nasty bug when using regparm=3.  */
+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 NESTED_FUNC_ATTR (*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 = GRUB_FSHELP_DIR;
+  int symlinknest = 0;
+  
+  auto grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
+					      grub_fshelp_node_t currroot,
+					      grub_fshelp_node_t *currfound);
+
+  grub_err_t NESTED_FUNC_ATTR 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 NESTED_FUNC_ATTR 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 NESTED_FUNC_ATTR 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);
+      
+      /* Remove all leading slashes.  */
+      while (*name == '/')
+	name++;
+  
+      if (!*name)
+	{
+	  *currfound = currnode;
+	  return 0;
+	}
+      
+      for (;;)
+	{
+	  int found;
+      
+	  /* Extract the actual part from the pathname.  */
+	  next = grub_strchr (name, '/');
+	  if (next)
+	    {
+	      /* Remove all leading slashes.  */
+	      while (*next == '/')
+		*(next++) = '\0';
+	    }
+	  
+	  /* 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);
+	      grub_free (symlink);
+	      
+	      if (grub_errno)
+		{
+		  free_node (oldnode);
+		  return grub_errno;
+		}
+	    }
+      
+	  free_node (oldnode);
+	  
+	  /* Found 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)
+    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
+  else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype)
+    return 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;
+}
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	9 Sep 2004 20:49:37 -0000
@@ -0,0 +1,75 @@
+/* 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
+  };
+
+/* 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.  Make
+   sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
+   because GCC has a nasty bug when using regparm=3.  */
+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 NESTED_FUNC_ATTR
+							(*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);
+
+
+/* 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
+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 */




  reply	other threads:[~2004-09-09 21:10 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
2004-08-29 23:00   ` Tomas Ebenlendr
2004-09-09 21:02     ` Marco Gerards [this message]
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=87vfen5f67.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.