bug-coreutils
[Top][All Lists]
Advanced

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

Re: rm -r gives up before going deeper upon encountering hindrances


From: Paul Eggert
Subject: Re: rm -r gives up before going deeper upon encountering hindrances
Date: Sat, 08 May 2004 00:39:39 -0700
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

Jim Meyering <address@hidden> writes:

> +  /* Accept either EISDIR, EPERM, or EACCES as an indication that FILENAME 
> may
> +     be a directory.  POSIX says that unlink must set errno to EPERM when it
> +     fails to remove a directory, while Linux-2.4.18 sets it to EISDIR.
> +     unlink fails with EACCES when the parent directory is write-protected.  
> */

Aren't other error numbers possible?  For example, suppose the
directory is in a read-only filesystem, but there's a writeable
filesystem mounted underneath it.  Then errno might equal EROFS even
though it's a directory and 'rm' should recurse underneath it.

Come to think of it, the current approach in remove.c seems backwards.
Instead of checking _against_ error numbers like EPERM that suggest
that the file might be a directory, the code should be checking _for_
error numbers like ENOENT and ENOTDIR that can be returned only if the
file cannot possibly be a directory.

Not that I'm an expert in this part of the code, but here's a proposed
patch.

2004-05-08  Paul Eggert  <address@hidden>

        Fix bug where "rm" gave up too easily, reported by Dan Jacobsen in
        <http://mail.gnu.org/archive/html/bug-coreutils/2004-05/msg00013.html>.

        * src/remove.c (remove_entry): Check for errno values like ENOENT
        that show the file cannot be directory, instead of for errno
        values like EPERM that show the file might be a directory.  This
        is necessary because, when a single unlink() call has multiple
        reasons to fail, it can set errno to any of those reasons; it's
        only the rare errno value like ENOENT that excludes all the other
        possible reasons to fail even when the file is a directory.
        (remove_cwd_entries): Don't attempt chdir if the file is known
        to not be a directory.
        (remove_dir): Use the same method that remove_cwd_entries uses
        (for some reason they differed).  Don't assert that saved_errno
        must be EPERM; it might be just about anything.

Index: src/remove.c
===================================================================
RCS file: /home/meyering/coreutils/cu/src/remove.c,v
retrieving revision 1.102
diff -p -u -r1.102 remove.c
--- src/remove.c        27 Apr 2004 15:00:32 -0000      1.102
+++ src/remove.c        8 May 2004 07:31:07 -0000
@@ -762,14 +762,12 @@ remove_entry (Dirstack_state const *ds, 
 
   DO_UNLINK (filename, x);
 
-  /* Accept either EISDIR or EPERM as an indication that FILENAME may be
-     a directory.  POSIX says that unlink must set errno to EPERM when it
-     fails to remove a directory, while Linux-2.4.18 sets it to EISDIR.  */
-  if ((errno != EISDIR && errno != EPERM) || ! x->recursive)
-    {
-      /* some other error code.  Report it and fail.
-        Likewise, if we're trying to remove a directory without
-        the --recursive option.  */
+  if (! x->recursive
+      || errno == ENOENT || errno == ENOTDIR
+      || errno == ELOOP || errno == ENAMETOOLONG)
+    {
+      /* Either --recursive is not in effect, or the file cannot be a
+        directory.  Report the unlink problem and fail.  */
       error (0, errno, _("cannot remove %s"),
             quote (full_filename (filename)));
       return RM_ERROR;
@@ -871,8 +869,7 @@ remove_cwd_entries (Dirstack_state *ds, 
        case RM_NONEMPTY_DIR:
          {
            /* Save a copy of errno, in case the preceding unlink (from
-              remove_entry's DO_UNLINK) of a non-directory failed due
-              to EPERM.  */
+              remove_entry's DO_UNLINK) of a non-directory failed.  */
            int saved_errno = errno;
 
            /* Record dev/ino of F so that we can compare
@@ -882,7 +879,8 @@ remove_cwd_entries (Dirstack_state *ds, 
              error (EXIT_FAILURE, errno, _("cannot lstat %s"),
                     quote (full_filename (f)));
 
-           if (chdir (f))
+           errno = ENOTDIR;
+           if (! S_ISDIR (subdir_sb->st_mode) || chdir (f) != 0)
              {
                /* It is much more common that we reach this point for an
                   inaccessible directory.  Hence the second diagnostic, below.
@@ -989,14 +987,11 @@ remove_dir (Dirstack_state *ds, char con
       return RM_ERROR;
     }
 
-  if (chdir (dir))
+  errno = ENOTDIR;
+  if (! S_ISDIR (dir_sb.st_mode) || chdir (dir) != 0)
     {
-      if (! S_ISDIR (dir_sb.st_mode))
+      if (errno == ENOTDIR)
        {
-         /* This happens on Linux-2.4.18 when a non-privileged user tries
-            to delete a file that is owned by another user in a directory
-            like /tmp that has the S_ISVTX flag set.  */
-         assert (saved_errno == EPERM);
          error (0, saved_errno,
                 _("cannot remove %s"), quote (full_filename (dir)));
        }




reply via email to

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