summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2018-07-11 23:59:10 -0400
committerTavian Barnes <tavianator@tavianator.com>2018-07-12 00:36:52 -0400
commit3529cc45cb25630a109cb650d5813a1f2ba405cb (patch)
treef143ca063614f86e90b7856a186c64678aff300d
parentf25efd83a941d143dff939d353113415b3a7fa90 (diff)
downloadbfs-3529cc45cb25630a109cb650d5813a1f2ba405cb.tar.xz
eval: Fix -delete when following symlinks.
Same bug as https://savannah.gnu.org/bugs/?46305. Please don't ever do this though.
-rw-r--r--eval.c17
-rwxr-xr-xtests.sh14
-rw-r--r--tests/test_L_delete.out2
3 files changed, 31 insertions, 2 deletions
diff --git a/eval.c b/eval.c
index a4ee5c5..9308a17 100644
--- a/eval.c
+++ b/eval.c
@@ -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) {
diff --git a/tests.sh b/tests.sh
index b74e067..3e47374 100755
--- a/tests.sh
+++ b/tests.sh
@@ -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