diff --git a/NEWS b/NEWS index 62a0f4176..70d0fc7f8 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,11 @@ GNU coreutils NEWS -*- outline -*- after a tab character are accounted for correctly. [bugs introduced in coreutils-9.8] + 'ls' no longer omits ACL indicators or capability coloring for subsequent + files on the same file system after an unsupported security context + lookup on a file without an ACL. + [bug introduced in coreutils-9.6] + 'realpath' without -s, or 'readlink' with -e,-f,-m no longer loop infinitely when resolving a symbolic link which resolves to a path beginning with the symbolic link itself. diff --git a/src/ls.c b/src/ls.c index 288b657a2..8e6706647 100644 --- a/src/ls.c +++ b/src/ls.c @@ -3255,7 +3255,10 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f, errno = 0; int n = file_has_aclinfo (file, ai, flags); int err = errno; + /* A security-context error does not imply that ACLs are unsupported. + Require an unsupported attribute listing too. */ if (f->stat_ok && n <= 0 && !acl_errno_valid (err) + && ai->size < 0 && !acl_errno_valid (ai->u.err) && (!(flags & ACL_GET_SCONTEXT) || !acl_errno_valid (ai->scontext_err))) { unsupported_return = n; diff --git a/tests/local.mk b/tests/local.mk index 06d8f934d..ce88b44f6 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -716,6 +716,7 @@ all_tests = \ tests/ln/target-1.sh \ tests/ls/a-option.sh \ tests/ls/acl.sh \ + tests/ls/acl-cache.sh \ tests/ls/abmon-align.sh \ tests/ls/birthtime.sh \ tests/ls/block-size.sh \ diff --git a/tests/ls/acl-cache.sh b/tests/ls/acl-cache.sh new file mode 100755 index 000000000..fb7007f86 --- /dev/null +++ b/tests/ls/acl-cache.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# A failed security-context lookup must not disable ACL detection. +# This failed from v9.6 to v9.12 inclusive. + +# 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 . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ ls +require_strace_ getxattr,listxattr +require_setfacl_ + +grep '^#define USE_ACL 1' $CONFIG_HEADER > /dev/null || + skip_ 'insufficient ACL support' + +touch acl plain || framework_failure_ +uid=$(id -u) || framework_failure_ +setfacl -m "u:$uid:rw" acl && setfacl -b plain || framework_failure_ + +ls -l acl > exp || framework_failure_ +grep '^..........+' exp > /dev/null || skip_ 'ls does not report ACLs' + +# Require injection to affect only the selected path. +strace --quiet=all -o /dev/null -P plain \ + -e fault=listxattr,llistxattr:error=EIO ls -l acl > out 2> err && +compare exp out || skip_ 'strace does not support path-filtered injection' + +# Ensure a failed getxattr doesn't induce failure to lookup ACLs. +# Also check the fallback when listxattr fails with E2BIG. +for list_fault in '' '-e fault=listxattr,llistxattr:error=E2BIG'; do + strace --quiet=all -o trace -P plain \ + -e trace=listxattr,llistxattr,getxattr,lgetxattr \ + -e fault=getxattr,lgetxattr:error=EOPNOTSUPP \ + $list_fault ls -l acl plain acl > out 2> err || fail=1 + test "$DEBUG" && cat trace + compare /dev/null err || fail=1 + test "$(grep -c '^..........+' out)" = 2 || fail=1 +done + +grep ' (INJECTED)' trace > /dev/null || + skip_ 'strace did not inject the requested failure' + +Exit $fail diff --git a/tests/ls/acl.sh b/tests/ls/acl.sh index 74e63226b..729b1b632 100755 --- a/tests/ls/acl.sh +++ b/tests/ls/acl.sh @@ -52,4 +52,9 @@ gap1=$(ls -l pad/f1 | sed -n 's/^[^+]*+\( *\)[0-9].*/\1/p') gap5=$(ls -l pad | sed -n 's/^[^+]*+\( *\)[0-9].*/\1/p' | head -n1) test "x$gap1" = "x$gap5" || fail=1 +# A file without an ACL must not suppress ACLs on subsequent files. +touch pad/plain && setfacl -b pad/plain || framework_failure_ +ls -l pad/f1 pad/plain pad/f1 > out || fail=1 +test "$(grep -c '^..........+' out)" = 2 || fail=1 + Exit $fail