On 31/01/2020 17:51, Pádraig Brady wrote:
On 31/01/2020 01:46, Matthew Pfeiffer wrote:
'rmdir --ignore-fail-on-non-empty' would not report errors on non-empty
directories that fail for a different reason.
---
   src/rmdir.c | 10 ++++++----
   1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/rmdir.c b/src/rmdir.c
index c9f417957..7b253ab0d 100644
--- a/src/rmdir.c
+++ b/src/rmdir.c
@@ -133,18 +133,19 @@ remove_parents (char *dir)
           prog_fprintf (stdout, _("removing directory, %s"), quoteaf (dir));
ok = (rmdir (dir) == 0);
+      int rmdir_errno = errno;
if (!ok)
           {
             /* Stop quietly if --ignore-fail-on-non-empty. */
-          if (ignorable_failure (errno, dir))
+          if (ignorable_failure (rmdir_errno, dir))
               {
                 ok = true;
               }
             else
               {
                 /* Barring race conditions, DIR is expected to be a directory. 
 */
-              error (0, errno, _("failed to remove directory %s"),
+              error (0, rmdir_errno, _("failed to remove directory %s"),
                        quoteaf (dir));
               }
             break;
@@ -233,12 +234,13 @@ main (int argc, char **argv)
if (rmdir (dir) != 0)
           {
-          if (ignorable_failure (errno, dir))
+          int rmdir_errno = errno;
+          if (ignorable_failure (rmdir_errno, dir))
               continue;
/* Here, the diagnostic is less precise, since we have no idea
                whether DIR is a directory.  */
-          error (0, errno, _("failed to remove %s"), quoteaf (dir));
+          error (0, rmdir_errno, _("failed to remove %s"), quoteaf (dir));
             ok = false;
           }
         else if (remove_empty_parents)


This looks like a regression introduced in v6.10-21-ged5c4e7

For reference, this was originally discussed at:
https://lists.gnu.org/archive/html/bug-coreutils/2008-01/msg00283.html

I.E. is_empty_dir() is globbering errno, and thus
a non specific and non terminating warning is output,
rather than a specific error, and exit.

Actually I think the key issue is not errno handling,
but a logic error fixed with:

@@ -102,7 +102,7 @@ ignorable_failure (int error_number, char const *dir)
   return (ignore_fail_on_non_empty
           && (errno_rmdir_non_empty (error_number)
               || (errno_may_be_empty (error_number)
-                  && is_empty_dir (AT_FDCWD, dir))));
+                  && ! is_empty_dir (AT_FDCWD, dir))));


Attached is a full patch to address these issues.
cheers,
Pádraig
>From 3b379d77ec342b66a36d4f39435dccc121ca86eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <p...@draigbrady.com>
Date: Thu, 30 Jan 2020 20:46:40 -0500
Subject: [PATCH] rmdir: fix --ignore-fail-on-non-empty with permissions errors

Since 6.11 `rmdir --ignore-fail-on-non-empty` had reversed
the failure status for directories that failed to be removed
for permissions reasons.  I.E. it would have returned a
failure status for such non empty dirs, and vice versa.

* src/rmdir.c (errno_may_be_non_empty): Rename from the
more confusing errno_may_be_empty(), and remove the EEXIST
case (specific to Solaris), which is moot here since
handled in errno_rmdir_non_empty().
(ignorable_failure): Fix the logic error so that
_non_ empty dirs are deemed to have ignorable failures.
(main): Fix clobbering of errno by is_empty_dir().
(remove_parents): Likewise.
* tests/rmdir/ignore.sh: Add a test case.
* THANKS.in: Add reporter who fixed the errno handling.
* NEWS: Mention the bug fix.
Fixes https://bugs.gnu.org/39364
---
 NEWS                  |  5 +++++
 THANKS.in             |  1 +
 src/rmdir.c           | 21 +++++++++++----------
 tests/rmdir/ignore.sh | 12 ++++++++++++
 4 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/NEWS b/NEWS
index 5231b84ac..8a349634e 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,11 @@ GNU coreutils NEWS                                    -*- outline -*-
   (like Solaris 10 and Solaris 11).
   [bug introduced in coreutils-8.31]
 
+  rmdir --ignore-fail-on-non-empty now works correctly for directories
+  that fail to be removed due to permission issues.  Previously the exit status
+  was reversed, failing for non empty and succeeding for empty directories.
+  [bug introduced in coreutils-6.11]
+
   'shuf -r -n 0 file' no longer mistakenly reads from standard input.
   [bug introduced with the --repeat feature in coreutils-8.22]
 
