On 2023-09-07 23:36, Jordi Sanfeliu wrote:
some days later, I decided to include support for lchown in the Fiwix kernel ...<https://github.com/mikaku/Fiwix/commit/4a82a2de818436a42ab22e34caa6a68b66c93b0a>

You added lchown but removed chown? Not sure that's an improvement.

Anyway, just for fun I wrote the attached patch. Does it fix the original problem for you? I have not installed it into Gnulib because I lack easy access to oddball operating systems that have that particular chown bug.
From fa893bf7902889a4375392a0f5a1f0e64e6592dc Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Fri, 8 Sep 2023 18:59:04 -0700
Subject: [PATCH] chown: work around symlink issues on odd platforms

Problem reported by Jordi Sanfeliu in:
https://lists.gnu.org/archive/html/bug-gnulib/2023-07/msg00116.html
* lib/chown.c (rpl_chown) [CHOWN_MODIFIES_SYMLINK]:
Do not declare unused locals st, stat_valid.
Redo to just call chown if arg is a symlink.
This induces a race but is perhaps the best we can do easily
on oddball platforms where chown does not follow symlinks.
---
 ChangeLog   | 11 ++++++++++
 lib/chown.c | 63 +++++++++++++++++++++++------------------------------
 2 files changed, 38 insertions(+), 36 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2aec64130e..5963bed7d5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2023-09-08  Paul Eggert  <[email protected]>
+
+	chown: work around symlink issues on odd platforms
+	Problem reported by Jordi Sanfeliu in:
+	https://lists.gnu.org/archive/html/bug-gnulib/2023-07/msg00116.html
+	* lib/chown.c (rpl_chown) [CHOWN_MODIFIES_SYMLINK]:
+	Do not declare unused locals st, stat_valid.
+	Redo to just call chown if arg is a symlink.
+	This induces a race but is perhaps the best we can do easily
+	on oddball platforms where chown does not follow symlinks.
+
 2023-09-07  Paul Eggert  <[email protected]>
 
 	mbscasecmp: support GNULIB_MCEL_PREFER
diff --git a/lib/chown.c b/lib/chown.c
index d735818afd..a89a460536 100644
--- a/lib/chown.c
+++ b/lib/chown.c
@@ -53,8 +53,11 @@ chown (_GL_UNUSED const char *file, _GL_UNUSED uid_t uid,
 int
 rpl_chown (const char *file, uid_t uid, gid_t gid)
 {
+# if (CHOWN_CHANGE_TIME_BUG || CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE \
+      || CHOWN_TRAILING_SLASH_BUG)
   struct stat st;
   bool stat_valid = false;
+# endif
   int result;
 
 # if CHOWN_CHANGE_TIME_BUG
@@ -80,42 +83,30 @@ rpl_chown (const char *file, uid_t uid, gid_t gid)
 # endif
 
 # if CHOWN_MODIFIES_SYMLINK
-  {
-    /* Handle the case in which the system-supplied chown function
-       does *not* follow symlinks.  Instead, it changes permissions
-       on the symlink itself.  To work around that, we open the
-       file (but this can fail due to lack of read or write permission) and
-       use fchown on the resulting descriptor.  */
-    int open_flags = O_NONBLOCK | O_NOCTTY | O_CLOEXEC;
-    int fd = open (file, O_RDONLY | open_flags);
-    if (0 <= fd
-        || (errno == EACCES
-            && 0 <= (fd = open (file, O_WRONLY | open_flags))))
-      {
-        int saved_errno;
-        bool fchown_socket_failure;
-
-        result = fchown (fd, uid, gid);
-        saved_errno = errno;
-
-        /* POSIX says fchown can fail with errno == EINVAL on sockets
-           and pipes, so fall back on chown in that case.  */
-        fchown_socket_failure =
-          (result != 0 && saved_errno == EINVAL
-           && fstat (fd, &st) == 0
-           && (S_ISFIFO (st.st_mode) || S_ISSOCK (st.st_mode)));
-
-        close (fd);
-
-        if (! fchown_socket_failure)
-          {
-            errno = saved_errno;
-            return result;
-          }
-      }
-    else if (errno != EACCES)
-      return -1;
-  }
+  /* The system-supplied chown function does not follow symlinks.
+     If the file is a symlink, open the file (following symlinks), and
+     fchown the resulting descriptor.  Although the open might fail
+     due to lack of permissions, it's the best we can easily do.  */
+  char linkbuf[1];
+  if (0 <= readlink (file, linkbuf, sizeof linkbuf))
+    {
+      int open_flags = O_NONBLOCK | O_NOCTTY | O_CLOEXEC;
+      int fd = open (file, O_RDONLY | open_flags);
+      if (fd < 0
+          && (errno != EACCES
+              || ((fd = open (file, O_WRONLY | open_flags)) < 0))
+          && (errno != EACCES || O_EXEC == O_RDONLY
+              || ((fd = open (file, O_EXEC | open_flags)) < 0))
+          && (errno != EACCES || O_SEARCH == O_RDONLY || O_SEARCH == O_EXEC
+              || ((fd = open (file, O_SEARCH | open_flags)) < 0)))
+        return fd;
+
+      int r = fchown (fd, uid, gid);
+      int err = errno;
+      close (fd);
+      errno = err;
+      return r;
+    }
 # endif
 
 # if CHOWN_TRAILING_SLASH_BUG
-- 
2.39.2

Reply via email to