Hi
I have a system that precaches directory content at opendir call and I
found that coreutils-6.7 rm -r command doesn't work on it (it used to work
fine in coreutils 5).
The problem is this: when walking up to the root in directory tree, rm
opens parent directory with opendir, then deletes its subdirectory with
rmdir and then starts reading the parent with readdir --- readdir reads
just deleted entry, rm tries to delete it again and fails.
opendir caching is allowed by standard
http://www.opengroup.org/onlinepubs/007908799/xsh/readdir.html ("If a file
is removed from or added to the directory after the most recent call to
opendir() or rewinddir(), whether a subsequent call to readdir() returns
an entry for that file is unspecified."), so the system behaves correctly
and rm has a bug.
Here is a trace when I tried rm -rf /A and there was four directories
/A/B/C/D --- it correctly deletes D, but reads entry for 'D' again and
fails.
I made a fix --- it modifies AD_pop_and_chdir so that it only returns fd
of a directory but doesn't try to do fdopendir and do fdopendir in
remove_dir after the subdirectory is deleted - it seems to work but it
makes the file even more messy.
Mikulas
The trace of rm -rf /A, I added printfs to opendir, readdir and closedir
to see what happens:
opendir '/A' -> BFFEF950
readdir BFFEF950, pos 0: name '.', dt 4
readdir BFFEF950, pos 1: name '..', dt 4
readdir BFFEF950, pos 2: name 'B', dt 4
opendir '/A/B' -> BFFEF9F0
closedir: BFFEF950
readdir BFFEF9F0, pos 0: name '.', dt 4
readdir BFFEF9F0, pos 1: name '..', dt 4
readdir BFFEF9F0, pos 2: name 'C', dt 4
opendir '/A/B/C' -> BFFEF980
closedir: BFFEF9F0
readdir BFFEF980, pos 0: name '.', dt 4
readdir BFFEF980, pos 1: name '..', dt 4
readdir BFFEF980, pos 2: name 'D', dt 4
opendir '/A/B/C/D' -> BFFEF9C0
closedir: BFFEF980
readdir BFFEF9C0, pos 0: name '.', dt 4
readdir BFFEF9C0, pos 1: name '..', dt 4
closedir: BFFEF9C0
--- note here: it opens the parent directory and then deletes the
subdirectory --- the subsequent readdir will read just deleted
entry
opendir '/A/B/C' -> BFFEBE00
removed directory: `A/B/C/D'
readdir BFFEBE00, pos 0: name '.', dt 4
readdir BFFEBE00, pos 1: name '..', dt 4
--- here: entry 'D' no longer exists, but it is read from cached
information on opendir
readdir BFFEBE00, pos 2: name 'D', dt 4
closedir: BFFEBE00
opendir '/A/B' -> BFFEBE00
readdir BFFEBE00, pos 0: name '.', dt 4
readdir BFFEBE00, pos 1: name '..', dt 4
readdir BFFEBE00, pos 2: name 'C', dt 4
closedir: BFFEBE00
opendir '/A' -> BFFEBE00
readdir BFFEBE00, pos 0: name '.', dt 4
readdir BFFEBE00, pos 1: name '..', dt 4
readdir BFFEBE00, pos 2: name 'B', dt 4
closedir: BFFEBE00
--- src/remove.c_ 2006-12-19 05:20:54.000000000 +0200
+++ src/remove.c 2006-12-19 06:41:29.000000000 +0200
@@ -432,7 +436,7 @@
directory (usually now empty) from which we're coming, and which
corresponds to the input value of *DIRP. */
static char *
-AD_pop_and_chdir (DIR **dirp, Dirstack_state *ds)
+AD_pop_and_chdir (DIR **dirp, int *fdp, Dirstack_state *ds)
{
struct AD_ent *leaf_dir_ent = AD_stack_top(ds);
struct dev_ino leaf_dev_ino = leaf_dir_ent->dev_ino;
@@ -499,22 +503,15 @@
{
error (0, 0, _("FATAL: directory %s changed dev/ino"),
quote (full_filename (".")));
- goto close_and_next;
- }
+ close_and_next:;
+ close (fd);
- *dirp = fdopendir (fd);
- if (*dirp == NULL)
- {
- error (0, errno, _("FATAL: cannot return to .. from %s"),
- quote (full_filename (".")));
+ next_cmdline_arg:
- close_and_next:;
- close (fd);
-
- next_cmdline_arg:;
free (prev_dir);
longjmp (ds->current_arg_jumpbuf, 1);
}
+ *fdp = fd;
}
else
{
@@ -524,9 +521,10 @@
quote (full_filename (prev_dir)));
goto next_cmdline_arg;
}
- *dirp = NULL;
+ *fdp = AT_FDCWD;
}
+ *dirp = NULL;
return prev_dir;
}
@@ -1384,9 +1382,9 @@
{
/* The name of the directory that we have just processed,
nominally removing all of its contents. */
- char *empty_dir = AD_pop_and_chdir (&dirp, ds);
- int fd = (dirp != NULL ? dirfd (dirp) : AT_FDCWD);
- assert (dirp != NULL || AD_stack_height (ds) == 1);
+ int fd;
+ char *empty_dir = AD_pop_and_chdir (&dirp, &fd, ds);
+ assert (fd != AT_FDCWD || AD_stack_height (ds) == 1);
/* Try to remove EMPTY_DIR only if remove_cwd_entries succeeded. */
if (tmp_status == RM_OK)
@@ -1405,6 +1403,7 @@
{
free (empty_dir);
status = s;
+ if (fd != AT_FDCWD) close (fd);
goto closedir_and_return;
}
@@ -1426,7 +1425,18 @@
free (empty_dir);
- if (AD_stack_height (ds) == 1)
+ if (fd != AT_FDCWD)
+ {
+ dirp = fdopendir (fd);
+ if (dirp == NULL)
+ {
+ error (0, errno, _("FATAL: cannot return to .. from %s"),
+ quote (full_filename (".")));
+ close (fd);
+ longjmp (ds->current_arg_jumpbuf, 1);
+ }
+ }
+ else
break;
}
}
_______________________________________________
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils