It probably isn't super clear what this test case is for, so here is a
more obvious example:

    $ stat --format=%U /
    root
    $ id -un
    collin
    $ chmod = /
    chmod: changing permissions of '/': Operation not permitted

If one tries to skip calling chmod(2) because the file mode bits are
unchanged, they will get the incorrect behavior.

That obviously wouldn't work when the tests are run as root, so I
think that 'strace' is the best way to test this.

-- 8< --

* tests/chmod/only-op.sh: New file.
* tests/local.mk (all_test): Add the new test.
---
 tests/chmod/only-op.sh | 38 ++++++++++++++++++++++++++++++++++++++
 tests/local.mk         |  1 +
 2 files changed, 39 insertions(+)
 create mode 100755 tests/chmod/only-op.sh

diff --git a/tests/chmod/only-op.sh b/tests/chmod/only-op.sh
new file mode 100755
index 000000000..1d234e8c3
--- /dev/null
+++ b/tests/chmod/only-op.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Test that 'chmod' does not skip calling chmod(2) even when the file mode bits
+# are unchanged.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ chmod
+uses_strace_
+getlimits_
+
+echo 'hello' > file || framework_failure_
+
+cat <<EOF > exp || framework_failure_
+chmod: changing permissions of 'file': $EPERM
+EOF
+
+for op in '+' '-' '='; do
+  returns_ 1 strace -o /dev/null -P file -e quiet=path-resolution \
+    -e '/f?chmod' -e fault=all:error=EPERM chmod "$op" file 2>err \
+    || fail=1
+  compare exp err || fail=1
+done
+
+Exit $fail
diff --git a/tests/local.mk b/tests/local.mk
index 5f9f3d19b..aa71951f8 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -517,6 +517,7 @@ all_tests =                                 \
   tests/chmod/ignore-symlink.sh                        \
   tests/chmod/inaccessible.sh                  \
   tests/chmod/octal.sh                         \
+  tests/chmod/only-op.sh                       \
   tests/chmod/partial-fail.sh                  \
   tests/chmod/setgid.sh                                \
   tests/chmod/silent.sh                                \
-- 
2.53.0


Reply via email to