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 <fweimer@redhat.com>
This commit is contained in:
Adhemerval Zanella 2023-09-28 13:56:21 -03:00
parent c52c2c32db
commit 65341f7bbe
3 changed files with 80 additions and 58 deletions

View file

@ -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);
}
/* Close the descriptors. */
for (int *pfd = fd_list_begin (&fd_list); pfd < fd_list_end (&fd_list);

View file

@ -26,15 +26,13 @@
#include <sysdep.h>
#include <unistd.h>
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). */
@ -87,5 +85,21 @@ fchmodat (int fd, const char *file, mode_t mode, int flag)
__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)

View file

@ -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 */