History

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.

Miklos Szeredi committed 6 years ago

Commit c8ffd8b1 parent 55923e423 files · +62 −13Browse files

Unified · Split

23 files
  1. arch/alpha/kernel/syscalls/syscall.tbl +1 −0
  2. arch/arm/tools/syscall.tbl +1 −0
  3. arch/arm64/include/asm/unistd.h +1 −1
  4. arch/arm64/include/asm/unistd32.h +2 −0
  5. arch/ia64/kernel/syscalls/syscall.tbl +1 −0
  6. arch/m68k/kernel/syscalls/syscall.tbl +1 −0
  7. arch/microblaze/kernel/syscalls/syscall.tbl +1 −0
  8. arch/mips/kernel/syscalls/syscall_n32.tbl +1 −0
  9. arch/mips/kernel/syscalls/syscall_n64.tbl +1 −0
  10. arch/mips/kernel/syscalls/syscall_o32.tbl +1 −0
  11. arch/parisc/kernel/syscalls/syscall.tbl +1 −0
  12. arch/powerpc/kernel/syscalls/syscall.tbl +1 −0
  13. arch/s390/kernel/syscalls/syscall.tbl +1 −0
  14. arch/sh/kernel/syscalls/syscall.tbl +1 −0
  15. arch/sparc/kernel/syscalls/syscall.tbl +1 −0
  16. arch/x86/entry/syscalls/syscall_32.tbl +1 −0
  17. arch/x86/entry/syscalls/syscall_64.tbl +1 −0
  18. arch/xtensa/kernel/syscalls/syscall.tbl +1 −0
  19. fs/internal.h +0 −1
  20. fs/open.c +26 −8
  21. include/linux/syscalls.h +4 −2
  22. include/uapi/asm-generic/unistd.h +3 −1
  23. include/uapi/linux/fcntl.h +10 −0
