diff options
-rw-r--r-- | eval.c | 17 | ||||
-rwxr-xr-x | tests.sh | 14 | ||||
-rw-r--r-- | tests/test_L_delete.out | 2 |
3 files changed, 31 insertions, 2 deletions
@@ -277,8 +277,21 @@ bool eval_delete(const struct expr *expr, struct eval_state *state) { } int flag = 0; - if (ftwbuf->typeflag == BFTW_DIR) { - flag |= AT_REMOVEDIR; + if (ftwbuf->at_flags & AT_SYMLINK_NOFOLLOW) { + if (ftwbuf->typeflag == BFTW_DIR) { + flag |= AT_REMOVEDIR; + } + } else { + // We need to know the actual type of the path, not what it points to + struct bfs_stat sb; + if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->at_flags | AT_SYMLINK_NOFOLLOW, 0, &sb) == 0) { + if (S_ISDIR(sb.mode)) { + flag |= AT_REMOVEDIR; + } + } else { + eval_error(state); + return false; + } } if (unlinkat(ftwbuf->at_fd, ftwbuf->at_path, flag) != 0) { @@ -266,6 +266,7 @@ bsd_tests=( test_ok_stdin test_okdir_stdin test_delete + test_L_delete test_rm test_regex test_iregex @@ -369,6 +370,7 @@ gnu_tests=( test_perm_symbolic_slash test_perm_leading_plus_symbolic_slash test_delete + test_L_delete test_regex test_iregex test_regex_parens @@ -1177,6 +1179,18 @@ function test_delete() { bfs_diff scratch } +function test_L_delete() { + rm -rf scratch/* + mkdir scratch/foo + mkdir scratch/bar + ln -s ../foo scratch/bar/baz + + # Don't try to rmdir() a symlink + invoke_bfs -L scratch/bar -delete || return 1 + + bfs_diff scratch +} + function test_rm() { rm -rf scratch/* touchp scratch/foo/bar/baz diff --git a/tests/test_L_delete.out b/tests/test_L_delete.out new file mode 100644 index 0000000..ed0e9a1 --- /dev/null +++ b/tests/test_L_delete.out @@ -0,0 +1,2 @@ +scratch +scratch/foo |