* src/copy.c (copy_attr_quote): Return a xstrdup'd copy of the result of
quoteaf, so that slot 0 of quotearg can be overwritten by a subsequent
call without overwriting the previously quoted string.
(copy_attr_free): Free the string. Remove MAYBE_UNUSED from the string.
* tests/cp/xattr.sh: Add a getlimits_ and uses_strace_ invocation.
(block_setxattr): New function. Use it to check the error/warning
message when setting extended attributes on the destination fails.
* NEWS: Mention the bug fix.
Discovered by Anthropic's OSS Scanner, as bug ANT-2026-BPGHOHDX.
---
NEWS | 5 +++++
src/copy.c | 9 ++++++---
tests/cp/xattr.sh | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 48 insertions(+), 3 deletions(-)
diff --git a/NEWS b/NEWS
index a20815642..ff06b642f 100644
--- a/NEWS
+++ b/NEWS
@@ -45,6 +45,11 @@ GNU coreutils NEWS -*-
outline -*-
operating on dangling symbolic links.
[bug introduced in coreutils-5.3.0]
+ 'cp --preserve=xattr' and 'mv' now refer to the destination file name in
error
+ and warning messages when setting an extended attribute fails. Previously,
+ they would mistakenly print the extended attribute name in its place.
+ [bug introduced with extended attribute support in coreutils-7.1]
+
'cp --verbose --parents' now quotes the names of the parent directories that
it creates and their sources.
[This bug was present in "the beginning".]
diff --git a/src/copy.c b/src/copy.c
index 7964a6eeb..cf9be7389 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -287,13 +287,16 @@ copy_attr_allerror (MAYBE_UNUSED struct error_context
*ctx,
static char const *
copy_attr_quote (MAYBE_UNUSED struct error_context *ctx, char const *str)
{
- return quoteaf (str);
+ int const saved_errno = errno;
+ char *result = xstrdup (quoteaf (str));
+ errno = saved_errno;
+ return result;
}
static void
-copy_attr_free (MAYBE_UNUSED struct error_context *ctx,
- MAYBE_UNUSED char const *str)
+copy_attr_free (MAYBE_UNUSED struct error_context *ctx, char const *str)
{
+ free (bad_cast (str));
}
/* Exclude SELinux extended attributes that are otherwise handled,
diff --git a/tests/cp/xattr.sh b/tests/cp/xattr.sh
index 6e36c5f57..fee42a171 100755
--- a/tests/cp/xattr.sh
+++ b/tests/cp/xattr.sh
@@ -20,6 +20,8 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ cp mv ginstall
+getlimits_
+uses_strace_
touch src dest || framework_failure_
cp --preserve=xattr -n src dest \
@@ -128,4 +130,39 @@ failed to set xattr of file $b_other
EOF
fi
+block_setxattr ()
+{
+ setxattr_calls=setxattr,lsetxattr,fsetxattr
+ strace --quiet=all -o /dev/null -e inject=$setxattr_calls:error=EIO "$@"
+}
+
+if block_setxattr true; then
+ echo a >a &&
+ cp a exp-b &&
+ setfattr -n "$xattr_name" -v "$xattr_value" a || framework_failure_
+ returns_ 1 block_setxattr cp --preserve=xattr a b >out 2>err-t || fail=1
+ # The source file may have other extended attributes other than the
+ # one we have specified that cannot be set, e.g., "security.selinux".
+ # Ignore them for the purposes of this test.
+ echo "cp: setting attribute '$xattr_name' for 'b': $EIO" >exp-err \
+ || framework_failure_
+ grep -F "$xattr_name" err-t >err || fail=1
+ compare exp-err err || fail=1
+ compare /dev/null out || fail=1
+ compare exp-b b || fail=1
+ returns_ 1 getfattr -n "$xattr_name" b || fail=1
+ # The same warning is printed for 'mv', when it is unable to use rename.
+ if test $test_mv -eq 1; then
+ rm -f "$b_other" || framework_failure_
+ block_setxattr mv a "$b_other" >out 2>err-t || fail=1
+ echo "mv: setting attribute '$xattr_name' for '$b_other': $EIO" >exp-err \
+ || framework_failure_
+ grep -F "$xattr_name" err-t >err || fail=1
+ compare exp-err err || fail=1
+ compare /dev/null out || fail=1
+ compare exp-b "$b_other" || fail=1
+ returns_ 1 getfattr -n "$xattr_name" "$b_other" || fail=1
+ fi
+fi
+
Exit $fail
--
2.56.0