arch/alpha/kernel/syscalls/syscall.tbl+1−0
@@ -477,3 +477,4 @@ · 476 unmodified lines
477477 # 545 reserved for clone3
478478 547 common openat2 sys_openat2
479479 548 common pidfd_getfd sys_pidfd_getfd
480+549 common faccessat2 sys_faccessat2
arch/arm/tools/syscall.tbl+1−0
@@ -451,3 +451,4 @@ · 450 unmodified lines
451451 435 common clone3 sys_clone3
452452 437 common openat2 sys_openat2
453453 438 common pidfd_getfd sys_pidfd_getfd
454+439 common faccessat2 sys_faccessat2
arch/arm64/include/asm/unistd.h+1−1
@@ -38,7 +38,7 @@ · 37 unmodified lines
3838 #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)
3939 #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)
4040
41−#define __NR_compat_syscalls 439
41+#define __NR_compat_syscalls 440
4242 #endif
4343
4444 #define __ARCH_WANT_SYS_CLONE
arch/arm64/include/asm/unistd32.h+2−0
@@ -883,6 +883,8 @@ __SYSCALL(__NR_clone3, sys_clone3) · 882 unmodified lines
883883 __SYSCALL(__NR_openat2, sys_openat2)
884884 #define __NR_pidfd_getfd 438
885885 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
886+#define __NR_faccessat2 439
887+__SYSCALL(__NR_faccessat2, sys_faccessat2)
886888
887889 /*
888890 * Please add new compat syscalls above this comment and update
arch/ia64/kernel/syscalls/syscall.tbl+1−0
@@ -358,3 +358,4 @@ · 357 unmodified lines
358358 # 435 reserved for clone3
359359 437 common openat2 sys_openat2
360360 438 common pidfd_getfd sys_pidfd_getfd
361+439 common faccessat2 sys_faccessat2
arch/m68k/kernel/syscalls/syscall.tbl+1−0
@@ -437,3 +437,4 @@ · 436 unmodified lines
437437 435 common clone3 __sys_clone3
438438 437 common openat2 sys_openat2
439439 438 common pidfd_getfd sys_pidfd_getfd
440+439 common faccessat2 sys_faccessat2
arch/microblaze/kernel/syscalls/syscall.tbl+1−0
@@ -443,3 +443,4 @@ · 442 unmodified lines
443443 435 common clone3 sys_clone3
444444 437 common openat2 sys_openat2
445445 438 common pidfd_getfd sys_pidfd_getfd
446+439 common faccessat2 sys_faccessat2
arch/mips/kernel/syscalls/syscall_n32.tbl+1−0
@@ -376,3 +376,4 @@ · 375 unmodified lines
376376 435 n32 clone3 __sys_clone3
377377 437 n32 openat2 sys_openat2
378378 438 n32 pidfd_getfd sys_pidfd_getfd
379+439 n32 faccessat2 sys_faccessat2
arch/mips/kernel/syscalls/syscall_n64.tbl+1−0
@@ -352,3 +352,4 @@ · 351 unmodified lines
352352 435 n64 clone3 __sys_clone3
353353 437 n64 openat2 sys_openat2
354354 438 n64 pidfd_getfd sys_pidfd_getfd
355+439 n64 faccessat2 sys_faccessat2
arch/mips/kernel/syscalls/syscall_o32.tbl+1−0
@@ -425,3 +425,4 @@ · 424 unmodified lines
425425 435 o32 clone3 __sys_clone3
426426 437 o32 openat2 sys_openat2
427427 438 o32 pidfd_getfd sys_pidfd_getfd
428+439 o32 faccessat2 sys_faccessat2
arch/parisc/kernel/syscalls/syscall.tbl+1−0
@@ -435,3 +435,4 @@ · 434 unmodified lines
435435 435 common clone3 sys_clone3_wrapper
436436 437 common openat2 sys_openat2
437437 438 common pidfd_getfd sys_pidfd_getfd
438+439 common faccessat2 sys_faccessat2
arch/powerpc/kernel/syscalls/syscall.tbl+1−0
@@ -527,3 +527,4 @@ · 526 unmodified lines
527527 435 spu clone3 sys_ni_syscall
528528 437 common openat2 sys_openat2
529529 438 common pidfd_getfd sys_pidfd_getfd
530+439 common faccessat2 sys_faccessat2
arch/s390/kernel/syscalls/syscall.tbl+1−0
@@ -440,3 +440,4 @@ · 439 unmodified lines
440440 435 common clone3 sys_clone3 sys_clone3
441441 437 common openat2 sys_openat2 sys_openat2
442442 438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd
443+439 common faccessat2 sys_faccessat2 sys_faccessat2
arch/sh/kernel/syscalls/syscall.tbl+1−0
@@ -440,3 +440,4 @@ · 439 unmodified lines
440440 # 435 reserved for clone3
441441 437 common openat2 sys_openat2
442442 438 common pidfd_getfd sys_pidfd_getfd
443+439 common faccessat2 sys_faccessat2
arch/sparc/kernel/syscalls/syscall.tbl+1−0
@@ -483,3 +483,4 @@ · 482 unmodified lines
483483 # 435 reserved for clone3
484484 437 common openat2 sys_openat2
485485 438 common pidfd_getfd sys_pidfd_getfd
486+439 common faccessat2 sys_faccessat2
arch/x86/entry/syscalls/syscall_32.tbl+1−0
@@ -442,3 +442,4 @@ · 441 unmodified lines
442442 435 i386 clone3 sys_clone3
443443 437 i386 openat2 sys_openat2
444444 438 i386 pidfd_getfd sys_pidfd_getfd
445+439 i386 faccessat2 sys_faccessat2
arch/x86/entry/syscalls/syscall_64.tbl+1−0
@@ -359,6 +359,7 @@ · 358 unmodified lines
359359 435 common clone3 sys_clone3
360360 437 common openat2 sys_openat2
361361 438 common pidfd_getfd sys_pidfd_getfd
362+439 common faccessat2 sys_faccessat2
362363
363364 #
364365 # x32-specific system call numbers start at 512 to avoid cache impact
arch/xtensa/kernel/syscalls/syscall.tbl+1−0
@@ -408,3 +408,4 @@ · 407 unmodified lines
408408 435 common clone3 sys_clone3
409409 437 common openat2 sys_openat2
410410 438 common pidfd_getfd sys_pidfd_getfd
411+439 common faccessat2 sys_faccessat2
fs/internal.h+0−1
@@ -126,7 +126,6 @@ extern struct open_how build_open_how(int flags, umode_t mode); · 125 unmodified lines
126126 extern int build_open_flags(const struct open_how *how, struct open_flags *op);
127127
128128 long do_sys_ftruncate(unsigned int fd, loff_t length, int small);
129−long do_faccessat(int dfd, const char __user *filename, int mode);
130129 int do_fchmodat(int dfd, const char __user *filename, umode_t mode);
131130 int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group,
132131 int flag);
fs/open.c+26−8
@@ -394,20 +394,30 @@ static const struct cred *access_override_creds(void) · 393 unmodified lines
394394 return old_cred;
395395 }
396396
397−long do_faccessat(int dfd, const char __user *filename, int mode)
397+long do_faccessat(int dfd, const char __user *filename, int mode, int flags)
398398 {
399399 struct path path;
400400 struct inode *inode;
401401 int res;
402402 unsigned int lookup_flags = LOOKUP_FOLLOW;
403− const struct cred *old_cred;
403+ const struct cred *old_cred = NULL;
404404
405405 if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
406406 return -EINVAL;
407407
408− old_cred = access_override_creds();
409− if (!old_cred)
410− return -ENOMEM;
408+ if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
409+ return -EINVAL;
410+
411+ if (flags & AT_SYMLINK_NOFOLLOW)
412+ lookup_flags &= ~LOOKUP_FOLLOW;
413+ if (flags & AT_EMPTY_PATH)
414+ lookup_flags |= LOOKUP_EMPTY;
415+
416+ if (!(flags & AT_EACCESS)) {
417+ old_cred = access_override_creds();
418+ if (!old_cred)
419+ return -ENOMEM;
420+ }
411421
412422 retry:
413423 res = user_path_at(dfd, filename, lookup_flags, &path);
@@ -450,18 +460,26 @@ out_path_release: · 36 unmodified lines
450460 goto retry;
451461 }
452462 out:
453− revert_creds(old_cred);
463+ if (old_cred)
464+ revert_creds(old_cred);
465+
454466 return res;
455467 }
456468
457469 SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
458470 {
459− return do_faccessat(dfd, filename, mode);
471+ return do_faccessat(dfd, filename, mode, 0);
472+}
473+
474+SYSCALL_DEFINE4(faccessat2, int, dfd, const char __user *, filename, int, mode,
475+ int, flags)
476+{
477+ return do_faccessat(dfd, filename, mode, flags);
460478 }
461479
462480 SYSCALL_DEFINE2(access, const char __user *, filename, int, mode)
463481 {
464− return do_faccessat(AT_FDCWD, filename, mode);
482+ return do_faccessat(AT_FDCWD, filename, mode, 0);
465483 }
466484
467485 int ksys_chdir(const char __user *filename)
include/linux/syscalls.h+4−2
@@ -428,6 +428,8 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length); · 427 unmodified lines
428428 #endif
429429 asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len);
430430 asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode);
431+asmlinkage long sys_faccessat2(int dfd, const char __user *filename, int mode,
432+ int flags);
431433 asmlinkage long sys_chdir(const char __user *filename);
432434 asmlinkage long sys_fchdir(unsigned int fd);
433435 asmlinkage long sys_chroot(const char __user *filename);
@@ -1333,11 +1335,11 @@ static inline int ksys_chmod(const char __user *filename, umode_t mode) · 899 unmodified lines
13331335 return do_fchmodat(AT_FDCWD, filename, mode);
13341336 }
13351337
1336−extern long do_faccessat(int dfd, const char __user *filename, int mode);
1338+long do_faccessat(int dfd, const char __user *filename, int mode, int flags);
13371339
13381340 static inline long ksys_access(const char __user *filename, int mode)
13391341 {
1340− return do_faccessat(AT_FDCWD, filename, mode);
1342+ return do_faccessat(AT_FDCWD, filename, mode, 0);
13411343 }
13421344
13431345 extern int do_fchownat(int dfd, const char __user *filename, uid_t user,
include/uapi/asm-generic/unistd.h+3−1
@@ -855,9 +855,11 @@ __SYSCALL(__NR_clone3, sys_clone3) · 854 unmodified lines
855855 __SYSCALL(__NR_openat2, sys_openat2)
856856 #define __NR_pidfd_getfd 438
857857 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
858+#define __NR_faccessat2 439
859+__SYSCALL(__NR_faccessat2, sys_faccessat2)
858860
859861 #undef __NR_syscalls
860−#define __NR_syscalls 439
862+#define __NR_syscalls 440
861863
862864 /*
863865 * 32 bit systems traditionally used different
include/uapi/linux/fcntl.h+10−0
@@ -84,10 +84,20 @@ · 83 unmodified lines
8484 #define DN_ATTRIB 0x00000020 /* File changed attibutes */
8585 #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
8686
87+/*
88+ * The constants AT_REMOVEDIR and AT_EACCESS have the same value. AT_EACCESS is
89+ * meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to
90+ * unlinkat. The two functions do completely different things and therefore,
91+ * the flags can be allowed to overlap. For example, passing AT_REMOVEDIR to
92+ * faccessat would be undefined behavior and thus treating it equivalent to
93+ * AT_EACCESS is valid undefined behavior.
94+ */
8795 #define AT_FDCWD -100 /* Special value used to indicate
8896 openat should use the current
8997 working directory. */
9098 #define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */
99+#define AT_EACCESS 0x200 /* Test access permitted for
100+ effective IDs, not real IDs. */
91101 #define AT_REMOVEDIR 0x200 /* Remove directory instead of
92102 unlinking file. */
93103 #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */