public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* BUG in fs/ext3/dir.c
@ 2004-12-07  9:56 Holger Kiehl
  2004-12-07 18:16 ` Andreas Dilger
  2004-12-07 20:11 ` Theodore Ts'o
  0 siblings, 2 replies; 4+ messages in thread
From: Holger Kiehl @ 2004-12-07  9:56 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: Type: TEXT/PLAIN, Size: 2460 bytes --]

Hello

[Sorry if you get this twice. This was send to ext3-users@redhat.com and
 the authors of ext3, but got no responce.]

When using readdir() on a directory with many files or long file names
it can happen that it returns the same file name twice. Attached is
a program that demonstrates this. To produce the error start the program
as follows:

     ./a.out 200 20
     BUG: 00000000000000000061 appears twice!
     stat() error (testbugdir/input/00000000000000000061) : No such file or directory
     BUG: 00000000000000000061 appears twice!
     unlink() error (testbugdir/output/00000000000000000061) : No such file or directory

or:

     ./a.out 50 61
     BUG: 0000000000000000000000000000000000000000000000000000000000020 appears twice!
     stat() error (testbugdir/input/0000000000000000000000000000000000000000000000000000000000020) : No such file or directory
     unlink() error (testbugdir/output/0000000000000000000000000000000000000000000000000000000000020) : No such file or directory

First parameter is the number of files, second the file name length.

I have traced this problem back to linux-2.6.10-rc1-bk18 and all kernels
after this one are effected. linux-2.6.10-rc1-bk17 is okay. If I reverse
the following patch in linux-2.6.10-rc1-bk18, readdir() works again
correctly:

diff -Nru linux-2.6.10-rc1-bk17/fs/ext3/dir.c linux-2.6.10-rc1-bk18/fs/ext3/dir.c
--- linux-2.6.10-rc1-bk17/fs/ext3/dir.c	2004-10-18 23:54:30.000000000 +0200
+++ linux-2.6.10-rc1-bk18/fs/ext3/dir.c	2004-12-05 16:44:21.000000000 +0100
@@ -418,7 +418,7 @@
 				get_dtype(sb, fname->file_type));
 		if (error) {
 			filp->f_pos = curr_pos;
-			info->extra_fname = fname->next;
+			info->extra_fname = fname;
 			return error;
 		}
 		fname = fname->next;
@@ -457,9 +457,12 @@
 	 * If there are any leftover names on the hash collision
 	 * chain, return them first.
 	 */
-	if (info->extra_fname &&
-	    call_filldir(filp, dirent, filldir, info->extra_fname))
-		goto finished;
+	if (info->extra_fname) {
+		if(call_filldir(filp, dirent, filldir, info->extra_fname))
+			goto finished;
+		else
+			goto next_entry;
+	}
 
 	if (!info->curr_node)
 		info->curr_node = rb_first(&info->root);
@@ -492,7 +495,7 @@
 		info->curr_minor_hash = fname->minor_hash;
 		if (call_filldir(filp, dirent, filldir, fname))
 			break;
