vfs: add faccessat2 syscall
authorMiklos Szeredi <mszeredi@redhat.com>
Thu, 14 May 2020 14:44:25 +0000 (16:44 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 14 May 2020 14:44:25 +0000 (16:44 +0200)
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>
23 files changed:
arch/alpha/kernel/syscalls/syscall.tbl
arch/arm/tools/syscall.tbl
arch/arm64/include/asm/unistd.h
arch/arm64/include/asm/unistd32.h
arch/ia64/kernel/syscalls/syscall.tbl
arch/m68k/kernel/syscalls/syscall.tbl
arch/microblaze/kernel/syscalls/syscall.tbl
arch/mips/kernel/syscalls/syscall_n32.tbl
arch/mips/kernel/syscalls/syscall_n64.tbl
arch/mips/kernel/syscalls/syscall_o32.tbl
arch/parisc/kernel/syscalls/syscall.tbl
arch/powerpc/kernel/syscalls/syscall.tbl
arch/s390/kernel/syscalls/syscall.tbl
arch/sh/kernel/syscalls/syscall.tbl
arch/sparc/kernel/syscalls/syscall.tbl
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/entry/syscalls/syscall_64.tbl
arch/xtensa/kernel/syscalls/syscall.tbl
fs/internal.h
fs/open.c
include/linux/syscalls.h
include/uapi/asm-generic/unistd.h
include/uapi/linux/fcntl.h

index 36d42da..5ddd128 100644 (file)
 # 545 reserved for clone3
 547    common  openat2                         sys_openat2
 548    common  pidfd_getfd                     sys_pidfd_getfd
+549    common  faccessat2                      sys_faccessat2
index 4d1cf74..d5cae5f 100644 (file)
 435    common  clone3                          sys_clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 803039d..3b85959 100644 (file)
@@ -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
index c1c6163..6d95d0c 100644 (file)
@@ -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
index 042911e..49e325b 100644 (file)
 # 435 reserved for clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index f4f49fc..f71b1bb 100644 (file)
 435    common  clone3                          __sys_clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 4c67b11..edacc45 100644 (file)
 435    common  clone3                          sys_clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 1f9e8ad..f777141 100644 (file)
 435    n32     clone3                          __sys_clone3
 437    n32     openat2                         sys_openat2
 438    n32     pidfd_getfd                     sys_pidfd_getfd
+439    n32     faccessat2                      sys_faccessat2
index c0b9d80..da8c763 100644 (file)
 435    n64     clone3                          __sys_clone3
 437    n64     openat2                         sys_openat2
 438    n64     pidfd_getfd                     sys_pidfd_getfd
+439    n64     faccessat2                      sys_faccessat2
index ac58677..1328062 100644 (file)
 435    o32     clone3                          __sys_clone3
 437    o32     openat2                         sys_openat2
 438    o32     pidfd_getfd                     sys_pidfd_getfd
+439    o32     faccessat2                      sys_faccessat2
index 52a15f5..5a758fa 100644 (file)
 435    common  clone3                          sys_clone3_wrapper
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 220ae11..f833a31 100644 (file)
 435    spu     clone3                          sys_ni_syscall
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index bd7bd35..bfdcb76 100644 (file)
 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
index c7a30fc..acc35da 100644 (file)
 # 435 reserved for clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index f13615e..8004a27 100644 (file)
 # 435 reserved for clone3
 437    common  openat2                 sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 54581ac..d8f8a1a 100644 (file)
 435    i386    clone3                  sys_clone3
 437    i386    openat2                 sys_openat2
 438    i386    pidfd_getfd             sys_pidfd_getfd
+439    i386    faccessat2              sys_faccessat2
index 37b844f..78847b3 100644 (file)
 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
index 85a9ab1..69d0d73 100644 (file)
 435    common  clone3                          sys_clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index aa5d455..0d467e3 100644 (file)
@@ -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);
index 0ea3cd1..e62b1db 100644 (file)
--- 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)
index 1815065..7c354c2 100644 (file)
@@ -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,
index 3a3201e..f4a0130 100644 (file)
@@ -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
index ca88b7b..2f86b2a 100644 (file)
 #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.  */