mirror of
git://git.sv.gnu.org/coreutils.git
synced 2026-07-25 10:16:53 +02:00
rmdir: fix --ignore-fail-on-non-empty with permissions errors
Since v6.10-21-ged5c4e7 `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
This commit is contained in:
@@ -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]
|
||||
|
||||
|
||||
@@ -421,6 +421,7 @@ Matthew Arnison maffew@cat.org.au
|
||||
Matthew Braun matthew@ans.net
|
||||
Matthew Clarke Matthew_Clarke@mindlink.bc.ca
|
||||
Matthew M. Boedicker matthewm@boedicker.org
|
||||
Matthew Pfeiffer spferical@gmail.com
|
||||
Matthew S. Levine mslevine@theory.lcs.mit.edu
|
||||
Matthew Smith matts@bluesguitar.org
|
||||
Matthew Swift swift@alum.mit.edu
|
||||
|
||||
+12
-10
@@ -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,9 @@ 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)
|
||||
&& errno == 0 /* definitely non empty */)));
|
||||
}
|
||||
|
||||
/* Remove any empty parent directories of DIR.
|
||||
@@ -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)
|
||||
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)
|
||||
|
||||
+4
-1
@@ -285,7 +285,9 @@ readdir_ignoring_dot_and_dotdot (DIR *dirp)
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if DIR is determined to be an empty directory. */
|
||||
/* Return true if DIR is determined to be an empty directory.
|
||||
Return false with ERRNO==0 if DIR is a non empty directory.
|
||||
Return false if not able to determine if directory empty. */
|
||||
static inline bool
|
||||
is_empty_dir (int fd_cwd, char const *dir)
|
||||
{
|
||||
@@ -310,6 +312,7 @@ is_empty_dir (int fd_cwd, char const *dir)
|
||||
dp = readdir_ignoring_dot_and_dotdot (dirp);
|
||||
saved_errno = errno;
|
||||
closedir (dirp);
|
||||
errno = saved_errno;
|
||||
if (dp != NULL)
|
||||
return false;
|
||||
return saved_errno == 0 ? true : false;
|
||||
|
||||
@@ -29,4 +29,21 @@ 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
|
||||
# assume empty dir if unreadable entries (so failure to remove diagnosed)
|
||||
rm x/y/z || framework_failure_
|
||||
chmod a-r x/y || framework_failure_
|
||||
returns_ 1 rmdir --ignore-fail-on-non-empty x/y || fail=1
|
||||
test -d x/y || fail=1
|
||||
|
||||
Exit $fail
|
||||
|
||||
Reference in New Issue
Block a user