vfs: add faccessat2 syscall
POSIX defines faccessat() as having a fourth "flags" argument, while the linux syscall doesn't have it. Glibc tries to emulate AT_EACCESS and AT_SYMLINK_NOFOLLOW, but AT_EACCESS emulation is broken. Add a new faccessat(2) syscall with the added flags argument and implement both flags. The value of AT_EACCESS is defined in glibc headers to be the same as AT_REMOVEDIR. Use this value for the kernel interface as well, together with the explanatory comment. Also add AT_EMPTY_PATH support, which is not documented by POSIX, but can be useful and is trivial to implement. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
55923e4d7d
commit
c8ffd8bcdd
@ -477,3 +477,4 @@
|
||||
# 545 reserved for clone3
|
||||
547 common openat2 sys_openat2
|
||||
548 common pidfd_getfd sys_pidfd_getfd
|
||||
549 common faccessat2 sys_faccessat2
|
||||
|
@ -451,3 +451,4 @@
|
||||
435 common clone3 sys_clone3
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -38,7 +38,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 439
|
||||
#define __NR_compat_syscalls 440
|
||||
#endif
|
||||
|
||||
#define __ARCH_WANT_SYS_CLONE
|
||||
|
@ -883,6 +883,8 @@ __SYSCALL(__NR_clone3, sys_clone3)
|
||||
__SYSCALL(__NR_openat2, sys_openat2)
|
||||
#define __NR_pidfd_getfd 438
|
||||
__SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
|
||||
#define __NR_faccessat2 439
|
||||
__SYSCALL(__NR_faccessat2, sys_faccessat2)
|
||||
|
||||
/*
|
||||
* Please add new compat syscalls above this comment and update
|
||||
|
@ -358,3 +358,4 @@
|
||||
# 435 reserved for clone3
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -437,3 +437,4 @@
|
||||
435 common clone3 __sys_clone3
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -443,3 +443,4 @@
|
||||
435 common clone3 sys_clone3
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -376,3 +376,4 @@
|
||||
435 n32 clone3 __sys_clone3
|
||||
437 n32 openat2 sys_openat2
|
||||
438 n32 pidfd_getfd sys_pidfd_getfd
|
||||
439 n32 faccessat2 sys_faccessat2
|
||||
|
@ -352,3 +352,4 @@
|
||||
435 n64 clone3 __sys_clone3
|
||||
437 n64 openat2 sys_openat2
|
||||
438 n64 pidfd_getfd sys_pidfd_getfd
|
||||
439 n64 faccessat2 sys_faccessat2
|
||||
|
@ -425,3 +425,4 @@
|
||||
435 o32 clone3 __sys_clone3
|
||||
437 o32 openat2 sys_openat2
|
||||
438 o32 pidfd_getfd sys_pidfd_getfd
|
||||
439 o32 faccessat2 sys_faccessat2
|
||||
|
@ -435,3 +435,4 @@
|
||||
435 common clone3 sys_clone3_wrapper
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -527,3 +527,4 @@
|
||||
435 spu clone3 sys_ni_syscall
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -440,3 +440,4 @@
|
||||
435 common clone3 sys_clone3 sys_clone3
|
||||
437 common openat2 sys_openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2 sys_faccessat2
|
||||
|
@ -440,3 +440,4 @@
|
||||
# 435 reserved for clone3
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -483,3 +483,4 @@
|
||||
# 435 reserved for clone3
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -442,3 +442,4 @@
|
||||
435 i386 clone3 sys_clone3
|
||||
437 i386 openat2 sys_openat2
|
||||
438 i386 pidfd_getfd sys_pidfd_getfd
|
||||
439 i386 faccessat2 sys_faccessat2
|
||||
|
@ -359,6 +359,7 @@
|
||||
435 common clone3 sys_clone3
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
||||
#
|
||||
# x32-specific system call numbers start at 512 to avoid cache impact
|
||||
|
@ -408,3 +408,4 @@
|
||||
435 common clone3 sys_clone3
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
@ -126,7 +126,6 @@ extern struct open_how build_open_how(int flags, umode_t mode);
|
||||
extern int build_open_flags(const struct open_how *how, struct open_flags *op);
|
||||
|
||||
long do_sys_ftruncate(unsigned int fd, loff_t length, int small);
|
||||
long do_faccessat(int dfd, const char __user *filename, int mode);
|
||||
int do_fchmodat(int dfd, const char __user *filename, umode_t mode);
|
||||
int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group,
|
||||
int flag);
|
||||
|
34
fs/open.c
34
fs/open.c
@ -394,20 +394,30 @@ static const struct cred *access_override_creds(void)
|
||||
return old_cred;
|
||||
}
|
||||
|
||||
long do_faccessat(int dfd, const char __user *filename, int mode)
|
||||
long do_faccessat(int dfd, const char __user *filename, int mode, int flags)
|
||||
{
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
const struct cred *old_cred;
|
||||
const struct cred *old_cred = NULL;
|
||||
|
||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||
return -EINVAL;
|
||||
|
||||
old_cred = access_override_creds();
|
||||
if (!old_cred)
|
||||
return -ENOMEM;
|
||||
if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & AT_SYMLINK_NOFOLLOW)
|
||||
lookup_flags &= ~LOOKUP_FOLLOW;
|
||||
if (flags & AT_EMPTY_PATH)
|
||||
lookup_flags |= LOOKUP_EMPTY;
|
||||
|
||||
if (!(flags & AT_EACCESS)) {
|
||||
old_cred = access_override_creds();
|
||||
if (!old_cred)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
retry:
|
||||
res = user_path_at(dfd, filename, lookup_flags, &path);
|
||||
@ -450,18 +460,26 @@ out_path_release:
|
||||
goto retry;
|
||||
}
|
||||
out:
|
||||
revert_creds(old_cred);
|
||||
if (old_cred)
|
||||
revert_creds(old_cred);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
{
|
||||
return do_faccessat(dfd, filename, mode);
|
||||
return do_faccessat(dfd, filename, mode, 0);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(faccessat2, int, dfd, const char __user *, filename, int, mode,
|
||||
int, flags)
|
||||
{
|
||||
return do_faccessat(dfd, filename, mode, flags);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(access, const char __user *, filename, int, mode)
|
||||
{
|
||||
return do_faccessat(AT_FDCWD, filename, mode);
|
||||
return do_faccessat(AT_FDCWD, filename, mode, 0);
|
||||
}
|
||||
|
||||
int ksys_chdir(const char __user *filename)
|
||||
|
@ -428,6 +428,8 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length);
|
||||
#endif
|
||||
asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len);
|
||||
asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode);
|
||||
asmlinkage long sys_faccessat2(int dfd, const char __user *filename, int mode,
|
||||
int flags);
|
||||
asmlinkage long sys_chdir(const char __user *filename);
|
||||
asmlinkage long sys_fchdir(unsigned int fd);
|
||||
asmlinkage long sys_chroot(const char __user *filename);
|
||||
@ -1333,11 +1335,11 @@ static inline int ksys_chmod(const char __user *filename, umode_t mode)
|
||||
return do_fchmodat(AT_FDCWD, filename, mode);
|
||||
}
|
||||
|
||||
extern long do_faccessat(int dfd, const char __user *filename, int mode);
|
||||
long do_faccessat(int dfd, const char __user *filename, int mode, int flags);
|
||||
|
||||
static inline long ksys_access(const char __user *filename, int mode)
|
||||
{
|
||||
return do_faccessat(AT_FDCWD, filename, mode);
|
||||
return do_faccessat(AT_FDCWD, filename, mode, 0);
|
||||
}
|
||||
|
||||
extern int do_fchownat(int dfd, const char __user *filename, uid_t user,
|
||||
|
@ -855,9 +855,11 @@ __SYSCALL(__NR_clone3, sys_clone3)
|
||||
__SYSCALL(__NR_openat2, sys_openat2)
|
||||
#define __NR_pidfd_getfd 438
|
||||
__SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
|
||||
#define __NR_faccessat2 439
|
||||
__SYSCALL(__NR_faccessat2, sys_faccessat2)
|
||||
|
||||
#undef __NR_syscalls
|
||||
#define __NR_syscalls 439
|
||||
#define __NR_syscalls 440
|
||||
|
||||
/*
|
||||
* 32 bit systems traditionally used different
|
||||
|
@ -84,10 +84,20 @@
|
||||
#define DN_ATTRIB 0x00000020 /* File changed attibutes */
|
||||
#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
|
||||
|
||||
/*
|
||||
* The constants AT_REMOVEDIR and AT_EACCESS have the same value. AT_EACCESS is
|
||||
* meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to
|
||||
* unlinkat. The two functions do completely different things and therefore,
|
||||
* the flags can be allowed to overlap. For example, passing AT_REMOVEDIR to
|
||||
* faccessat would be undefined behavior and thus treating it equivalent to
|
||||
* AT_EACCESS is valid undefined behavior.
|
||||
*/
|
||||
#define AT_FDCWD -100 /* Special value used to indicate
|
||||
openat should use the current
|
||||
working directory. */
|
||||
#define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */
|
||||
#define AT_EACCESS 0x200 /* Test access permitted for
|
||||
effective IDs, not real IDs. */
|
||||
#define AT_REMOVEDIR 0x200 /* Remove directory instead of
|
||||
unlinking file. */
|
||||
#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
|
||||
|
Loading…
Reference in New Issue
Block a user