v6.6-vfs.fchmodat2
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCZOXT7QAKCRCRxhvAZXjc ort3AP0VIK/oJk5skgjpinQrCfvtVz0XOtawuBtn0f1weIfb6AD9Hg1rqOKnQD5z dkvn3xaEr3gPOVzqU5SvFwVoCM0cMwA= =24Ha -----END PGP SIGNATURE----- Merge tag 'v6.6-vfs.fchmodat2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull fchmodat2 system call from Christian Brauner: "This adds the fchmodat2() system call. It is a revised version of the fchmodat() system call, adding a missing flag argument. Support for both AT_SYMLINK_NOFOLLOW and AT_EMPTY_PATH are included. Adding this system call revision has been a longstanding request but so far has always fallen through the cracks. While the kernel implementation of fchmodat() does not have a flag argument the libc provided POSIX-compliant fchmodat(3) version does. Both glibc and musl have to implement a workaround in order to support AT_SYMLINK_NOFOLLOW (see [1] and [2]). The workaround is brittle because it relies not just on O_PATH and O_NOFOLLOW semantics and procfs magic links but also on our rather inconsistent symlink semantics. This gives userspace a proper fchmodat2() system call that libcs can use to properly implement fchmodat(3) and allows them to get rid of their hacks. In this case it will immediately benefit them as the current workaround is already defunct because of aformentioned inconsistencies. In addition to AT_SYMLINK_NOFOLLOW, give userspace the ability to use AT_EMPTY_PATH with fchmodat2(). This is already possible with fchownat() so there's no reason to not also support it for fchmodat2(). The implementation is simple and comes with selftests. Implementation of the system call and wiring up the system call are done as separate patches even though they could arguably be one patch. But in case there are merge conflicts from other system call additions it can be beneficial to have separate patches" Link: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/fchmodat.c;h=17eca54051ee28ba1ec3f9aed170a62630959143;hb=a492b1e5ef7ab50c6fdd4e4e9879ea5569ab0a6c#l35 [1] Link: https://git.musl-libc.org/cgit/musl/tree/src/stat/fchmodat.c?id=718f363bc2067b6487900eddc9180c84e7739f80#n28 [2] * tag 'v6.6-vfs.fchmodat2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: selftests: fchmodat2: remove duplicate unneeded defines fchmodat2: add support for AT_EMPTY_PATH selftests: Add fchmodat2 selftest arch: Register fchmodat2, usually as syscall 452 fs: Add fchmodat2() Non-functional cleanup of a "__user * filename"
This commit is contained in:
commit
475d4df827
@ -491,3 +491,4 @@
|
||||
559 common futex_waitv sys_futex_waitv
|
||||
560 common set_mempolicy_home_node sys_ni_syscall
|
||||
561 common cachestat sys_cachestat
|
||||
562 common fchmodat2 sys_fchmodat2
|
||||
|
@ -465,3 +465,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
@ -39,7 +39,7 @@
|
||||
#define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)
|
||||
#define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)
|
||||
|
||||
#define __NR_compat_syscalls 452
|
||||
#define __NR_compat_syscalls 453
|
||||
#endif
|
||||
|
||||
#define __ARCH_WANT_SYS_CLONE
|
||||
|
@ -909,6 +909,8 @@ __SYSCALL(__NR_futex_waitv, sys_futex_waitv)
|
||||
__SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node)
|
||||
#define __NR_cachestat 451
|
||||
__SYSCALL(__NR_cachestat, sys_cachestat)
|
||||
#define __NR_fchmodat2 452
|
||||
__SYSCALL(__NR_fchmodat2, sys_fchmodat2)
|
||||
|
||||
/*
|
||||
* Please add new compat syscalls above this comment and update
|
||||
|
@ -372,3 +372,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
@ -451,3 +451,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
@ -457,3 +457,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
@ -390,3 +390,4 @@
|
||||
449 n32 futex_waitv sys_futex_waitv
|
||||
450 n32 set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 n32 cachestat sys_cachestat
|
||||
452 n32 fchmodat2 sys_fchmodat2
|
||||
|
@ -366,3 +366,4 @@
|
||||
449 n64 futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 n64 cachestat sys_cachestat
|
||||
452 n64 fchmodat2 sys_fchmodat2
|
||||
|
@ -439,3 +439,4 @@
|
||||
449 o32 futex_waitv sys_futex_waitv
|
||||
450 o32 set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 o32 cachestat sys_cachestat
|
||||
452 o32 fchmodat2 sys_fchmodat2
|
||||
|
@ -450,3 +450,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
@ -538,3 +538,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 nospu set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
@ -454,3 +454,4 @@
|
||||
449 common futex_waitv sys_futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2 sys_fchmodat2
|
||||
|
@ -454,3 +454,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
@ -497,3 +497,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
@ -456,3 +456,4 @@
|
||||
449 i386 futex_waitv sys_futex_waitv
|
||||
450 i386 set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 i386 cachestat sys_cachestat
|
||||
452 i386 fchmodat2 sys_fchmodat2
|
||||
|
@ -373,6 +373,7 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
||||
#
|
||||
# Due to a historical design error, certain syscalls are numbered differently
|
||||
|
@ -422,3 +422,4 @@
|
||||
449 common futex_waitv sys_futex_waitv
|
||||
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 common cachestat sys_cachestat
|
||||
452 common fchmodat2 sys_fchmodat2
|
||||
|
23
fs/open.c
23
fs/open.c
@ -671,11 +671,20 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_fchmodat(int dfd, const char __user *filename, umode_t mode)
|
||||
static int do_fchmodat(int dfd, const char __user *filename, umode_t mode,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct path path;
|
||||
int error;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
unsigned int lookup_flags;
|
||||
|
||||
if (unlikely(flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)))
|
||||
return -EINVAL;
|
||||
|
||||
lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
|
||||
if (flags & AT_EMPTY_PATH)
|
||||
lookup_flags |= LOOKUP_EMPTY;
|
||||
|
||||
retry:
|
||||
error = user_path_at(dfd, filename, lookup_flags, &path);
|
||||
if (!error) {
|
||||
@ -689,15 +698,21 @@ retry:
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(fchmodat2, int, dfd, const char __user *, filename,
|
||||
umode_t, mode, unsigned int, flags)
|
||||
{
|
||||
return do_fchmodat(dfd, filename, mode, flags);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename,
|
||||
umode_t, mode)
|
||||
{
|
||||
return do_fchmodat(dfd, filename, mode);
|
||||
return do_fchmodat(dfd, filename, mode, 0);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode)
|
||||
{
|
||||
return do_fchmodat(AT_FDCWD, filename, mode);
|
||||
return do_fchmodat(AT_FDCWD, filename, mode, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -438,8 +438,10 @@ asmlinkage long sys_chdir(const char __user *filename);
|
||||
asmlinkage long sys_fchdir(unsigned int fd);
|
||||
asmlinkage long sys_chroot(const char __user *filename);
|
||||
asmlinkage long sys_fchmod(unsigned int fd, umode_t mode);
|
||||
asmlinkage long sys_fchmodat(int dfd, const char __user * filename,
|
||||
asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
|
||||
umode_t mode);
|
||||
asmlinkage long sys_fchmodat2(int dfd, const char __user *filename,
|
||||
umode_t mode, unsigned int flags);
|
||||
asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
|
||||
gid_t group, int flag);
|
||||
asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group);
|
||||
|
@ -820,8 +820,11 @@ __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node)
|
||||
#define __NR_cachestat 451
|
||||
__SYSCALL(__NR_cachestat, sys_cachestat)
|
||||
|
||||
#define __NR_fchmodat2 452
|
||||
__SYSCALL(__NR_fchmodat2, sys_fchmodat2)
|
||||
|
||||
#undef __NR_syscalls
|
||||
#define __NR_syscalls 452
|
||||
#define __NR_syscalls 453
|
||||
|
||||
/*
|
||||
* 32 bit systems traditionally used different
|
||||
|
@ -18,6 +18,7 @@ TARGETS += drivers/net/bonding
|
||||
TARGETS += drivers/net/team
|
||||
TARGETS += efivarfs
|
||||
TARGETS += exec
|
||||
TARGETS += fchmodat2
|
||||
TARGETS += filesystems
|
||||
TARGETS += filesystems/binderfs
|
||||
TARGETS += filesystems/epoll
|
||||
|
2
tools/testing/selftests/fchmodat2/.gitignore
vendored
Normal file
2
tools/testing/selftests/fchmodat2/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
/*_test
|
6
tools/testing/selftests/fchmodat2/Makefile
Normal file
6
tools/testing/selftests/fchmodat2/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
CFLAGS += -Wall -O2 -g -fsanitize=address -fsanitize=undefined $(KHDR_INCLUDES)
|
||||
TEST_GEN_PROGS := fchmodat2_test
|
||||
|
||||
include ../lib.mk
|
142
tools/testing/selftests/fchmodat2/fchmodat2_test.c
Normal file
142
tools/testing/selftests/fchmodat2/fchmodat2_test.c
Normal file
@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../kselftest.h"
|
||||
|
||||
int sys_fchmodat2(int dfd, const char *filename, mode_t mode, int flags)
|
||||
{
|
||||
int ret = syscall(__NR_fchmodat2, dfd, filename, mode, flags);
|
||||
|
||||
return ret >= 0 ? ret : -errno;
|
||||
}
|
||||
|
||||
int setup_testdir(void)
|
||||
{
|
||||
int dfd, ret;
|
||||
char dirname[] = "/tmp/ksft-fchmodat2.XXXXXX";
|
||||
|
||||
/* Make the top-level directory. */
|
||||
if (!mkdtemp(dirname))
|
||||
ksft_exit_fail_msg("%s: failed to create tmpdir\n", __func__);
|
||||
|
||||
dfd = open(dirname, O_PATH | O_DIRECTORY);
|
||||
if (dfd < 0)
|
||||
ksft_exit_fail_msg("%s: failed to open tmpdir\n", __func__);
|
||||
|
||||
ret = openat(dfd, "regfile", O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
||||
if (ret < 0)
|
||||
ksft_exit_fail_msg("%s: failed to create file in tmpdir\n",
|
||||
__func__);
|
||||
close(ret);
|
||||
|
||||
ret = symlinkat("regfile", dfd, "symlink");
|
||||
if (ret < 0)
|
||||
ksft_exit_fail_msg("%s: failed to create symlink in tmpdir\n",
|
||||
__func__);
|
||||
|
||||
return dfd;
|
||||
}
|
||||
|
||||
int expect_mode(int dfd, const char *filename, mode_t expect_mode)
|
||||
{
|
||||
struct stat st;
|
||||
int ret = fstatat(dfd, filename, &st, AT_SYMLINK_NOFOLLOW);
|
||||
|
||||
if (ret)
|
||||
ksft_exit_fail_msg("%s: %s: fstatat failed\n",
|
||||
__func__, filename);
|
||||
|
||||
return (st.st_mode == expect_mode);
|
||||
}
|
||||
|
||||
void test_regfile(void)
|
||||
{
|
||||
int dfd, ret;
|
||||
|
||||
dfd = setup_testdir();
|
||||
|
||||
ret = sys_fchmodat2(dfd, "regfile", 0640, 0);
|
||||
|
||||
if (ret < 0)
|
||||
ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__);
|
||||
|
||||
if (!expect_mode(dfd, "regfile", 0100640))
|
||||
ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n",
|
||||
__func__);
|
||||
|
||||
ret = sys_fchmodat2(dfd, "regfile", 0600, AT_SYMLINK_NOFOLLOW);
|
||||
|
||||
if (ret < 0)
|
||||
ksft_exit_fail_msg("%s: fchmodat2(AT_SYMLINK_NOFOLLOW) failed\n",
|
||||
__func__);
|
||||
|
||||
if (!expect_mode(dfd, "regfile", 0100600))
|
||||
ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n",
|
||||
__func__);
|
||||
|
||||
ksft_test_result_pass("fchmodat2(regfile)\n");
|
||||
}
|
||||
|
||||
void test_symlink(void)
|
||||
{
|
||||
int dfd, ret;
|
||||
|
||||
dfd = setup_testdir();
|
||||
|
||||
ret = sys_fchmodat2(dfd, "symlink", 0640, 0);
|
||||
|
||||
if (ret < 0)
|
||||
ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__);
|
||||
|
||||
if (!expect_mode(dfd, "regfile", 0100640))
|
||||
ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n",
|
||||
__func__);
|
||||
|
||||
if (!expect_mode(dfd, "symlink", 0120777))
|
||||
ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2\n",
|
||||
__func__);
|
||||
|
||||
ret = sys_fchmodat2(dfd, "symlink", 0600, AT_SYMLINK_NOFOLLOW);
|
||||
|
||||
/*
|
||||
* On certain filesystems (xfs or btrfs), chmod operation fails. So we
|
||||
* first check the symlink target but if the operation fails we mark the
|
||||
* test as skipped.
|
||||
*
|
||||
* https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html
|
||||
*/
|
||||
if (ret == 0 && !expect_mode(dfd, "symlink", 0120600))
|
||||
ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2 with nofollow\n",
|
||||
__func__);
|
||||
|
||||
if (!expect_mode(dfd, "regfile", 0100640))
|
||||
ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n",
|
||||
__func__);
|
||||
|
||||
if (ret != 0)
|
||||
ksft_test_result_skip("fchmodat2(symlink)\n");
|
||||
else
|
||||
ksft_test_result_pass("fchmodat2(symlink)\n");
|
||||
}
|
||||
|
||||
#define NUM_TESTS 2
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ksft_print_header();
|
||||
ksft_set_plan(NUM_TESTS);
|
||||
|
||||
test_regfile();
|
||||
test_symlink();
|
||||
|
||||
if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
|
||||
ksft_exit_fail();
|
||||
else
|
||||
ksft_exit_pass();
|
||||
}
|
Loading…
Reference in New Issue
Block a user