This has the same motivation as the recent fchmodat change.
* lib/fchownat.c (AT_EMPTY_PATH): Default to 0.
(FCHOWNAT_EMPTY_FILENAME_BUG): Remove; no longer needed.
(rpl_fchownat): Do not mishandle AT_EMPTY_PATH if specified
and if the underlying library+kernel supports it.
* m4/fchownat.m4 (gl_FUNC_FCHOWNAT):
Don’t define FCHOWNAT_EMPTY_FILENAME_BUG, as it is no longer needed.
---
 ChangeLog      |  9 +++++++++
 lib/fchownat.c | 26 +++++++++++++++-----------
 m4/fchownat.m4 |  8 ++------
 3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d78d0d1102..5bf7f08c52 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2026-06-16  Paul Eggert  <[email protected]>
 
+       fchownat: don’t mishandle AT_EMPTY_PATH
+       This has the same motivation as the recent fchmodat change.
+       * lib/fchownat.c (AT_EMPTY_PATH): Default to 0.
+       (FCHOWNAT_EMPTY_FILENAME_BUG): Remove; no longer needed.
+       (rpl_fchownat): Do not mishandle AT_EMPTY_PATH if specified
+       and if the underlying library+kernel supports it.
+       * m4/fchownat.m4 (gl_FUNC_FCHOWNAT):
+       Don’t define FCHOWNAT_EMPTY_FILENAME_BUG, as it is no longer needed.
+
        fchmodat: don’t mishandle AT_EMPTY_PATH
        This does not add support for AT_EMPTY_PATH on platforms lacking it.
        It merely fixes bugs in handling AT_EMPTY_PATH on platforms
diff --git a/lib/fchownat.c b/lib/fchownat.c
index 5c9901fbbd..1201e1b100 100644
--- a/lib/fchownat.c
+++ b/lib/fchownat.c
@@ -36,15 +36,16 @@
 #include "openat.h"
 #include "stat-time.h"
 
+#ifndef AT_EMPTY_PATH
+# define AT_EMPTY_PATH 0
+#endif
+
 #ifndef CHOWN_CHANGE_TIME_BUG
 # define CHOWN_CHANGE_TIME_BUG 0
 #endif
 #ifndef CHOWN_TRAILING_SLASH_BUG
 # define CHOWN_TRAILING_SLASH_BUG 0
 #endif
-#ifndef FCHOWNAT_EMPTY_FILENAME_BUG
-# define FCHOWNAT_EMPTY_FILENAME_BUG 0
-#endif
 
 /* Gnulib target platforms lacking utimensat do not need it,
    because in practice the bug it works around does not occur.  */
@@ -107,25 +108,28 @@ local_lchownat (int fd, char const *file, uid_t owner, 
gid_t group);
    chown and lchown.  */
 
 int
-rpl_fchownat (int fd, char const *file, uid_t owner, gid_t group, int flag)
+rpl_fchownat (int fd, char const *file, uid_t owner, gid_t group, int flags)
 {
   /* No need to worry about CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE
      or CHOWN_MODIFIES_SYMLINK, as no known fchownat implementations
      have these bugs.  */
 
-  if (FCHOWNAT_EMPTY_FILENAME_BUG && file[0] == '\0')
+  if (file && *file)
+    flags &= ~AT_EMPTY_PATH;
+  else if (! (flags & AT_EMPTY_PATH))
     {
       errno = ENOENT;
       return -1;
     }
 
   bool trailing_slash_check = (CHOWN_TRAILING_SLASH_BUG
-                               && file[0] && file[strlen (file) - 1] == '/');
+                               && file && *file
+                               && file[strlen (file) - 1] == '/');
   if (trailing_slash_check)
-    flag &= ~AT_SYMLINK_NOFOLLOW;
+    flags &= ~AT_SYMLINK_NOFOLLOW;
 
 # if FCHOWNAT_NOFOLLOW_BUG
-  if (flag == AT_SYMLINK_NOFOLLOW)
+  if (flags == AT_SYMLINK_NOFOLLOW)
     return local_lchownat (fd, file, owner, group);
 # endif
 
@@ -138,7 +142,7 @@ rpl_fchownat (int fd, char const *file, uid_t owner, gid_t 
group, int flag)
 
   if (change_time_check | trailing_slash_check)
     {
-      int r = fstatat (fd, file, &st, flag);
+      int r = fstatat (fd, file, &st, flags);
 
       /* EOVERFLOW means the file exists, which is all that the
          trailing slash check needs.  */
@@ -146,7 +150,7 @@ rpl_fchownat (int fd, char const *file, uid_t owner, gid_t 
group, int flag)
         return r;
     }
 
-  int result = fchownat (fd, file, owner, group, flag);
+  int result = fchownat (fd, file, owner, group, flags);
 
   /* If no change in ownership, but at least one argument was not -1,
      update ctime indirectly via a no-change update to atime and mtime.
@@ -160,7 +164,7 @@ rpl_fchownat (int fd, char const *file, uid_t owner, gid_t 
group, int flag)
       struct timespec times[2];
       times[0] = get_stat_atime (&st);
       times[1] = get_stat_mtime (&st);
-      utimensat (fd, file, times, flag);
+      utimensat (fd, file, times, flags);
     }
 
   return result;
diff --git a/m4/fchownat.m4 b/m4/fchownat.m4
index f1ee41c70a..f0fb726035 100644
--- a/m4/fchownat.m4
+++ b/m4/fchownat.m4
@@ -1,5 +1,5 @@
 # fchownat.m4
-# serial 8
+# serial 9
 dnl Copyright (C) 2004-2026 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -27,11 +27,7 @@ AC_DEFUN([gl_FUNC_FCHOWNAT]
                    perform lchown tasks.])
        ])
      gl_FUNC_FCHOWNAT_EMPTY_FILENAME_BUG(
-       [REPLACE_FCHOWNAT=1
-        AC_DEFINE([FCHOWNAT_EMPTY_FILENAME_BUG], [1],
-                  [Define to 1 if your platform has fchownat, but it does
-                   not reject an empty file name.])
-       ])
+       [REPLACE_FCHOWNAT=1])
      if test $REPLACE_CHOWN = 1; then
        REPLACE_FCHOWNAT=1
      fi],
-- 
2.54.0


Reply via email to