Subject: [PATCH] fix NFSv4 acl detection on F39
From: Ondrej Valousek <[email-masked]>
To: [email-masked]
Cc: Ondrej Valousek <[email-masked]>
Date: Mon, 1 May 2023 21:38:08 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

Sending the other proposed patch fixing the NFSv4 acl detection on Fedora39.
It's bit longer, but I think should be better.
Please review.

---
 lib/file-has-acl.c | 60 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 49 insertions(+), 11 deletions(-)

diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index b31a2ea252..27da1c0607 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -121,6 +121,30 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
 
   return 0;
 }
+
+/* Return 1 if ATTR is found in the xattr list given by BUF */
+int have_xattr (char const *attr, char *buf, ssize_t buflen)
+{
+  char const *key = buf;
+  /* Loop over the list of zero terminated strings with the
+     attribute keys. Use the remaining buffer length to determine
+     the end of the list. */
+  while (buflen > 0) {
+    int keylen;
+
+    if (strcmp (key, attr) == 0)
+      {
+        /* we don't expect this function will be called again
+           so we free the buffer so caller does not have to */
+        free (buf);
+        return 1;
+      }
+    keylen = strlen (key) + 1;
+    buflen -= keylen;
+    key += keylen;
+  }
+  return 0;
+}
 #endif
 
 /* Return 1 if NAME has a nontrivial access control list,
@@ -139,25 +163,37 @@ file_has_acl (char const *name, struct stat const *sb)
 
 # if GETXATTR_WITH_POSIX_ACLS
 
-      ssize_t ret;
+      ssize_t ret, buflen;
+      char *attrlist;
       int initial_errno = errno;
 
-      ret = getxattr (name, XATTR_NAME_POSIX_ACL_ACCESS, NULL, 0);
-      if (ret < 0 && errno == ENODATA)
-        ret = 0;
-      else if (ret > 0)
+      ret = listxattr (name, NULL, 0);
+      if (ret <= 0)
+        return ret;
+      attrlist = malloc (ret);
+      if (attrlist == NULL)
+        return -1;
+      buflen = listxattr (name, attrlist, ret);
+      if (buflen == -1)
+        {
+          free (attrlist);
+          return - acl_errno_valid (errno);
+        }
+
+      if(have_xattr (XATTR_NAME_POSIX_ACL_ACCESS, attrlist, buflen))
         return 1;
+      else
+        ret = 0;
 
-      if (ret == 0 && S_ISDIR (sb->st_mode))
+      if (S_ISDIR (sb->st_mode))
         {
-          ret = getxattr (name, XATTR_NAME_POSIX_ACL_DEFAULT, NULL, 0);
-          if (ret < 0 && errno == ENODATA)
-            ret = 0;
-          else if (ret > 0)
+          if (have_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, attrlist, buflen))
             return 1;
+          else
+            ret = 0;
         }
 
-      if (ret < 0)
+      if (have_xattr (XATTR_NAME_NFSV4_ACL, attrlist, buflen))
         {
           /* Check for NFSv4 ACLs.  The max length of a trivial
              ACL is 6 words for owner, 6 for group, 7 for everyone,
@@ -186,6 +222,8 @@ file_has_acl (char const *name, struct stat const *sb)
               errno = initial_errno;
             }
         }
+      else
+        free (attrlist);
       if (ret < 0)
         return - acl_errno_valid (errno);
       return ret;
-- 
2.40.1