]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
tests: cover symlink-cycle detection in chmod/chown -RL
authorSylvestre Ledru <sylvestre@debian.org>
Fri, 29 May 2026 22:43:06 +0000 (00:43 +0200)
committerPádraig Brady <P@draigBrady.com>
Tue, 2 Jun 2026 10:43:06 +0000 (11:43 +0100)
With -L, a recursive chmod/chown follows symlinks while descending;
a symlink pointing back to an ancestor forms a cycle that must be
detected and not descended into forever.

* tests/chmod/symlinks.sh: Add a symlink-cycle case for chmod -RL.
* tests/chown/deref.sh: Likewise for chown -RL.
https://github.com/coreutils/coreutils/pull/274

tests/chmod/symlinks.sh
tests/chown/deref.sh

index f5f87860d2a9b7a27a081de6d0cdd9032f9f0e80..e1b01259a4f3a5926b933b234e1659ea3cfbd106 100755 (executable)
@@ -84,4 +84,15 @@ for deref in '' '--deref' '-R'; do
   returns_ 1 chmod 755 $deref a/dangle 2>err || fail=1
 done
 
+# A symlink pointing back to an ancestor forms a cycle.  With -L, chmod
+# follows symlinks while recursing, so it must detect the cycle and stop
+# rather than descend into it forever.
+mkdir -p cyc/b/c || framework_failure_
+ln -s "$(pwd)/cyc" cyc/b/c/d || framework_failure_
+chmod -vRL +r cyc > out 2>&1 || fail=1
+# The symlinked directory is visited exactly once...
+grep -F "'cyc/b/c/d'" out > /dev/null || { cat out; fail=1; }
+# ...and recursion does not re-enter it through the cycle.
+grep -F "'cyc/b/c/d/b'" out > /dev/null && { cat out; fail=1; }
+
 Exit $fail
index 85efd66c2c3e064f175c042c10ca1802ed506d09..9fbb93f86fd162be8a22c0ab7499a8490cc59453 100755 (executable)
@@ -35,4 +35,15 @@ EOF
 
 compare exp out || fail=1
 
+# A symlink pointing back to an ancestor forms a cycle.  With -L, chown
+# follows symlinks while recursing, so it must detect the cycle and stop
+# rather than descend into it forever.
+mkdir -p cyc/b/c || framework_failure_
+ln -s "$(pwd)/cyc" cyc/b/c/d || framework_failure_
+chown -vRL $user cyc > out2 2>&1 || fail=1
+# The symlinked directory is visited exactly once...
+grep -F "'cyc/b/c/d'" out2 > /dev/null || { cat out2; fail=1; }
+# ...and recursion does not re-enter it through the cycle.
+grep -F "'cyc/b/c/d/b'" out2 > /dev/null && { cat out2; fail=1; }
+
 Exit $fail