From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1791) id CA5323858410; Mon, 20 Nov 2023 16:20:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CA5323858410 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1700497217; bh=Cy9CzWXS+TKEHkI5YMqhlYfq7W/gK+xRHiD4LnW/pbs=; h=From:To:Subject:Date:From; b=Wj52ewzVRenJBmr8nxT3jB/5rWDINMqZpaxKsdD9ybsXq5LDsQ2eyvfJl9TULCRUq Yx1dDMtZhYD49N00K2O32GL88uMQstqyUiq/6iGtvspG7wz4E8SiEPJnhpqHKQRhkw 9kQW1ZV/ve+Ilzdyzar8wz/EIvJ5Q+62moc6WarA= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Adhemerval Zanella To: glibc-cvs@sourceware.org Subject: [glibc] linux: Use fchmodat2 on fchmod for flags different than 0 (BZ 26401) X-Act-Checkin: glibc X-Git-Author: Adhemerval Zanella X-Git-Refname: refs/heads/master X-Git-Oldrev: c52c2c32db15aba8bbe1a0b4d3235f97d9c1a525 X-Git-Newrev: 65341f7bbea824d2ff9d37db15d8be162df42bd3 Message-Id: <20231120162017.CA5323858410@sourceware.org> Date: Mon, 20 Nov 2023 16:20:17 +0000 (GMT) List-Id: https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=65341f7bbea824d2ff9d37db15d8be162df42bd3 commit 65341f7bbea824d2ff9d37db15d8be162df42bd3 Author: Adhemerval Zanella Date: Thu Sep 28 13:56:21 2023 -0300 linux: Use fchmodat2 on fchmod for flags different than 0 (BZ 26401) Linux 6.6 (09da082b07bbae1c) added support for fchmodat2, which has similar semantics as fchmodat with an extra flag argument. This allows fchmodat to implement AT_SYMLINK_NOFOLLOW and AT_EMPTY_PATH without the need for procfs. The syscall is registered on all architectures (with value of 452 except on alpha which is 562, commit 78252deb023cf087). The tst-lchmod.c requires a small fix where fchmodat checks two contradictory assertions ('(st.st_mode & 0777) == 2' and '(st.st_mode & 0777) == 3'). Checked on x86_64-linux-gnu on a 6.6 kernel. Reviewed-by: Florian Weimer Diff: --- io/tst-lchmod.c | 4 +- sysdeps/unix/sysv/linux/fchmodat.c | 120 +++++++++++++++++------------- sysdeps/unix/sysv/linux/kernel-features.h | 8 ++ 3 files changed, 77 insertions(+), 55 deletions(-) diff --git a/io/tst-lchmod.c b/io/tst-lchmod.c index 2bf4835b05..6496dc61e0 100644 --- a/io/tst-lchmod.c +++ b/io/tst-lchmod.c @@ -219,9 +219,9 @@ test_1 (bool do_relative_path, int (*chmod_func) (int fd, const char *, mode_t, /* The error code from the openat fallback leaks out. */ if (errno != ENFILE && errno != EMFILE) TEST_COMPARE (errno, EOPNOTSUPP); + xstat (path_file, &st); + TEST_COMPARE (st.st_mode & 0777, 3); } - xstat (path_file, &st); - TEST_COMPARE (st.st_mode & 0777, 3); /* Close the descriptors. */ for (int *pfd = fd_list_begin (&fd_list); pfd < fd_list_end (&fd_list); diff --git a/sysdeps/unix/sysv/linux/fchmodat.c b/sysdeps/unix/sysv/linux/fchmodat.c index 99527a3727..99d3df6440 100644 --- a/sysdeps/unix/sysv/linux/fchmodat.c +++ b/sysdeps/unix/sysv/linux/fchmodat.c @@ -26,66 +26,80 @@ #include #include -int -fchmodat (int fd, const char *file, mode_t mode, int flag) +#if !__ASSUME_FCHMODAT2 +static int +fchmodat_fallback (int fd, const char *file, mode_t mode, int flag) { - if (flag == 0) - return INLINE_SYSCALL (fchmodat, 3, fd, file, mode); - else if (flag != AT_SYMLINK_NOFOLLOW) + if (flag != AT_SYMLINK_NOFOLLOW) return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL); - else - { - /* The kernel system call does not have a mode argument. - However, we can create an O_PATH descriptor and change that - via /proc (which does not resolve symbolic links). */ - int pathfd = __openat_nocancel (fd, file, - O_PATH | O_NOFOLLOW | O_CLOEXEC); - if (pathfd < 0) - /* This may report errors such as ENFILE and EMFILE. The - caller can treat them as temporary if necessary. */ - return pathfd; + /* The kernel system call does not have a mode argument. + However, we can create an O_PATH descriptor and change that + via /proc (which does not resolve symbolic links). */ - /* Use fstatat because fstat does not work on O_PATH descriptors - before Linux 3.6. */ - struct __stat64_t64 st; - if (__fstatat64_time64 (pathfd, "", &st, AT_EMPTY_PATH) != 0) - { - __close_nocancel (pathfd); - return -1; - } + int pathfd = __openat_nocancel (fd, file, + O_PATH | O_NOFOLLOW | O_CLOEXEC); + if (pathfd < 0) + /* This may report errors such as ENFILE and EMFILE. The + caller can treat them as temporary if necessary. */ + return pathfd; - /* Some Linux versions with some file systems can actually - change symbolic link permissions via /proc, but this is not - intentional, and it gives inconsistent results (e.g., error - return despite mode change). The expected behavior is that - symbolic link modes cannot be changed at all, and this check - enforces that. */ - if (S_ISLNK (st.st_mode)) - { - __close_nocancel (pathfd); - __set_errno (EOPNOTSUPP); - return -1; - } + /* Use fstatat because fstat does not work on O_PATH descriptors + before Linux 3.6. */ + struct __stat64_t64 st; + if (__fstatat64_time64 (pathfd, "", &st, AT_EMPTY_PATH) != 0) + { + __close_nocancel (pathfd); + return -1; + } - /* For most file systems, fchmod does not operate on O_PATH - descriptors, so go through /proc. */ - struct fd_to_filename filename; - int ret = __chmod (__fd_to_filename (pathfd, &filename), mode); - if (ret != 0) - { - if (errno == ENOENT) - /* /proc has not been mounted. Without /proc, there is no - way to upgrade the O_PATH descriptor to a full - descriptor. It is also not possible to re-open the - file without O_PATH because the file name may refer to - another file, and opening that without O_PATH may have - side effects (such as blocking, device rewinding, or - releasing POSIX locks). */ - __set_errno (EOPNOTSUPP); - } + /* Some Linux versions with some file systems can actually + change symbolic link permissions via /proc, but this is not + intentional, and it gives inconsistent results (e.g., error + return despite mode change). The expected behavior is that + symbolic link modes cannot be changed at all, and this check + enforces that. */ + if (S_ISLNK (st.st_mode)) + { __close_nocancel (pathfd); - return ret; + __set_errno (EOPNOTSUPP); + return -1; + } + + /* For most file systems, fchmod does not operate on O_PATH + descriptors, so go through /proc. */ + struct fd_to_filename filename; + int ret = __chmod (__fd_to_filename (pathfd, &filename), mode); + if (ret != 0) + { + if (errno == ENOENT) + /* /proc has not been mounted. Without /proc, there is no + way to upgrade the O_PATH descriptor to a full + descriptor. It is also not possible to re-open the + file without O_PATH because the file name may refer to + another file, and opening that without O_PATH may have + side effects (such as blocking, device rewinding, or + releasing POSIX locks). */ + __set_errno (EOPNOTSUPP); } + __close_nocancel (pathfd); + return ret; +} +#endif + +int +fchmodat (int fd, const char *file, mode_t mode, int flag) +{ +#if __ASSUME_FCHMODAT2 + return INLINE_SYSCALL_CALL (fchmodat2, fd, file, mode, flag); +#else + if (flag == 0) + return INLINE_SYSCALL_CALL (fchmodat, fd, file, mode); + + int r = INLINE_SYSCALL_CALL (fchmodat2, fd, file, mode, flag); + if (r != 0 && errno == ENOSYS) + return fchmodat_fallback (fd, file, mode, flag); + return r; +#endif } libc_hidden_def (fchmodat) diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h index 07b440f4ee..670d2604d2 100644 --- a/sysdeps/unix/sysv/linux/kernel-features.h +++ b/sysdeps/unix/sysv/linux/kernel-features.h @@ -252,4 +252,12 @@ # define __ASSUME_CLONE3 0 #endif +/* The fchmodat2 system call was introduced across all architectures + in Linux 6.6. */ +#if __LINUX_KERNEL_VERSION >= 0x060600 +# define __ASSUME_FCHMODAT2 1 +#else +# define __ASSUME_FCHMODAT2 0 +#endif + #endif /* kernel-features.h */