diff --git a/THANKS.in b/THANKS.in
index 23b089754..a667197ed 100644
--- a/THANKS.in
+++ b/THANKS.in
@@ -420,6 +420,7 @@ Matt Swift                          sw...@alum.mit.edu
 Matthew Arnison                     maf...@cat.org.au
 Matthew Braun                       matt...@ans.net
 Matthew Clarke                      matthew_cla...@mindlink.bc.ca
+Matthew Pfeiffer                    spferi...@gmail.com
 Matthew M. Boedicker                matth...@boedicker.org
 Matthew S. Levine                   mslev...@theory.lcs.mit.edu
 Matthew Smith                       ma...@bluesguitar.org
diff --git a/src/rmdir.c b/src/rmdir.c
index c9f417957..a2f0f0813 100644
--- a/src/rmdir.c
+++ b/src/rmdir.c
@@ -77,16 +77,15 @@ errno_rmdir_non_empty (int error_number)
 }
 
 /* Return true if when rmdir fails with errno == ERROR_NUMBER
-   the directory may be empty.  */
+   the directory may be non empty.  */
 static bool
-errno_may_be_empty (int error_number)
+errno_may_be_non_empty (int error_number)
 {
   switch (error_number)
     {
     case EACCES:
     case EPERM:
     case EROFS:
-    case EEXIST:
     case EBUSY:
       return true;
     default:
@@ -101,8 +100,8 @@ ignorable_failure (int error_number, char const *dir)
 {
   return (ignore_fail_on_non_empty
           && (errno_rmdir_non_empty (error_number)
-              || (errno_may_be_empty (error_number)
-                  && is_empty_dir (AT_FDCWD, dir))));
+              || (errno_may_be_non_empty (error_number)
+                  && ! is_empty_dir (AT_FDCWD, dir))));
 }
 
 /* Remove any empty parent directories of DIR.
@@ -133,18 +132,19 @@ remove_parents (char *dir)
         prog_fprintf (stdout, _("removing directory, %s"), quoteaf (dir));
 
       ok = (rmdir (dir) == 0);
+      int rmdir_errno = errno;
 
-      if (!ok)
+      if (! ok)
         {
           /* Stop quietly if --ignore-fail-on-non-empty. */
-          if (ignorable_failure (errno, dir))
+          if (ignorable_failure (rmdir_errno, dir))
             {
               ok = true;
             }
           else
             {
               /* Barring race conditions, DIR is expected to be a directory.  */
-              error (0, errno, _("failed to remove directory %s"),
+              error (0, rmdir_errno, _("failed to remove directory %s"),
                      quoteaf (dir));
             }
           break;
@@ -233,12 +233,13 @@ main (int argc, char **argv)
 
       if (rmdir (dir) != 0)
         {
-          if (ignorable_failure (errno, dir))
+          int rmdir_errno = errno;
+          if (ignorable_failure (rmdir_errno, dir))
             continue;
 
           /* Here, the diagnostic is less precise, since we have no idea
              whether DIR is a directory.  */
-          error (0, errno, _("failed to remove %s"), quoteaf (dir));
+          error (0, rmdir_errno, _("failed to remove %s"), quoteaf (dir));
           ok = false;
         }
       else if (remove_empty_parents)
diff --git a/tests/rmdir/ignore.sh b/tests/rmdir/ignore.sh
index d000c30cb..0d2be25ed 100755
--- a/tests/rmdir/ignore.sh
+++ b/tests/rmdir/ignore.sh
@@ -29,4 +29,16 @@ test -d "$cwd/a/x" || fail=1
 test -d "$cwd/a/b" && fail=1
 test -d "$cwd/a/b/c" && fail=1
 
+# Ensure that with --ignore-fail-on-non-empty, we still fail, e.g., for EPERM.
+# Between 6.11 and 8.31, the following rmdir would mistakenly succeed.
+mkdir -p x/y || framework_failure_
+chmod a-w x || framework_failure_
+returns_ 1 rmdir --ignore-fail-on-non-empty x/y || fail=1
+test -d x/y || fail=1
+# Between 6.11 and 8.31, the following rmdir would mistakenly fail,
+# and also give a non descript error
+touch x/y/z || framework_failure_
+rmdir --ignore-fail-on-non-empty x/y || fail=1
+test -d x/y || fail=1
+
 Exit $fail
-- 
2.24.1

Reply via email to