mirror of
git://git.sv.gnu.org/coreutils.git
synced 2026-04-11 14:44:18 +02:00
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>. (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.
This commit is contained in:
27
src/remove.c
27
src/remove.c
@@ -762,14 +762,12 @@ remove_entry (Dirstack_state const *ds, char const *filename,
|
||||
|
||||
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)
|
||||
if (! x->recursive
|
||||
|| errno == ENOENT || errno == ENOTDIR
|
||||
|| errno == ELOOP || errno == ENAMETOOLONG)
|
||||
{
|
||||
/* some other error code. Report it and fail.
|
||||
Likewise, if we're trying to remove a directory without
|
||||
the --recursive option. */
|
||||
/* 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, char **subdir, struct stat *subdir_sb,
|
||||
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, char **subdir, struct stat *subdir_sb,
|
||||
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 const *dir, struct saved_cwd **cwd_state,
|
||||
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)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user