bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

file-has-acl: Fix performance regression on FreeBSD, Cygwin


From: Bruno Haible
Subject: file-has-acl: Fix performance regression on FreeBSD, Cygwin
Date: Mon, 07 Oct 2024 14:31:40 +0200

The recent changes in module file-has-acl can lead to extra system calls
executed on FreeBSD and Cygwin for the file_has_acl() function.

Namely, the caller of this function calls lstat(), and if the directory
entry is of some unusual type, the IFTODT macro returns DT_UNKNOWN, which
loses the information that the directory entry does not name a directory.
Then, the file_has_aclinfo() function calls
   acl_get_file (name, ACL_TYPE_DEFAULT)
although this makes only sense for directories.

This patch restores the previous behaviour that once we have called lstat()
we don't need to determine the ACL_TYPE_DEFAULT ACL unless it's a directory.


2024-10-07  Bruno Haible  <bruno@clisp.org>

        file-has-acl: Fix performance regression on FreeBSD, Cygwin.
        * lib/dirent.in.h (_GL_DT_NOTDIR): New macro.
        * lib/acl.h (ACL_SYMLINK_FOLLOW): Increase value.
        * lib/file-has-acl.c (file_has_aclinfo): Don't call
        acl_get_file (name, ACL_TYPE_DEFAULT) if we know that name does not
        denote a directory.
        (file_has_acl): Extract from *SB the information that NAME is not a
        directory.

diff --git a/lib/acl.h b/lib/acl.h
index 29ec2496fa..68a421a093 100644
--- a/lib/acl.h
+++ b/lib/acl.h
@@ -32,9 +32,9 @@
 extern "C" {
 #endif
 
-/* Follow symlinks when getting an ACL.  This is greater than any
-   unsigned char so that it can be ORed with any <dirent.h> DT_* value.  */
-enum { ACL_SYMLINK_FOLLOW = 1 + (unsigned char) -1 };
+/* Follow symlinks when getting an ACL.  This is a bitmask that is guaranteed
+   not to collide with any <dirent.h> DT_* or _GL_DT_* value.  */
+enum { ACL_SYMLINK_FOLLOW = 0x10000 };
 
 /* Information about an ACL.  */
 struct aclinfo
diff --git a/lib/dirent.in.h b/lib/dirent.in.h
index c8c39c3e6b..a0ac39b496 100644
--- a/lib/dirent.in.h
+++ b/lib/dirent.in.h
@@ -54,6 +54,7 @@ struct dirent
    but not (yet) DT_MQ, DT_SEM, DT_SHM, DT_TMO.
    These macros can be useful even on platforms that do not support
    d_type or the corresponding file types.
+   The values of these macros are all in the 'unsigned char' range.
    Default to the Linux values which are also popular elsewhere,
    and check that all macros have distinct values.  */
 #ifndef DT_UNKNOWN
@@ -97,6 +98,9 @@ static_assert (DT_UNKNOWN != DT_FIFO && DT_UNKNOWN != DT_CHR
                && DT_LNK != DT_SOCK && DT_LNK != DT_WHT
                && DT_SOCK != DT_WHT);
 
+/* Other optional information about a directory entry.  */
+#define _GL_DT_NOTDIR 0x100   /* Not a directory */
+
 /* Conversion between S_IF* and DT_* file types.  */
 #if ! (defined IFTODT && defined DTTOIF)
 # include <sys/stat.h>
@@ -111,6 +115,7 @@ static_assert (DT_UNKNOWN != DT_FIFO && DT_UNKNOWN != DT_CHR
 #  define _GL_DIRENT_S_IFWHT (DT_WHT << 12) /* just a guess */
 # endif
 #endif
+/* Conversion from a 'stat' mode to a DT_* value.  */
 #ifndef IFTODT
 # define IFTODT(mode) \
    (S_ISREG (mode) ? DT_REG : S_ISDIR (mode) ? DT_DIR \
@@ -119,6 +124,7 @@ static_assert (DT_UNKNOWN != DT_FIFO && DT_UNKNOWN != DT_CHR
     : S_ISSOCK (mode) ? DT_SOCK \
     : _GL_DIRENT_S_ISWHT (mode) ? DT_WHT : DT_UNKNOWN)
 #endif
+/* Conversion from a DT_* value to a 'stat' mode.  */
 #ifndef DTTOIF
 # define DTTOIF(dirtype) \
    ((dirtype) == DT_REG ? S_IFREG : (dirtype) == DT_DIR ? S_IFDIR \
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index 1fc54a7687..0dfd25b52a 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -328,8 +328,10 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
    if ACLs are not supported as errno is set in that case also.
    Set *AI to ACL info regardless of return value.
    FLAGS should be a <dirent.h> d_type value, optionally ORed with
-   ACL_SYMLINK_FOLLOW; if the d_type value is not known,
-   use DT_UNKNOWN though this may be less efficient.
+     - _GL_DT_NOTDIR if it is known that NAME is not a directory,
+     - ACL_SYMLINK_FOLLOW to follow the link if NAME is a symbolic link.
+   If the d_type value is not known, use DT_UNKNOWN though this may be less
+   efficient.
    If FLAGS & ACL_SYMLINK_FOLLOW, follow symlinks when retrieving ACL info;
    otherwise do not follow them if possible.  */
 int
@@ -463,7 +465,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
              either both succeed or both fail; it depends on the
              file system.  Therefore there is no point in making the second
              call if the first one already failed.  */
-          if (ret == 0 && (d_type == DT_DIR || d_type == DT_UNKNOWN))
+          if (ret == 0
+              && (d_type == DT_DIR
+                  || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR))))
             {
               acl = acl_get_file (name, ACL_TYPE_DEFAULT);
               if (acl)
@@ -851,8 +855,11 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
 int
 file_has_acl (char const *name, struct stat const *sb)
 {
+  int flags = IFTODT (sb->st_mode);
+  if (!S_ISDIR (sb->st_mode))
+    flags |= _GL_DT_NOTDIR;
   struct aclinfo ai;
-  int r = file_has_aclinfo (name, &ai, IFTODT (sb->st_mode));
+  int r = file_has_aclinfo (name, &ai, flags);
   aclinfo_free (&ai);
   return r;
 }






reply via email to

[Prev in Thread] Current Thread [Next in Thread]