summaryrefslogtreecommitdiffstats
path: root/gl/lib/chown.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/lib/chown.c')
-rw-r--r--gl/lib/chown.c75
1 files changed, 33 insertions, 42 deletions
diff --git a/gl/lib/chown.c b/gl/lib/chown.c
index d735818..398f8c1 100644
--- a/gl/lib/chown.c
+++ b/gl/lib/chown.c
@@ -1,7 +1,7 @@
/* provide consistent interface to chown for systems that don't interpret
an ID of -1 as meaning "don't change the corresponding ID".
- Copyright (C) 1997, 2004-2007, 2009-2023 Free Software Foundation, Inc.
+ Copyright (C) 1997, 2004-2007, 2009-2024 Free Software Foundation, Inc.
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -53,11 +53,14 @@ 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
+# if CHOWN_CHANGE_TIME_BUG /* OpenBSD 7.2 */
if (gid != (gid_t) -1 || uid != (uid_t) -1)
{
if (stat (file, &st))
@@ -66,7 +69,7 @@ rpl_chown (const char *file, uid_t uid, gid_t gid)
}
# endif
-# if CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE
+# if CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE /* some very old platforms */
if (gid == (gid_t) -1 || uid == (uid_t) -1)
{
/* Stat file to get id(s) that should remain unchanged. */
@@ -79,46 +82,34 @@ 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;
- }
+# if CHOWN_MODIFIES_SYMLINK /* some very old platforms */
+ /* 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
+# if CHOWN_TRAILING_SLASH_BUG /* macOS 12.5, FreeBSD 7.2, AIX 7.3.1, Solaris 9 */
if (!stat_valid)
{
size_t len = strlen (file);
@@ -129,7 +120,7 @@ rpl_chown (const char *file, uid_t uid, gid_t gid)
result = chown (file, uid, gid);
-# if CHOWN_CHANGE_TIME_BUG
+# if CHOWN_CHANGE_TIME_BUG /* OpenBSD 7.2 */
if (result == 0 && stat_valid
&& (uid == st.st_uid || uid == (uid_t) -1)
&& (gid == st.st_gid || gid == (gid_t) -1))