Hi,

On Tue, Jun 03, 2025 at 10:20:47PM +0000, bls 3427 wrote:
> Looks like the same issue. 
> 
> Any idea when this will end up in Debian 13?

The patch which is referenced seems to address the issue. Attached is
a proposed debdiff, Michael I guess this should be fixed for trixie in
time, but needs an unblock request (or maybe first better a
pre-approval given the involved package). Should we raise the severity
as well for the bug?

Regards,
Salvatore
diff -Nru coreutils-9.7/debian/changelog coreutils-9.7/debian/changelog
--- coreutils-9.7/debian/changelog      2025-04-20 19:39:09.000000000 +0200
+++ coreutils-9.7/debian/changelog      2025-06-04 16:48:41.000000000 +0200
@@ -1,3 +1,11 @@
+coreutils (9.7-2.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * copy-acl: port better to NFSv4 on GNU/Linux (Closes: #1107169) 
+    - Fixes "copy fails setting system.nfs4_acl extended attribute"
+
+ -- Salvatore Bonaccorso <car...@debian.org>  Wed, 04 Jun 2025 16:48:41 +0200
+
 coreutils (9.7-2) unstable; urgency=low
 
   * Build without y2038 support on hurd-i386 (Closes: #1078000)
diff -Nru coreutils-9.7/debian/patches/gnulib-acl-fixes.patch 
coreutils-9.7/debian/patches/gnulib-acl-fixes.patch
--- coreutils-9.7/debian/patches/gnulib-acl-fixes.patch 1970-01-01 
01:00:00.000000000 +0100
+++ coreutils-9.7/debian/patches/gnulib-acl-fixes.patch 2025-06-04 
16:47:03.000000000 +0200
@@ -0,0 +1,446 @@
+diff --git a/lib/acl.h b/lib/acl.h
+index 90fd24e152..e3c134fb41 100644
+--- a/lib/acl.h
++++ b/lib/acl.h
+@@ -79,6 +79,8 @@ struct aclinfo
+ bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST;
+ int file_has_acl (char const *, struct stat const *);
+ int file_has_aclinfo (char const *restrict, struct aclinfo *restrict, int);
++int fdfile_has_aclinfo (int, char const *restrict,
++                        struct aclinfo *restrict, int);
+ 
+ #if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
+ bool aclinfo_has_xattr (struct aclinfo const *, char const *)
+diff --git a/lib/copy-acl.c b/lib/copy-acl.c
+index c36f64e51d..2fce6c7d46 100644
+--- a/lib/copy-acl.c
++++ b/lib/copy-acl.c
+@@ -33,6 +33,7 @@
+    a valid file descriptor, use file descriptor operations, else use
+    filename based operations on SRC_NAME. Likewise for DEST_DESC and
+    DST_NAME.
++   MODE should be the source file's st_mode.
+    If access control lists are not available, fchmod the target file to
+    MODE.  Also sets the non-permission bits of the destination file
+    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
+diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
+index 66b920c1ab..a356ee0d0b 100644
+--- a/lib/file-has-acl.c
++++ b/lib/file-has-acl.c
+@@ -85,6 +85,13 @@ smack_new_label_from_path (MAYBE_UNUSED const char *path,
+ {
+   return -1;
+ }
++static ssize_t
++smack_new_label_from_file (MAYBE_UNUSED int fd,
++                           MAYBE_UNUSED const char *xattr,
++                           MAYBE_UNUSED char **label)
++{
++  return -1;
++}
+ # endif
+ static bool
+ is_smack_enabled (void)
+@@ -115,14 +122,16 @@ aclinfo_may_indicate_xattr (struct aclinfo const *ai)
+ 
+ static bool
+ has_xattr (char const *xattr, struct aclinfo const *ai,
+-           MAYBE_UNUSED char const *restrict name, MAYBE_UNUSED int flags)
++           int fd, char const *restrict name, int flags)
+ {
+   if (ai && aclinfo_has_xattr (ai, xattr))
+     return true;
+   else if (!ai || aclinfo_may_indicate_xattr (ai))
+     {
+-      int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
+-                (name, xattr, NULL, 0));
++      int ret = (fd < 0
++                 ? ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
++                    (name, xattr, NULL, 0))
++                 : fgetxattr (fd, xattr, NULL, 0));
+       if (0 <= ret || (errno == ERANGE || errno == E2BIG))
+         return true;
+     }
+@@ -145,11 +154,12 @@ aclinfo_has_xattr (struct aclinfo const *ai, char const 
*xattr)
+   return false;
+ }
+ 
+-/* Get attributes of the file NAME into AI, if USE_ACL.
++/* Get attributes of the file FD aka NAME into AI, if USE_ACL.
++   Ignore FD if it is negative.
+    If FLAGS & ACL_GET_SCONTEXT, also get security context.
+    If FLAGS & ACL_SYMLINK_FOLLOW, follow symbolic links.  */
+ static void
+-get_aclinfo (char const *name, struct aclinfo *ai, int flags)
++get_aclinfo (int fd, char const *name, struct aclinfo *ai, int flags)
+ {
+   int scontext_err = ENOTSUP;
+   ai->buf = ai->u.__gl_acl_ch;
+@@ -163,7 +173,9 @@ get_aclinfo (char const *name, struct aclinfo *ai, int 
flags)
+         = (flags & ACL_SYMLINK_FOLLOW ? listxattr : llistxattr);
+       while (true)
+         {
+-          ai->size = lsxattr (name, ai->buf, acl_alloc);
++          ai->size = (fd < 0
++                      ? lsxattr (name, ai->buf, acl_alloc)
++                      : flistxattr (fd, ai->buf, acl_alloc));
+           if (0 < ai->size)
+             break;
+           ai->u.err = ai->size < 0 ? errno : 0;
+@@ -171,7 +183,9 @@ get_aclinfo (char const *name, struct aclinfo *ai, int 
flags)
+             break;
+ 
+           /* The buffer was too small.  Find how large it should have been.  
*/
+-          ssize_t size = lsxattr (name, NULL, 0);
++          ssize_t size = (fd < 0
++                          ? lsxattr (name, NULL, 0)
++                          : flistxattr (fd, NULL, 0));
+           if (size <= 0)
+             {
+               ai->size = size;
+@@ -214,9 +228,13 @@ get_aclinfo (char const *name, struct aclinfo *ai, int 
flags)
+         {
+           if (ai->size < 0 || aclinfo_has_xattr (ai, XATTR_NAME_SMACK))
+             {
+-              ssize_t r = smack_new_label_from_path (name, "security.SMACK64",
+-                                                     flags & 
ACL_SYMLINK_FOLLOW,
+-                                                     &ai->scontext);
++              static char const SMACK64[] = "security.SMACK64";
++              ssize_t r =
++                (fd < 0
++                 ? smack_new_label_from_path (name, SMACK64,
++                                              flags & ACL_SYMLINK_FOLLOW,
++                                              &ai->scontext)
++                 : smack_new_label_from_file (fd, SMACK64, &ai->scontext));
+               scontext_err = r < 0 ? errno : 0;
+             }
+         }
+@@ -226,8 +244,10 @@ get_aclinfo (char const *name, struct aclinfo *ai, int 
flags)
+           if (ai->size < 0 || aclinfo_has_xattr (ai, XATTR_NAME_SELINUX))
+             {
+               ssize_t r =
+-                ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon)
+-                 (name, &ai->scontext));
++                (fd < 0
++                 ? ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon)
++                    (name, &ai->scontext))
++                 : fgetfilecon (fd, &ai->scontext));
+               scontext_err = r < 0 ? errno : 0;
+ #  ifndef SE_SELINUX_INLINE
+               /* Gnulib's selinux-h module is not in use, so getfilecon and
+@@ -362,11 +382,13 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
+ }
+ #endif
+ 
+-#if (!USE_LINUX_XATTR && USE_ACL && HAVE_ACL_GET_FD \
+-     && !HAVE_ACL_EXTENDED_FILE && !HAVE_ACL_TYPE_EXTENDED \
+-     && !HAVE_ACL_GET_LINK_NP)
+-# include <fcntl.h>
+-# ifdef O_PATH
++#if (!USE_LINUX_XATTR && USE_ACL && !HAVE_ACL_EXTENDED_FILE \
++     && !HAVE_ACL_TYPE_EXTENDED)
++
++# if HAVE_ACL_GET_FD && !HAVE_ACL_GET_LINK_NP
++#  include <fcntl.h>
++#  ifdef O_PATH
++#   define acl_get_fd_np(fd, type) acl_get_fd (fd)
+ 
+ /* Like acl_get_file, but do not follow symbolic links.  */
+ static acl_t
+@@ -381,8 +403,24 @@ acl_get_link_np (char const *name, acl_type_t type)
+   errno = err;
+   return r;
+ }
+-#  define HAVE_ACL_GET_LINK_NP 1
++#   define HAVE_ACL_GET_LINK_NP 1
++#  endif
+ # endif
++
++static acl_t
++acl_get_fdfile (int fd, char const *name, acl_type_t type, int flags)
++{
++  acl_t (*get) (char const *, acl_type_t) = acl_get_file;
++# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10, Cygwin >= 2.5 */
++  if (0 <= fd)
++    return acl_get_fd_np (fd, type);
++  if (! (flags & ACL_SYMLINK_FOLLOW))
++    get = acl_get_link_np;
++# else
++  /* Ignore FD and FLAGS, unfortunately.  */
++# endif
++  return get (name, type);
++}
+ #endif
+ 
+ /* Return 1 if NAME has a nontrivial access control list,
+@@ -398,14 +436,35 @@ acl_get_link_np (char const *name, acl_type_t type)
+    If the d_type value is not known, use DT_UNKNOWN though this may be less
+    efficient.  */
+ int
+-file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
++file_has_aclinfo (char const *restrict name,
+                   struct aclinfo *restrict ai, int flags)
++{
++  return fdfile_has_aclinfo (-1, name, ai, flags);
++}
++
++/* Return 1 if FD aka NAME has a nontrivial access control list,
++   0 if ACLs are not supported, or if NAME has no or only a base ACL,
++   and -1 (setting errno) on error.  Note callers can determine
++   if ACLs are not supported as errno is set in that case also.
++   Ignore FD if it is negative.
++   Set *AI to ACL info regardless of return value.
++   FLAGS should be a <dirent.h> d_type value, optionally ORed with
++     - _GL_DT_NOTDIR if it is known that NAME is not a directory,
++     - ACL_GET_SCONTEXT to retrieve security context and return 1 if present,
++     - ACL_SYMLINK_FOLLOW to follow the link if NAME is a symbolic link;
++       otherwise do not follow them if possible.
++   If the d_type value is not known, use DT_UNKNOWN though this may be less
++   efficient.  */
++int
++fdfile_has_aclinfo (MAYBE_UNUSED int fd,
++                    MAYBE_UNUSED char const *restrict name,
++                    struct aclinfo *restrict ai, int flags)
+ {
+   MAYBE_UNUSED unsigned char d_type = flags & UCHAR_MAX;
+ 
+ #if USE_LINUX_XATTR
+   int initial_errno = errno;
+-  get_aclinfo (name, ai, flags);
++  get_aclinfo (fd, name, ai, flags);
+ 
+   if (!aclinfo_may_indicate_xattr (ai) && ai->size <= 0)
+     {
+@@ -418,11 +477,11 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+      In earlier Fedora the two types of ACLs were mutually exclusive.
+      Attempt to work correctly on both kinds of systems.  */
+ 
+-  if (!has_xattr (XATTR_NAME_NFSV4_ACL, ai, name, flags))
++  if (!has_xattr (XATTR_NAME_NFSV4_ACL, ai, fd, name, flags))
+     return
+-      (has_xattr (XATTR_NAME_POSIX_ACL_ACCESS, ai, name, flags)
++      (has_xattr (XATTR_NAME_POSIX_ACL_ACCESS, ai, fd, name, flags)
+        || ((d_type == DT_DIR || d_type == DT_UNKNOWN)
+-           && has_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, ai, name, flags)));
++           && has_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, ai, fd, name, flags)));
+ 
+   /* A buffer large enough to hold any trivial NFSv4 ACL.
+      The max length of a trivial NFSv4 ACL is 6 words for owner,
+@@ -432,8 +491,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+      everyone is another word to hold "EVERYONE@".  */
+   uint32_t buf[2 * (6 + 6 + 7)];
+ 
+-  int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
+-             (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf));
++  int ret = (fd < 0
++             ? ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
++                (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf))
++             : fgetxattr (fd, XATTR_NAME_NFSV4_ACL, buf, sizeof buf));
+   if (ret < 0)
+     switch (errno)
+       {
+@@ -467,20 +528,23 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+       /* On Linux, acl_extended_file is an optimized function: It only
+          makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
+          ACL_TYPE_DEFAULT.  */
+-    ret = ((flags & ACL_SYMLINK_FOLLOW
+-            ? acl_extended_file
+-            : acl_extended_file_nofollow)
+-           (name));
++    ret = (fd < 0
++           ? ((flags & ACL_SYMLINK_FOLLOW
++               ? acl_extended_file
++               : acl_extended_file_nofollow)
++              (name))
++           : acl_extended_fd (fd));
+ #   elif HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
+     /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
+        and acl_get_file (name, ACL_TYPE_DEFAULT)
+        always return NULL / EINVAL.  There is no point in making
+        these two useless calls.  The real ACL is retrieved through
+-       acl_get_file (name, ACL_TYPE_EXTENDED).  */
+-    acl_t acl = ((flags & ACL_SYMLINK_FOLLOW
+-                  ? acl_get_file
+-                  : acl_get_link_np)
+-                 (name, ACL_TYPE_EXTENDED));
++       ACL_TYPE_EXTENDED.  */
++    acl_t acl =
++      (fd < 0
++       ? ((flags & ACL_SYMLINK_FOLLOW ? acl_get_file : acl_get_link_np)
++          (name, ACL_TYPE_EXTENDED))
++       : acl_get_fd_np (fd, ACL_TYPE_EXTENDED));
+     if (acl)
+       {
+         ret = acl_extended_nontrivial (acl);
+@@ -489,13 +553,8 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+     else
+       ret = -1;
+ #   else /* FreeBSD, NetBSD >= 10, IRIX, Tru64, Cygwin >= 2.5 */
+-    acl_t (*acl_get_file_or_link) (char const *, acl_type_t) = acl_get_file;
+-#    if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10, Cygwin >= 2.5 */
+-    if (! (flags & ACL_SYMLINK_FOLLOW))
+-      acl_get_file_or_link = acl_get_link_np;
+-#    endif
+ 
+-    acl_t acl = acl_get_file_or_link (name, ACL_TYPE_ACCESS);
++    acl_t acl = acl_get_fdfile (fd, name, ACL_TYPE_ACCESS, flags);
+     if (acl)
+       {
+         ret = acl_access_nontrivial (acl);
+@@ -517,7 +576,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+             && (d_type == DT_DIR
+                 || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR))))
+           {
+-            acl = acl_get_file_or_link (name, ACL_TYPE_DEFAULT);
++            acl = acl_get_fdfile (fd, name, ACL_TYPE_DEFAULT, flags);
+             if (acl)
+               {
+ #     ifdef __CYGWIN__ /* Cygwin >= 2.5 */
+@@ -562,7 +621,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+ 
+       /* Solaris 10 (newer version), which has additional API declared in
+          <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
+-         acl_fromtext, ...).  */
++         acl_fromtext, ...).
++
++         Ignore FD, unfortunately.  That is better than mishandling
++         ZFS-style ACLs, as the general case code does.  */
+       return acl_trivial (name);
+ 
+ #    else /* Solaris, Cygwin, general case */
+@@ -586,7 +648,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+ 
+         for (;;)
+           {
+-            count = acl (name, GETACL, alloc, entries);
++            count = (fd < 0
++                     ? acl (name, GETACL, alloc, entries)
++                     : facl (fd, GETACL, alloc, entries));
+             if (count < 0 && errno == ENOSPC)
+               {
+                 /* Increase the size of the buffer.  */
+@@ -657,7 +721,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+ 
+         for (;;)
+           {
+-            count = acl (name, ACE_GETACL, alloc, entries);
++            count = (fd < 0
++                     ? acl (name, ACE_GETACL, alloc, entries)
++                     : facl (fd, ACE_GETACL, alloc, entries));
+             if (count < 0 && errno == ENOSPC)
+               {
+                 /* Increase the size of the buffer.  */
+@@ -722,7 +788,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+         struct acl_entry entries[NACLENTRIES];
+         int count;
+ 
+-        count = getacl (name, NACLENTRIES, entries);
++        count = (fd < 0
++                 ? getacl (name, NACLENTRIES, entries)
++                 : fgetacl (fd, NACLENTRIES, entries));
+ 
+         if (count < 0)
+           {
+@@ -751,7 +819,8 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+             {
+               struct stat statbuf;
+ 
+-              if (stat (name, &statbuf) == -1 && errno != EOVERFLOW)
++              if ((fd < 0 ? stat (name, &statbuf) : fstat (fd, &statbuf)) < 0
++                  && errno != EOVERFLOW)
+                 return -1;
+ 
+               return acl_nontrivial (count, entries);
+@@ -765,6 +834,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+         struct acl entries[NACLVENTRIES];
+         int count;
+ 
++        /* Ignore FD, unfortunately.  */
+         count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries);
+ 
+         if (count < 0)
+@@ -809,7 +879,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+           /* The docs say that type being 0 is equivalent to ACL_ANY, but it
+              is not true, in AIX 5.3.  */
+           type.u64 = ACL_ANY;
+-          if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0)
++          if (0 <= (fd < 0
++                    ? aclx_get (name, 0, &type, aclbuf, &aclsize, &mode)
++                    : aclx_fget (fd, 0, &type, aclbuf, &aclsize, &mode)))
+             break;
+           if (errno == ENOSYS)
+             return 0;
+@@ -855,7 +927,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+ 
+       union { struct acl a; char room[4096]; } u;
+ 
+-      if (statacl ((char *) name, STX_NORMAL, &u.a, sizeof (u)) < 0)
++      if ((fd < 0
++           ? statacl ((char *) name, STX_NORMAL, &u.a, sizeof u)
++           : fstatacl (fd, STX_NORMAL, &u.a, sizeof u))
++          < 0)
+         return -1;
+ 
+       return acl_nontrivial (&u.a);
+@@ -866,6 +941,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+         struct acl entries[NACLENTRIES];
+         int count;
+ 
++        /* Ignore FD, unfortunately.  */
+         count = acl ((char *) name, ACL_GET, NACLENTRIES, entries);
+ 
+         if (count < 0)
+diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c
+index ad7966152a..282f4b2d2a 100644
+--- a/lib/qcopy-acl.c
++++ b/lib/qcopy-acl.c
+@@ -26,6 +26,7 @@
+ #if USE_XATTR
+ 
+ # include <attr/libattr.h>
++# include <dirent.h>
+ # include <string.h>
+ 
+ # if HAVE_LINUX_XATTR_H
+@@ -61,6 +62,7 @@ is_attr_permissions (const char *name, struct error_context 
*ctx)
+    a valid file descriptor, use file descriptor operations, else use
+    filename based operations on SRC_NAME. Likewise for DEST_DESC and
+    DST_NAME.
++   MODE should be the source file's st_mode.
+    If access control lists are not available, fchmod the target file to
+    MODE.  Also sets the non-permission bits of the destination file
+    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
+@@ -86,10 +88,29 @@ qcopy_acl (const char *src_name, int source_desc, const 
char *dst_name,
+      Functions attr_copy_* return 0 in case we copied something OR nothing
+      to copy */
+   if (ret == 0)
+-    ret = source_desc <= 0 || dest_desc <= 0
+-      ? attr_copy_file (src_name, dst_name, is_attr_permissions, NULL)
+-      : attr_copy_fd (src_name, source_desc, dst_name, dest_desc,
+-                      is_attr_permissions, NULL);
++    {
++      ret = source_desc <= 0 || dest_desc <= 0
++        ? attr_copy_file (src_name, dst_name, is_attr_permissions, NULL)
++        : attr_copy_fd (src_name, source_desc, dst_name, dest_desc,
++                        is_attr_permissions, NULL);
++
++      /* Copying can fail with EOPNOTSUPP even when the source
++         permissions are trivial (Bug#78328).  Don't report an error
++         in this case, as the chmod_or_fchmod suffices.  */
++      if (ret < 0 && errno == EOPNOTSUPP)
++        {
++          /* fdfile_has_aclinfo cares only about DT_DIR, _GL_DT_NOTDIR,
++             and DT_LNK (but DT_LNK is not possible here),
++             so use _GL_DT_NOTDIR | DT_UNKNOWN for other file types.  */
++          int flags = S_ISDIR (mode) ? DT_DIR : _GL_DT_NOTDIR | DT_UNKNOWN;
++
++          struct aclinfo ai;
++          if (!fdfile_has_aclinfo (source_desc, src_name, &ai, flags))
++            ret = 0;
++          aclinfo_free (&ai);
++          errno = EOPNOTSUPP;
++        }
++    }
+ #else
+   /* no XATTR, so we proceed the old dusty way */
+   struct permission_context ctx;
diff -Nru coreutils-9.7/debian/patches/series 
coreutils-9.7/debian/patches/series
--- coreutils-9.7/debian/patches/series 2025-04-08 19:57:40.000000000 +0200
+++ coreutils-9.7/debian/patches/series 2025-06-04 16:46:07.000000000 +0200
@@ -1 +1,2 @@
 72_id_checkngroups.patch
+gnulib-acl-fixes.patch

Reply via email to