diff options
Diffstat (limited to '')
-rw-r--r-- | gl/lib/chown.c | 75 |
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)) |