From 08c28bd6013b2b0c630daea6eb93369c04569f40 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 9 Sep 2023 09:53:02 -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 e2c5622011..5929236789 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2023-09-09 Paul Eggert + + 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-09 Bruno Haible chown, lchown: Revisit platforms. diff --git a/lib/chown.c b/lib/chown.c index bf15e920b9..65fa3bb082 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 /* OpenBSD 7.2 */ @@ -80,42 +83,30 @@ rpl_chown (const char *file, uid_t uid, gid_t gid) # endif # if CHOWN_MODIFIES_SYMLINK /* some very old platforms */ - { - /* 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 /* macOS 12.5, FreeBSD 7.2, AIX 7.3.1, Solaris 9 */ -- 2.39.5