summaryrefslogtreecommitdiff
path: root/src/remove.c
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>2007-09-22 10:02:09 +0200
committerJim Meyering <jim@meyering.net>2007-09-22 13:27:57 +0200
commita7ec8caffe1a48590f5e3da9400080ab8a6ec303 (patch)
tree2f60406ee7f34139c53def242c7bf89005074761 /src/remove.c
parent920b4416c147ecb76731137158da8bcc9041fecd (diff)
downloadcoreutils-a7ec8caffe1a48590f5e3da9400080ab8a6ec303.tar.xz
rm: give a sensible diagnostic when failing to remove a symlink
On some systems (those with openat et al), when rm would fail to remove a symlink, it would fail with the misleading diagnostic, "Too many levels of symbolic links". * NEWS: Mention the bug fix. * src/remove.c (is_nondir_lstat): New function. (remove_entry): Use it to catch failed-to-remove symlink (and any other non-dir) here so that we don't fall through and try to treat it as directory, which -- with a symlink -- would provoke the bogus ELOOP failure. * tests/rm/fail-eacces: Add a test for the above. * src/c99-to-c89.diff: Adjust offsets.
Diffstat (limited to 'src/remove.c')
-rw-r--r--src/remove.c19
1 files changed, 18 insertions, 1 deletions
diff --git a/src/remove.c b/src/remove.c
index aae7a8888..ac1010953 100644
--- a/src/remove.c
+++ b/src/remove.c
@@ -937,6 +937,21 @@ is_dir_lstat (int fd_cwd, char const *filename, struct stat *st)
return is_dir;
}
+/* Return true if FILENAME is a non-directory.
+ Otherwise, including the case in which lstat fails, return false.
+ *ST is FILENAME's tstatus.
+ Do not modify errno. */
+static inline bool
+is_nondir_lstat (int fd_cwd, char const *filename, struct stat *st)
+{
+ int saved_errno = errno;
+ bool is_non_dir =
+ (cache_fstatat (fd_cwd, filename, st, AT_SYMLINK_NOFOLLOW) == 0
+ && !S_ISDIR (st->st_mode));
+ errno = saved_errno;
+ return is_non_dir;
+}
+
#define DO_UNLINK(Fd_cwd, Filename, X) \
do \
{ \
@@ -1066,7 +1081,9 @@ remove_entry (int fd_cwd, Dirstack_state const *ds, char const *filename,
errno = EISDIR;
if (! x->recursive
- || (cache_stat_ok (st) && !S_ISDIR (st->st_mode)))
+ || (cache_stat_ok (st) && !S_ISDIR (st->st_mode))
+ || (errno == EACCES && is_nondir_lstat (fd_cwd, filename, st))
+ )
{
if (ignorable_missing (x, errno))
return RM_OK;