-
+next_entry:
 		info->curr_node = rb_next(info->curr_node);
 		if (!info->curr_node) {
 			if (info->next_hash == ~0) {

Regards,
Holger
-- 

[-- Attachment #2: Type: TEXT/PLAIN, Size: 3676 bytes --]

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>

int
main(int argc, char *argv[])
{
   int           fd, filename_length, i, j, no_of_files;
   char          pathname[256], *ptr,
                 prevname[256],
                 to_pathname[256], *to_ptr;
   DIR           *dp;
   struct dirent *p_dir;
   struct stat   stat_buf;

   if (argc != 3)
   {
      fprintf(stderr, "Usage: %s <no. of files> <filename length>\n", argv[0]);
      exit(1);
   }
   else
   {
      no_of_files = atoi(argv[1]);
      filename_length = atoi(argv[2]);
   }

   /* Create necessary dirs. */
   (void)mkdir("testbugdir", S_IRUSR|S_IWUSR|S_IXUSR);
   (void)mkdir("testbugdir/input", S_IRUSR|S_IWUSR|S_IXUSR);
   (void)mkdir("testbugdir/output", S_IRUSR|S_IWUSR|S_IXUSR);

   /* Create input files. */
   strcpy(pathname, "testbugdir/input/");
   ptr = pathname + strlen(pathname);
   for (i = 0; i < no_of_files; i++)
   {
      sprintf(ptr, "%0*d", filename_length, i);
      if ((fd = open(pathname, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR)) == -1)
      {
         fprintf(stderr, "open() error %s : %s\n", pathname, strerror(errno));
         exit(1);
      }
      close(fd);
   }

   /* Move input files to output. */
   strcpy(to_pathname, "testbugdir/output/");
   to_ptr = to_pathname + strlen(to_pathname);
   *ptr = '\0';
   if ((dp = opendir(pathname)) == NULL)
   {
      fprintf(stderr, "opendir() error (%s) : %s\n",
              pathname, strerror(errno));
      exit(1);
   }
   prevname[0] = '\0';
   while ((p_dir = readdir(dp)) != NULL)
   {
      if (p_dir->d_name[0] == '.')
      {
         continue;
      }
      if (strcmp(prevname, p_dir->d_name) == 0)
      {
         fprintf(stderr, "BUG: %s appears twice!\n", p_dir->d_name);
      }
      strcpy(prevname, p_dir->d_name);
      strcpy(ptr, p_dir->d_name);
      if (stat(pathname, &stat_buf) < 0)
      {
         fprintf(stderr, "stat() error (%s) : %s\n",
                 pathname, strerror(errno));
         continue;
      }
      strcpy(to_ptr, p_dir->d_name);
      if (rename(pathname, to_pathname) == -1)
      {
         fprintf(stderr, "rename() error (file %d) : %s\n",
                 pathname, strerror(errno));
      }
   }
   (void)closedir(dp);

   /* Remove everyting. */
   *to_ptr = '\0';
   if ((dp = opendir(to_pathname)) == NULL)
   {
      fprintf(stderr, "opendir() error (%s) : %s\n",
              to_pathname, strerror(errno));
      exit(1);
   }
   prevname[0] = '\0';
   while ((p_dir = readdir(dp)) != NULL)
   {
      if (p_dir->d_name[0] == '.')
      {
         continue;
      }
      if (strcmp(prevname, p_dir->d_name) == 0)
      {
         fprintf(stderr, "BUG: %s appears twice!\n", p_dir->d_name);
      }
      strcpy(prevname, p_dir->d_name);
      strcpy(to_ptr, p_dir->d_name);
      if (unlink(to_pathname) == -1)
      {
         fprintf(stderr, "unlink() error (%s) : %s\n",
                 to_pathname, strerror(errno));
         continue;
      }
   }
   (void)closedir(dp);
   if (rmdir("testbugdir/input") == -1)
   {
      fprintf(stderr, "rmdir() error (testbugdir/input) : %s\n",
              strerror(errno));
   }
   if (rmdir("testbugdir/output") == -1)
   {
      fprintf(stderr, "rmdir() error (testbugdir/output) : %s\n",
              strerror(errno));
   }
   if (rmdir("testbugdir") == -1)
   {
      fprintf(stderr, "rmdir() error (testbugdir) : %s\n", strerror(errno));
   }

   exit(0);
}

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: BUG in fs/ext3/dir.c
  2004-12-07  9:56 BUG in fs/ext3/dir.c Holger Kiehl
@ 2004-12-07 18:16 ` Andreas Dilger
  2004-12-07 20:11 ` Theodore Ts'o
  1 sibling, 0 replies; 4+ messages in thread
From: Andreas Dilger @ 2004-12-07 18:16 UTC (permalink / raw)
  To: Holger Kiehl; +Cc: linux-kernel, Andrew Morton, Theodore Ts'o

[-- Attachment #1: Type: text/plain, Size: 7528 bytes --]

On Dec 07, 2004  09:56 +0000, Holger Kiehl wrote:
> When using readdir() on a directory with many files or long file names
> it can happen that it returns the same file name twice. Attached is
> a program that demonstrates this. To produce the error start the program
> as follows:
> 
>      ./a.out 200 20
>      BUG: 00000000000000000061 appears twice!
>      stat() error (testbugdir/input/00000000000000000061) : No such file or directory
>      BUG: 00000000000000000061 appears twice!
>      unlink() error (testbugdir/output/00000000000000000061) : No such file or directory
> 
> or:
> 
>      ./a.out 50 61
>      BUG: 0000000000000000000000000000000000000000000000000000000000020 appears twice!
>      stat() error (testbugdir/input/0000000000000000000000000000000000000000000000000000000000020) : No such file or directory
>      unlink() error (testbugdir/output/0000000000000000000000000000000000000000000000000000000000020) : No such file or directory
> 
> First parameter is the number of files, second the file name length.
> 
> I have traced this problem back to linux-2.6.10-rc1-bk18 and all kernels
> after this one are effected. linux-2.6.10-rc1-bk17 is okay. If I reverse
> the following patch in linux-2.6.10-rc1-bk18, readdir() works again
> correctly:

This patch was added by Ted & Andrew because of some other problem when
handling duplicate hash values.  It looks like the relevant thread is at:

http://marc.theaimsgroup.com/?t=110062880800001&r=1&w=4

#       When there are more than one entry in fname linked list, the current
#       implementation of ext3_dx_readdir() can not traverse all entries
#       correctly in the case that call_filldir() fails.
#   
#       If we use system call readdir() to read entries in a directory which
#       happens that "." and ".." in the same fname linked list.  Each time we
#       call readdir(), it will return the "." entry and never returns 0 which
#       indicates that all entries are read.
#   
#       Although chances that more than one entry are in one fname linked list
#       are very slim, it does exist.

> diff -Nru linux-2.6.10-rc1-bk17/fs/ext3/dir.c linux-2.6.10-rc1-bk18/fs/ext3/dir.c
> --- linux-2.6.10-rc1-bk17/fs/ext3/dir.c	2004-10-18 23:54:30.000000000 +0200
> +++ linux-2.6.10-rc1-bk18/fs/ext3/dir.c	2004-12-05 16:44:21.000000000 +0100
> @@ -418,7 +418,7 @@
>  				get_dtype(sb, fname->file_type));
>  		if (error) {
>  			filp->f_pos = curr_pos;
> -			info->extra_fname = fname->next;
> +			info->extra_fname = fname;
>  			return error;
>  		}
>  		fname = fname->next;
> @@ -457,9 +457,12 @@
>  	 * If there are any leftover names on the hash collision
>  	 * chain, return them first.
>  	 */
> -	if (info->extra_fname &&
> -	    call_filldir(filp, dirent, filldir, info->extra_fname))
> -		goto finished;
> +	if (info->extra_fname) {
> +		if(call_filldir(filp, dirent, filldir, info->extra_fname))
> +			goto finished;
> +		else
> +			goto next_entry;
> +	}
>  
>  	if (!info->curr_node)
>  		info->curr_node = rb_first(&info->root);
> @@ -492,7 +495,7 @@
>  		info->curr_minor_hash = fname->minor_hash;
>  		if (call_filldir(filp, dirent, filldir, fname))
>  			break;
> -
> +next_entry:
>  		info->curr_node = rb_next(info->curr_node);
>  		if (!info->curr_node) {
>  			if (info->next_hash == ~0) {
> 
> Regards,
> Holger
> -- 
> #include <stdio.h>
> #include <string.h>
> #include <stdlib.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <dirent.h>
> #include <fcntl.h>
> #include <errno.h>
> 
> int
> main(int argc, char *argv[])
> {
>    int           fd, filename_length, i, j, no_of_files;
>    char          pathname[256], *ptr,
>                  prevname[256],
>                  to_pathname[256], *to_ptr;
>    DIR           *dp;
>    struct dirent *p_dir;
>    struct stat   stat_buf;
> 
>    if (argc != 3)
>    {
>       fprintf(stderr, "Usage: %s <no. of files> <filename length>\n", argv[0]);
>       exit(1);
>    }
>    else
>    {
>       no_of_files = atoi(argv[1]);
>       filename_length = atoi(argv[2]);
>    }
> 
>    /* Create necessary dirs. */
>    (void)mkdir("testbugdir", S_IRUSR|S_IWUSR|S_IXUSR);
>    (void)mkdir("testbugdir/input", S_IRUSR|S_IWUSR|S_IXUSR);
>    (void)mkdir("testbugdir/output", S_IRUSR|S_IWUSR|S_IXUSR);
> 
>    /* Create input files. */
>    strcpy(pathname, "testbugdir/input/");
>    ptr = pathname + strlen(pathname);
>    for (i = 0; i < no_of_files; i++)
>    {
>       sprintf(ptr, "%0*d", filename_length, i);
>       if ((fd = open(pathname, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR)) == -1)
>       {
>          fprintf(stderr, "open() error %s : %s\n", pathname, strerror(errno));
>          exit(1);
>       }
>       close(fd);
>    }
> 
>    /* Move input files to output. */
>    strcpy(to_pathname, "testbugdir/output/");
>    to_ptr = to_pathname + strlen(to_pathname);
>    *ptr = '\0';
>    if ((dp = opendir(pathname)) == NULL)
>    {
>       fprintf(stderr, "opendir() error (%s) : %s\n",
>               pathname, strerror(errno));
>       exit(1);
>    }
>    prevname[0] = '\0';
>    while ((p_dir = readdir(dp)) != NULL)
>    {
>       if (p_dir->d_name[0] == '.')
>       {
>          continue;
>       }
>       if (strcmp(prevname, p_dir->d_name) == 0)
>       {
>          fprintf(stderr, "BUG: %s appears twice!\n", p_dir->d_name);
>       }
>       strcpy(prevname, p_dir->d_name);
>       strcpy(ptr, p_dir->d_name);
>       if (stat(pathname, &stat_buf) < 0)
>       {
>          fprintf(stderr, "stat() error (%s) : %s\n",
>                  pathname, strerror(errno));
>          continue;
>       }
>       strcpy(to_ptr, p_dir->d_name);
>       if (rename(pathname, to_pathname) == -1)
>       {
>          fprintf(stderr, "rename() error (file %d) : %s\n",
>                  pathname, strerror(errno));
>       }
>    }
>    (void)closedir(dp);
> 
>    /* Remove everyting. */
>    *to_ptr = '\0';
>    if ((dp = opendir(to_pathname)) == NULL)
>    {
>       fprintf(stderr, "opendir() error (%s) : %s\n",
>               to_pathname, strerror(errno));
>       exit(1);
>    }
>    prevname[0] = '\0';
>    while ((p_dir = readdir(dp)) != NULL)
>    {
>       if (p_dir->d_name[0] == '.')
>       {
>          continue;
>       }
>       if (strcmp(prevname, p_dir->d_name) == 0)
>       {
>          fprintf(stderr, "BUG: %s appears twice!\n", p_dir->d_name);
>       }
>       strcpy(prevname, p_dir->d_name);
>       strcpy(to_ptr, p_dir->d_name);
>       if (unlink(to_pathname) == -1)
>       {
>          fprintf(stderr, "unlink() error (%s) : %s\n",
>                  to_pathname, strerror(errno));
>          continue;
>       }
>    }
>    (void)closedir(dp);
>    if (rmdir("testbugdir/input") == -1)
>    {
>       fprintf(stderr, "rmdir() error (testbugdir/input) : %s\n",
>               strerror(errno));
>    }
>    if (rmdir("testbugdir/output") == -1)
>    {
>       fprintf(stderr, "rmdir() error (testbugdir/output) : %s\n",
>               strerror(errno));
>    }
>    if (rmdir("testbugdir") == -1)
>    {
>       fprintf(stderr, "rmdir() error (testbugdir) : %s\n", strerror(errno));
>    }
> 
>    exit(0);
> }


Cheers, Andreas
--
Andreas Dilger
http://sourceforge.net/projects/ext2resize/
http://members.shaw.ca/adilger/             http://members.shaw.ca/golinux/


[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: BUG in fs/ext3/dir.c
  2004-12-07  9:56 BUG in fs/ext3/dir.c Holger Kiehl
  2004-12-07 18:16 ` Andreas Dilger
@ 2004-12-07 20:11 ` Theodore Ts'o
  2004-12-08 14:37   ` Holger Kiehl
  1 sibling, 1 reply; 4+ messages in thread
From: Theodore Ts'o @ 2004-12-07 20:11 UTC (permalink / raw)
  To: Holger Kiehl; +Cc: linux-kernel

On Tue, Dec 07, 2004 at 09:56:26AM +0000, Holger Kiehl wrote:
> [Sorry if you get this twice. This was send to ext3-users@redhat.com and
>  the authors of ext3, but got no responce.]

I'm on ext3-users, but I didn't get the e-mail.....  so this is the
first time I've seen this.

> When using readdir() on a directory with many files or long file names
> it can happen that it returns the same file name twice. Attached is
> a program that demonstrates this. 

Thanks for the test case, I'm currently looking at the problem....

						- Ted

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: BUG in fs/ext3/dir.c
  2004-12-07 20:11 ` Theodore Ts'o
@ 2004-12-08 14:37   ` Holger Kiehl
  0 siblings, 0 replies; 4+ messages in thread
From: Holger Kiehl @ 2004-12-08 14:37 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: linux-kernel

On Tue, 7 Dec 2004, Theodore Ts'o wrote:

> On Tue, Dec 07, 2004 at 09:56:26AM +0000, Holger Kiehl wrote:
> > [Sorry if you get this twice. This was send to ext3-users@redhat.com and
> >  the authors of ext3, but got no responce.]
> 
> I'm on ext3-users, but I didn't get the e-mail.....  so this is the
> first time I've seen this.
> 
> > When using readdir() on a directory with many files or long file names
> > it can happen that it returns the same file name twice. Attached is
> > a program that demonstrates this. 
> 
> Thanks for the test case, I'm currently looking at the problem....
> 
I see that Andrew Morton took out the relevant patch and that
Kris Karas reported some filesytem corruption. In my case it did NOT
corrupt the filesystem. I just checked it after some millions of files
went through the system for some days.

Holger

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2004-12-08 14:37 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-12-07  9:56 BUG in fs/ext3/dir.c Holger Kiehl
2004-12-07 18:16 ` Andreas Dilger
2004-12-07 20:11 ` Theodore Ts'o
2004-12-08 14:37   ` Holger Kiehl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox