diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2020-05-14 16:44:25 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2020-05-14 16:44:25 +0200 |
commit | c8ffd8bcdd28296a198f237cc595148a8d4adfbe (patch) | |
tree | 71ce857ad8d9feea14cfe9619013e84a88bb374c /fs/open.c | |
parent | vfs: don't parse "silent" option (diff) | |
download | linux-c8ffd8bcdd28296a198f237cc595148a8d4adfbe.tar.xz linux-c8ffd8bcdd28296a198f237cc595148a8d4adfbe.zip |
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>
Diffstat (limited to 'fs/open.c')
-rw-r--r-- | fs/open.c | 34 |
1 files changed, 26 insertions, 8 deletions
diff --git a/fs/open.c b/fs/open.c index 0ea3cd1a1250..e62b1db06638 100644 --- a/fs/open.c +++ b/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) |