* 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


Reply via email to