From a7ec8caffe1a48590f5e3da9400080ab8a6ec303 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 22 Sep 2007 10:02:09 +0200 Subject: 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. --- src/c99-to-c89.diff | 6 +++--- src/remove.c | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/c99-to-c89.diff b/src/c99-to-c89.diff index 3e66bc480..01e213beb 100644 --- a/src/c99-to-c89.diff +++ b/src/c99-to-c89.diff @@ -42,7 +42,7 @@ diff -upr src/remove.c src/remove.c if (!yesno ()) return RM_USER_DECLINED; -@@ -1515,6 +1519,7 @@ rm_1 (Dirstack_state *ds, char const *fi +@@ -1533,6 +1537,7 @@ rm_1 (Dirstack_state *ds, char const *fi return RM_ERROR; } @@ -50,7 +50,7 @@ diff -upr src/remove.c src/remove.c struct stat st; cache_stat_init (&st); cycle_check_init (&ds->cycle_check_state); -@@ -1537,6 +1542,7 @@ rm_1 (Dirstack_state *ds, char const *fi +@@ -1555,6 +1560,7 @@ rm_1 (Dirstack_state *ds, char const *fi AD_push_initial (ds); AD_INIT_OTHER_MEMBERS (); @@ -58,7 +58,7 @@ diff -upr src/remove.c src/remove.c enum RM_status status = remove_entry (AT_FDCWD, ds, filename, DT_UNKNOWN, &st, x); if (status == RM_NONEMPTY_DIR) -@@ -1555,6 +1561,8 @@ rm_1 (Dirstack_state *ds, char const *fi +@@ -1573,6 +1579,8 @@ rm_1 (Dirstack_state *ds, char const *fi ds_clear (ds); return status; } 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; -- cgit v1.2.3-70-g09d2