bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: problem with readdir in Darwin-6.5 and hfs?


From: Jim Meyering
Subject: Re: problem with readdir in Darwin-6.5 and hfs?
Date: Fri, 02 May 2003 14:57:55 +0200

Paul Eggert <address@hidden> wrote:
> Jim Meyering <address@hidden> writes:
>
>> I just went to remove a directory on a local `hfs' file system from a
>> powerpc-apple-darwin6.5 system and was surprised to see the `rm -rf' fail.
>
> Wow, a local filesystem?  FreeBSD bug kern/26142 says only that the
> bug occurs with an NFS filesystem.

Yep.  `df -T -h' says it's of type `hfs' and with capacity of 38G.

> http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/26142
>
>
>> Here's a demo:
>
> Perhaps you should submit your demo to the FreeBSD folks, as a
> followup to that bug.  They might raise the priority of fixing
> kern/26142.  That bug was filed over two years ago, with Severity
> "serious" and Priority "medium".  Your demo might talk them into
> raising the priority.

Thanks.  I've put it on my list.

>> I don't want yet another configure-time test for broken readdir
>> (SunOS had a problem very much like this long ago, but I obviated
>> the work-around code when I rewrote rm).
>
> I don't see any other alternative, other than something even worse
> like "#ifdef FreeBSD".
>
>> The work-around here is probably to call rewinddir from a readdir
>> wrapper to be used only on deficient systems.

Now, I am nearly convinced that it's not possible to do this with a wrapper.
Calling rewinddir unconditionally seems like it would lead to an infinite
loop when rm does not remove all entries in a directory (e.g., user
declined, unlink failed, etc.).

So I'm resigned to modifying remove.c itself.
Tentative patch below.

The configure-time test in m4/readdir.m4
(last included in fileutils-4.1.11) does almost
exactly what is needed here.

> 26142 gives the workaround "Call rewinddir after each unlink call".
> Is that what you mean?  Or were you just going to call rewinddir after
> readdir returns NULL?

That's what I meant, but I think it would have been insufficient.
The patch below does this:

        Work around nasty readdir bug with Darwin6.5 and hfs file system.
        * src/remove.c (IF_READDIR_NEEDS_REWINDDIR): Define.
        [! HAVE_WORKING_READDIR] (remove_cwd_entries): If readdir has just
        returned NULL and there has been at least one successful unlink or
        rmdir call since the opendir or previous rewinddir, then call
        rewinddir and reiterate the loop.

>> But then do we do the extra rewinddir unconditionally, or add
>> bookkeeping so we incur the cost only if we've removed more than
>> some minimum number of entries in a given directory.
>
> I would avoid the bookkeeping.  Just call rewinddir before each
> readdir.  Safe and simple.  (Only on buggy systems, of course.)

Index: remove.c
===================================================================
RCS file: /fetish/cu/src/remove.c,v
retrieving revision 1.86
diff -u -p -u -p -r1.86 remove.c
--- remove.c    2 May 2003 10:44:32 -0000       1.86
+++ remove.c    2 May 2003 12:35:11 -0000
@@ -55,6 +55,12 @@
 # define ROOT_CAN_UNLINK_DIRS 1
 #endif
 
+#if HAVE_WORKING_READDIR
+# define IF_READDIR_NEEDS_REWINDDIR(Code) /* empty */
+#else
+# define IF_READDIR_NEEDS_REWINDDIR(Code) Code
+#endif
+
 enum Ternary
   {
     T_UNKNOWN = 2,
@@ -804,6 +810,7 @@ remove_cwd_entries (Dirstack_state *ds, 
   DIR *dirp = opendir (".");
   struct AD_ent *top = AD_stack_top (ds);
   enum RM_status status = top->status;
+  IF_READDIR_NEEDS_REWINDDIR (int performed_unlink_or_rmdir = 0);
 
   assert (VALID_STATUS (status));
   *subdir = NULL;
@@ -839,6 +846,19 @@ remove_cwd_entries (Dirstack_state *ds, 
              /* Arrange to give a diagnostic after exiting this loop.  */
              dirp = NULL;
            }
+         else
+           {
+#if ! HAVE_WORKING_READDIR
+             /* On buggy systems, call rewinddir if we've called unlink
+                or rmdir since the opendir or a previous rewinddir.  */
+             if (performed_unlink_or_rmdir)
+               {
+                 rewinddir (dirp);
+                 performed_unlink_or_rmdir = 0;
+                 continue;
+               }
+#endif
+           }
          break;
        }
 
@@ -856,7 +876,9 @@ remove_cwd_entries (Dirstack_state *ds, 
       switch (tmp_status)
        {
        case RM_OK:
-         /* do nothing */
+         /* On buggy systems, record the fact that we've just
+            removed a directory entry.  */
+         IF_READDIR_NEEDS_REWINDDIR (performed_unlink_or_rmdir = 1);
          break;
 
        case RM_ERROR:




reply via email to

[Prev in Thread] Current Thread [Next in Thread]