On 2024-09-30 06:30, Bruno Haible wrote:
Did you mean 152 ? Or maybe 512 ?
I used "152" because that is what file-has-acl previously used. I
installed the first patch to explain this better.
I installed the 2nd patch as an optimization discovered after more
testing with coreutils.
Further improvements are in the pipeline to fix some coreutils bugs I
introduced with all this. These will involve using dirent stuff.
Speaking of dirent, I see that POSIX.1-2024 has a new function
posix_getdents that a quick web search suggests is only in Cygwin and
musl now. It would simplify parts of Gnulib, I think. But it can wait.From 0c55c3d7f74d42d273e0fffc8b774344eed65458 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 30 Sep 2024 09:24:09 -0700
Subject: [PATCH 1/2] file-has-acl: improve acl.h comment
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* lib/acl.h: Improve comment about ‘152’.
---
lib/acl.h | 5 ++++-
lib/file-has-acl.c | 3 +--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/lib/acl.h b/lib/acl.h
index 07ab04250d..1a627323ec 100644
--- a/lib/acl.h
+++ b/lib/acl.h
@@ -65,7 +65,10 @@ struct aclinfo
int err;
/* A small array of char, big enough for most listxattr results.
- The size is somewhat arbitrary. For internal use only. */
+ The size is somewhat arbitrary; it equals the max length of a
+ trivial NFSv4 ACL (a size used by file-has-acl.c in 2023-2024
+ but no longer relevant now), and a different value might be
+ better once experience is gained. For internal use only. */
char __gl_acl_ch[152];
} u;
};
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index 89faba565f..6924a616bd 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -349,8 +349,7 @@ file_has_aclinfo (char const *name, struct stat const *sb,
|| (S_ISDIR (sb->st_mode)
&& aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT))));
- /* If there is an NFSv4 ACL, follow up with a getxattr syscall
- to see whether the NFSv4 ACL is nontrivial. */
+ /* If there is an NFSv4 ACL, check whether it is nontrivial. */
if (nfsv4_acl)
{
/* A buffer large enough to hold any trivial NFSv4 ACL.
--
2.43.0
From 276260e7ec35181b0ea9fddf5bab397cf1061eca Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 30 Sep 2024 10:46:57 -0700
Subject: [PATCH 2/2] file-has-acl: improve performance on Linux symlinks
* lib/file-has-acl.c (USE_LINUX_XATTR): Now can be used outside #if.
(file_has_aclinfo): As an optimization on Linux, do not attempt to
get extended attributes on symlinks, as this will always fail.
Also, use lgetxattr (not getxattr) and acl_extended_file_nofollow
(not acl_extended_file) when not following symlinks, to avoid some
races.
---
ChangeLog | 10 ++++
lib/file-has-acl.c | 143 ++++++++++++++++++++++++++-------------------
2 files changed, 92 insertions(+), 61 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 0679ed28ef..f971059a0a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2024-09-30 Paul Eggert <egg...@cs.ucla.edu>
+
+ file-has-acl: improve performance on Linux symlinks
+ * lib/file-has-acl.c (USE_LINUX_XATTR): Now can be used outside #if.
+ (file_has_aclinfo): As an optimization on Linux, do not attempt to
+ get extended attributes on symlinks, as this will always fail.
+ Also, use lgetxattr (not getxattr) and acl_extended_file_nofollow
+ (not acl_extended_file) when not following symlinks, to avoid some
+ races.
+
2024-09-30 Bruno Haible <br...@clisp.org>
selinux-h: Fix two syntax errors.
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index 6924a616bd..944f0d2200 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -33,7 +33,11 @@
static char const UNKNOWN_SECURITY_CONTEXT[] = "?";
-#define USE_LINUX_XATTR (USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR)
+#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
+# define USE_LINUX_XATTR true
+#else
+# define USE_LINUX_XATTR false
+#endif
#if USE_LINUX_XATTR
# if USE_SELINUX_SELINUX_H
@@ -320,80 +324,95 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
SB must be set to the stat buffer of NAME,
obtained through stat() or lstat().
Set *AI to the ACL info if available.
- If FLAGS & AT_SYMLINK_FOLLOW, SB was gotten via stat and follow symlinks;
- otherwise, SB was gotten vi lstat and do not follow them. */
+ If FLAGS & AT_SYMLINK_FOLLOW, SB was gotten via stat, otherwise lstat.
+ Also, if FLAGS & AT_SYMLINK_FOLLOW, follow symlinks when retrieving
+ ACL info, otherwise do not follow them if possible. */
int
file_has_aclinfo (char const *name, struct stat const *sb,
struct aclinfo *ai, int flags)
{
-#if USE_LINUX_XATTR
- int initial_errno = errno;
- get_aclinfo (name, ai, flags);
+ /* Symbolic links lack extended attributes and ACLs on all supported
+ platforms, so don't bother trying to fetch them. If the platform
+ supports not following symlinks this defends against some races
+ and avoids a syscall; otherwise this is essential.
+
+ First, initialize *AI for cases known to be unsupported. */
- if (ai->size <= 0)
+ if (!USE_LINUX_XATTR || S_ISLNK (sb->st_mode))
{
- errno = ai->size < 0 ? ai->u.err : initial_errno;
- return ai->size;
+ ai->buf = ai->u.__gl_acl_ch;
+ ai->size = -1;
+ ai->u.err = ENOTSUP;
+ ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT;
+ ai->scontext_err = ENOTSUP;
}
- else
+
+ /* Now set *AI for cases that might be supported, then check for ACLs. */
+
+#if USE_ACL
+ if (! S_ISLNK (sb->st_mode))
{
- /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs,
- but if it has an NFSv4 ACL that's the one that matters.
- In earlier Fedora the two types of ACLs were mutually exclusive.
- Attempt to work correctly on both kinds of systems. */
- bool nfsv4_acl = aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL);
- int ret
- = (ai->size <= 0 ? ai->size
- : (nfsv4_acl
- || aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS)
- || (S_ISDIR (sb->st_mode)
- && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT))));
-
- /* If there is an NFSv4 ACL, check whether it is nontrivial. */
- if (nfsv4_acl)
+
+# if USE_LINUX_XATTR
+ int initial_errno = errno;
+ get_aclinfo (name, ai, flags);
+
+ if (ai->size <= 0)
{
- /* A buffer large enough to hold any trivial NFSv4 ACL.
- The max length of a trivial NFSv4 ACL is 6 words for owner,
- 6 for group, 7 for everyone, all times 2 because there are both
- allow and deny ACEs. There are 6 words for owner because of
- type, flag, mask, wholen, "OWNER@"+pad and similarly for group;
- everyone is another word to hold "EVERYONE@". */
- uint32_t buf[2 * (6 + 6 + 7)];
-
- ret = getxattr (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf);
- if (ret < 0)
- switch (errno)
- {
- case ENODATA: return 0;
- case ERANGE : return 1; /* ACL must be nontrivial. */
- }
- else
+ errno = ai->size < 0 ? ai->u.err : initial_errno;
+ return ai->size;
+ }
+ else
+ {
+ /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs,
+ but if it has an NFSv4 ACL that's the one that matters.
+ In earlier Fedora the two types of ACLs were mutually exclusive.
+ Attempt to work correctly on both kinds of systems. */
+ bool nfsv4_acl = aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL);
+ int ret
+ = (ai->size <= 0 ? ai->size
+ : (nfsv4_acl
+ || aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS)
+ || (S_ISDIR (sb->st_mode)
+ && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT))));
+
+ /* If there is an NFSv4 ACL, check whether it is nontrivial. */
+ if (nfsv4_acl)
{
- /* It looks like a trivial ACL, but investigate further. */
- ret = acl_nfs4_nontrivial (buf, ret);
+ /* A buffer large enough to hold any trivial NFSv4 ACL.
+ The max length of a trivial NFSv4 ACL is 6 words for owner,
+ 6 for group, 7 for everyone, all times 2 because there are both
+ allow and deny ACEs. There are 6 words for owner because of
+ type, flag, mask, wholen, "OWNER@"+pad and similarly for group;
+ everyone is another word to hold "EVERYONE@". */
+ uint32_t buf[2 * (6 + 6 + 7)];
+
+ ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
+ (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf));
if (ret < 0)
+ switch (errno)
+ {
+ case ENODATA: return 0;
+ case ERANGE : return 1; /* ACL must be nontrivial. */
+ }
+ else
{
- errno = EINVAL;
- return ret;
+ /* It looks like a trivial ACL, but investigate further. */
+ ret = acl_nfs4_nontrivial (buf, ret);
+ if (ret < 0)
+ {
+ errno = EINVAL;
+ return ret;
+ }
+ errno = initial_errno;
}
- errno = initial_errno;
}
+ if (ret < 0)
+ return - acl_errno_valid (errno);
+ return ret;
}
- if (ret < 0)
- return - acl_errno_valid (errno);
- return ret;
- }
-#else
- ai->size = -1;
- ai->u.err = ENOTSUP;
- ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT;
- ai->scontext_err = ENOTSUP;
-#endif
-#if USE_ACL
- if (! S_ISLNK (sb->st_mode))
- {
-# if HAVE_ACL_GET_FILE
+# elif HAVE_ACL_GET_FILE
/* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
/* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
@@ -404,7 +423,10 @@ file_has_aclinfo (char const *name, struct stat const *sb,
/* 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 = acl_extended_file (name);
+ ret = ((flags & ACL_SYMLINK_FOLLOW
+ ? acl_extended_file
+ : acl_extended_file_nofollow)
+ (name));
}
else /* FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
{
@@ -815,7 +837,6 @@ file_has_aclinfo (char const *name, struct stat const *sb,
return acl_nontrivial (count, entries);
}
}
-
# endif
}
#endif
--
2.43.0