Hi,

Lukáš Zaoral wrote:
> file_has_aclinfo always calls lgetxattr unless ACL_SYMLINK_FOLLOW is
> set.

... and the documentation of file_has_acl states that SB was
"obtained through stat() or lstat()".

But file_has_acl does not receive the information whether to call getxattr
or lgetxattr. That is the main problem, and your patch does not fix it:
it makes a guess. And that guess is correct in one case but wrong in
the other case.

Instead, the correct fix would be to add to file_has_acl a flags
argument, that would be either 0 (in the case lstat() was used)
or ACL_SYMLINK_FOLLOW (in the case stat() or fstat() was used).

But this is a backwards-incompatible change, and it would cause
compilation errors in GNU emacs and GNU tar. Therefore I don't
want to do it. Instead, I'm merely applying the attached documentation
change that adds a restriction to file_has_acl. I have verified that
  - The only packages that use or used file_has_acl are
    GNU coreutils, GNU emacs, GNU tar.
  - GNU coreutils does not use file_has_acl any more, it now uses
    file_has_aclinfo.
  - GNU emacs and GNU tar satisfy the restriction.

So, the only package that does not satisfy the restriction is the
GNU coreutils from two years ago, which is exactly your case.

For this case I would recommend that you apply a patch like this,
locally for the coreutils package only.

diff --git a/lib/acl.h b/lib/acl.h
index 4ac25fe226..74ccb09306 100644
--- a/lib/acl.h
+++ b/lib/acl.h
@@ -77,7 +77,7 @@ struct aclinfo
 };
 
 bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST;
-int file_has_acl (char const *, struct stat const *);
+int file_has_acl (char const *, struct stat const *, int);
 int file_has_aclinfo (char const *restrict, struct aclinfo *restrict, int);
 int fdfile_has_aclinfo (int, char const *restrict,
                         struct aclinfo *restrict, int);
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index 4a70d4cf0f..f9d8efed3d 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -973,14 +973,12 @@ fdfile_has_aclinfo (MAYBE_UNUSED int fd,
    if ACLs are not supported as errno is set in that case also.
    SB must be set to the stat buffer of NAME,
    obtained through stat(), fstat(), or lstat().
-   In the case that SB was obtained through lstat(), the caller must guarantee
-   that !S_ISLNK (SB->st_mode).  (In order to support S_ISLNK (SB->st_mode),
-   this function would need a flags parameter, where the caller would need
-   to pass either 0 or ACL_SYMLINK_FOLLOW.)  */
+   FLAGS must be ACL_SYMLINK_FOLLOW in the stat(), fstat() cases, or
+   0 in the lstat() case.  */
 int
-file_has_acl (char const *name, struct stat const *sb)
+file_has_acl (char const *name, struct stat const *sb, int flags)
 {
-  int flags = IFTODT (sb->st_mode);
+  flags |= IFTODT (sb->st_mode);
   if (!S_ISDIR (sb->st_mode))
     flags |= _GL_DT_NOTDIR;
   struct aclinfo ai;


Bruno

From ef0fd39eff54923d565c22ed260854c8a9136456 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Tue, 3 Nov 2026 14:25:12 +0100
Subject: [PATCH] file-has-acl: Document restriction.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Reported by Lukáš Zaoral <[email protected]> in
<https://lists.gnu.org/archive/html/bug-gnulib/2026-11/msg00026.html>.

* lib/file-has-acl.c (file_has_acl): Document that this function does
not support stat buffers that represent symbolic links.
---
 ChangeLog          | 8 ++++++++
 lib/file-has-acl.c | 6 +++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 2115938896..3a6e8ee7b3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2026-11-03  Bruno Haible  <[email protected]>
+
+	file-has-acl: Document restriction.
+	Reported by Lukáš Zaoral <[email protected]> in
+	<https://lists.gnu.org/archive/html/bug-gnulib/2026-11/msg00026.html>.
+	* lib/file-has-acl.c (file_has_acl): Document that this function does
+	not support stat buffers that represent symbolic links.
+
 2026-11-02  Bruno Haible  <[email protected]>
 
 	findprog-in: Use a default PATH like POSIX specifies.
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index 8b9bb0468f..4a70d4cf0f 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -972,7 +972,11 @@ fdfile_has_aclinfo (MAYBE_UNUSED int fd,
    and -1 (setting errno) on error.  Note callers can determine
    if ACLs are not supported as errno is set in that case also.
    SB must be set to the stat buffer of NAME,
-   obtained through stat() or lstat().  */
+   obtained through stat(), fstat(), or lstat().
+   In the case that SB was obtained through lstat(), the caller must guarantee
+   that !S_ISLNK (SB->st_mode).  (In order to support S_ISLNK (SB->st_mode),
+   this function would need a flags parameter, where the caller would need
+   to pass either 0 or ACL_SYMLINK_FOLLOW.)  */
 int
 file_has_acl (char const *name, struct stat const *sb)
 {
-- 
2.53.0

Reply via email to