Problem reported by Ian Dall in <https://bugs.gnu.org/78328>
and by Thomas Clark in <https://bugzilla.redhat.com/2363149>.
* lib/file-has-acl.c (smack_new_label_from_file) [!HAVE_SMACK]:
New dummy function.
(has_xattr, get_aclinfo): New arg FD.  All callers changed.
Remove some unnecessary MAYBE_UNUSEDs.
(acl_get_fd_np): Fall back on acl_get_fd if this function is
needed but not available.
(acl_get_fdfile): New function, if needed.
(file_has_aclinfo): Reimplement in terms of ...
(fdfile_has_aclinfo): ... this new function,
which also has an FD argument.
* lib/qcopy-acl.c [USE_XATTR]: Include dirent.h, for DT_DIR etc.
(qcopy_acl): If attr_copy_file or attr_copy_fd fail with EOPNOTSUPP,
don’t fail if the source has a trivial ACL (this is the part
that fixes the bug; the rest is optimization).
* modules/qcopy-acl (Depends-on): Depend on dirent-h and
file-has-acl if $use_xattr.  Also, depend on acl-permissions
unconditionally, since qcopy-acl.c includes acl.h unconditionally.
---
 ChangeLog          |  23 ++++++
 NEWS               |   4 ++
 lib/acl.h          |   2 +
 lib/copy-acl.c     |   1 +
 lib/file-has-acl.c | 172 ++++++++++++++++++++++++++++++++-------------
 lib/qcopy-acl.c    |  29 ++++++--
 modules/qcopy-acl  |   4 +-
 7 files changed, 182 insertions(+), 53 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f2da0a6843..842a40a47f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2025-05-09  Paul Eggert  <egg...@cs.ucla.edu>
+
+       qcopy-acl: port better to NFSv4 on GNU/Linux
+       Problem reported by Ian Dall in <https://bugs.gnu.org/78328>
+       and by Thomas Clark in <https://bugzilla.redhat.com/2363149>.
+       * lib/file-has-acl.c (smack_new_label_from_file) [!HAVE_SMACK]:
+       New dummy function.
+       (has_xattr, get_aclinfo): New arg FD.  All callers changed.
+       Remove some unnecessary MAYBE_UNUSEDs.
+       (acl_get_fd_np): Fall back on acl_get_fd if this function is
+       needed but not available.
+       (acl_get_fdfile): New function, if needed.
+       (file_has_aclinfo): Reimplement in terms of ...
+       (fdfile_has_aclinfo): ... this new function,
+       which also has an FD argument.
+       * lib/qcopy-acl.c [USE_XATTR]: Include dirent.h, for DT_DIR etc.
+       (qcopy_acl): If attr_copy_file or attr_copy_fd fail with EOPNOTSUPP,
+       don’t fail if the source has a trivial ACL (this is the part
+       that fixes the bug; the rest is optimization).
+       * modules/qcopy-acl (Depends-on): Depend on dirent-h and
+       file-has-acl if $use_xattr.  Also, depend on acl-permissions
+       unconditionally, since qcopy-acl.c includes acl.h unconditionally.
+
 2025-05-09  Bruno Haible  <br...@clisp.org>
 
        HACKING: Update.
diff --git a/NEWS b/NEWS
index 57249ffc02..639760a5b9 100644
--- a/NEWS
+++ b/NEWS
@@ -74,6 +74,10 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2025-05-09  copy-acl        The mode_t arguments of qcopy_acl and xcopy_acl
+            qcopy-acl       should be the full st_mode of the source,
+                            not merely st_mode's permission bits.
+
 2025-04-29  hash-triple-simple  This module is renamed to hashcode-named-file.
 
 2025-01-02  string-desc     The function prefix is changed from string_desc_
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 --git a/modules/qcopy-acl b/modules/qcopy-acl
index e692a28625..8adedf7ad9 100644
--- a/modules/qcopy-acl
+++ b/modules/qcopy-acl
@@ -7,7 +7,9 @@ m4/acl.m4
 m4/xattr.m4
 
 Depends-on:
-acl-permissions [test "$use_xattr" != yes]
+acl-permissions
+dirent-h        [test "$use_xattr" = yes]
+file-has-acl    [test "$use_xattr" = yes]
 
 configure.ac:
 gl_QCOPY_ACL
-- 
2.49.0


Reply via email to