Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

open: introduce openat2(2) syscall

/* Background. */
For a very long time, extending openat(2) with new features has been
incredibly frustrating. This stems from the fact that openat(2) is
possibly the most famous counter-example to the mantra "don't silently
accept garbage from userspace" -- it doesn't check whether unknown flags
are present[1].

This means that (generally) the addition of new flags to openat(2) has
been fraught with backwards-compatibility issues (O_TMPFILE has to be
defined as __O_TMPFILE|O_DIRECTORY|[O_RDWR or O_WRONLY] to ensure old
kernels gave errors, since it's insecure to silently ignore the
flag[2]). All new security-related flags therefore have a tough road to
being added to openat(2).

Userspace also has a hard time figuring out whether a particular flag is
supported on a particular kernel. While it is now possible with
contemporary kernels (thanks to [3]), older kernels will expose unknown
flag bits through fcntl(F_GETFL). Giving a clear -EINVAL during
openat(2) time matches modern syscall designs and is far more
fool-proof.

In addition, the newly-added path resolution restriction LOOKUP flags
(which we would like to expose to user-space) don't feel related to the
pre-existing O_* flag set -- they affect all components of path lookup.
We'd therefore like to add a new flag argument.

Adding a new syscall allows us to finally fix the flag-ignoring problem,
and we can make it extensible enough so that we will hopefully never
need an openat3(2).

/* Syscall Prototype. */
/*
* open_how is an extensible structure (similar in interface to
* clone3(2) or sched_setattr(2)). The size parameter must be set to
* sizeof(struct open_how), to allow for future extensions. All future
* extensions will be appended to open_how, with their zero value
* acting as a no-op default.
*/
struct open_how { /* ... */ };

int openat2(int dfd, const char *pathname,
struct open_how *how, size_t size);

/* Description. */
The initial version of 'struct open_how' contains the following fields:

flags
Used to specify openat(2)-style flags. However, any unknown flag
bits or otherwise incorrect flag combinations (like O_PATH|O_RDWR)
will result in -EINVAL. In addition, this field is 64-bits wide to
allow for more O_ flags than currently permitted with openat(2).

mode
The file mode for O_CREAT or O_TMPFILE.

Must be set to zero if flags does not contain O_CREAT or O_TMPFILE.

resolve
Restrict path resolution (in contrast to O_* flags they affect all
path components). The current set of flags are as follows (at the
moment, all of the RESOLVE_ flags are implemented as just passing
the corresponding LOOKUP_ flag).

RESOLVE_NO_XDEV => LOOKUP_NO_XDEV
RESOLVE_NO_SYMLINKS => LOOKUP_NO_SYMLINKS
RESOLVE_NO_MAGICLINKS => LOOKUP_NO_MAGICLINKS
RESOLVE_BENEATH => LOOKUP_BENEATH
RESOLVE_IN_ROOT => LOOKUP_IN_ROOT

open_how does not contain an embedded size field, because it is of
little benefit (userspace can figure out the kernel open_how size at
runtime fairly easily without it). It also only contains u64s (even
though ->mode arguably should be a u16) to avoid having padding fields
which are never used in the future.

Note that as a result of the new how->flags handling, O_PATH|O_TMPFILE
is no longer permitted for openat(2). As far as I can tell, this has
always been a bug and appears to not be used by userspace (and I've not
seen any problems on my machines by disallowing it). If it turns out
this breaks something, we can special-case it and only permit it for
openat(2) but not openat2(2).

After input from Florian Weimer, the new open_how and flag definitions
are inside a separate header from uapi/linux/fcntl.h, to avoid problems
that glibc has with importing that header.

/* Testing. */
In a follow-up patch there are over 200 selftests which ensure that this
syscall has the correct semantics and will correctly handle several
attack scenarios.

In addition, I've written a userspace library[4] which provides
convenient wrappers around openat2(RESOLVE_IN_ROOT) (this is necessary
because no other syscalls support RESOLVE_IN_ROOT, and thus lots of care
must be taken when using RESOLVE_IN_ROOT'd file descriptors with other
syscalls). During the development of this patch, I've run numerous
verification tests using libpathrs (showing that the API is reasonably
usable by userspace).

/* Future Work. */
Additional RESOLVE_ flags have been suggested during the review period.
These can be easily implemented separately (such as blocking auto-mount
during resolution).

Furthermore, there are some other proposed changes to the openat(2)
interface (the most obvious example is magic-link hardening[5]) which
would be a good opportunity to add a way for userspace to restrict how
O_PATH file descriptors can be re-opened.

Another possible avenue of future work would be some kind of
CHECK_FIELDS[6] flag which causes the kernel to indicate to userspace
which openat2(2) flags and fields are supported by the current kernel
(to avoid userspace having to go through several guesses to figure it
out).

[1]: https://lwn.net/Articles/588444/
[2]: https://lore.kernel.org/lkml/CA+55aFyyxJL1LyXZeBsf2ypriraj5ut1XkNDsunRBqgVjZU_6Q@mail.gmail.com
[3]: commit 629e014bb834 ("fs: completely ignore unknown open flags")
[4]: https://sourceware.org/bugzilla/show_bug.cgi?id=17523
[5]: https://lore.kernel.org/lkml/20190930183316.10190-2-cyphar@cyphar.com/
[6]: https://youtu.be/ggD-eb3yPVs

Suggested-by: Christian Brauner <christian.brauner@ubuntu.com>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

authored by

Aleksa Sarai and committed by
Al Viro
fddb5d43 ab87f9a5

+202 -43
+3 -1
CREDITS
··· 3302 3302 N: Aleksa Sarai 3303 3303 E: cyphar@cyphar.com 3304 3304 W: https://www.cyphar.com/ 3305 - D: `pids` cgroup subsystem 3305 + D: /sys/fs/cgroup/pids 3306 + D: openat2(2) 3307 + S: Sydney, Australia 3306 3308 3307 3309 N: Dipankar Sarma 3308 3310 E: dipankar@in.ibm.com
+1
MAINTAINERS
··· 6397 6397 F: include/linux/fs.h 6398 6398 F: include/linux/fs_types.h 6399 6399 F: include/uapi/linux/fs.h 6400 + F: include/uapi/linux/openat2.h 6400 6401 6401 6402 FINTEK F75375S HARDWARE MONITOR AND FAN CONTROLLER DRIVER 6402 6403 M: Riku Voipio <riku.voipio@iki.fi>
+1
arch/alpha/kernel/syscalls/syscall.tbl
··· 475 475 543 common fspick sys_fspick 476 476 544 common pidfd_open sys_pidfd_open 477 477 # 545 reserved for clone3 478 + 547 common openat2 sys_openat2
+1
arch/arm/tools/syscall.tbl
··· 449 449 433 common fspick sys_fspick 450 450 434 common pidfd_open sys_pidfd_open 451 451 435 common clone3 sys_clone3 452 + 437 common openat2 sys_openat2
+1 -1
arch/arm64/include/asm/unistd.h
··· 38 38 #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) 39 39 #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800) 40 40 41 - #define __NR_compat_syscalls 436 41 + #define __NR_compat_syscalls 438 42 42 #endif 43 43 44 44 #define __ARCH_WANT_SYS_CLONE
+2
arch/arm64/include/asm/unistd32.h
··· 879 879 __SYSCALL(__NR_pidfd_open, sys_pidfd_open) 880 880 #define __NR_clone3 435 881 881 __SYSCALL(__NR_clone3, sys_clone3) 882 + #define __NR_openat2 437 883 + __SYSCALL(__NR_openat2, sys_openat2) 882 884 883 885 /* 884 886 * Please add new compat syscalls above this comment and update
+1
arch/ia64/kernel/syscalls/syscall.tbl
··· 356 356 433 common fspick sys_fspick 357 357 434 common pidfd_open sys_pidfd_open 358 358 # 435 reserved for clone3 359 + 437 common openat2 sys_openat2
+1
arch/m68k/kernel/syscalls/syscall.tbl
··· 435 435 433 common fspick sys_fspick 436 436 434 common pidfd_open sys_pidfd_open 437 437 # 435 reserved for clone3 438 + 437 common openat2 sys_openat2
+1
arch/microblaze/kernel/syscalls/syscall.tbl
··· 441 441 433 common fspick sys_fspick 442 442 434 common pidfd_open sys_pidfd_open 443 443 435 common clone3 sys_clone3 444 + 437 common openat2 sys_openat2
+1
arch/mips/kernel/syscalls/syscall_n32.tbl
··· 374 374 433 n32 fspick sys_fspick 375 375 434 n32 pidfd_open sys_pidfd_open 376 376 435 n32 clone3 __sys_clone3 377 + 437 n32 openat2 sys_openat2
+1
arch/mips/kernel/syscalls/syscall_n64.tbl
··· 350 350 433 n64 fspick sys_fspick 351 351 434 n64 pidfd_open sys_pidfd_open 352 352 435 n64 clone3 __sys_clone3 353 + 437 n64 openat2 sys_openat2
+1
arch/mips/kernel/syscalls/syscall_o32.tbl
··· 423 423 433 o32 fspick sys_fspick 424 424 434 o32 pidfd_open sys_pidfd_open 425 425 435 o32 clone3 __sys_clone3 426 + 437 o32 openat2 sys_openat2
+1
arch/parisc/kernel/syscalls/syscall.tbl
··· 433 433 433 common fspick sys_fspick 434 434 434 common pidfd_open sys_pidfd_open 435 435 435 common clone3 sys_clone3_wrapper 436 + 437 common openat2 sys_openat2
+1
arch/powerpc/kernel/syscalls/syscall.tbl
··· 517 517 433 common fspick sys_fspick 518 518 434 common pidfd_open sys_pidfd_open 519 519 435 nospu clone3 ppc_clone3 520 + 437 common openat2 sys_openat2
+1
arch/s390/kernel/syscalls/syscall.tbl
··· 438 438 433 common fspick sys_fspick sys_fspick 439 439 434 common pidfd_open sys_pidfd_open sys_pidfd_open 440 440 435 common clone3 sys_clone3 sys_clone3 441 + 437 common openat2 sys_openat2 sys_openat2
+1
arch/sh/kernel/syscalls/syscall.tbl
··· 438 438 433 common fspick sys_fspick 439 439 434 common pidfd_open sys_pidfd_open 440 440 # 435 reserved for clone3 441 + 437 common openat2 sys_openat2
+1
arch/sparc/kernel/syscalls/syscall.tbl
··· 481 481 433 common fspick sys_fspick 482 482 434 common pidfd_open sys_pidfd_open 483 483 # 435 reserved for clone3 484 + 437 common openat2 sys_openat2
+1
arch/x86/entry/syscalls/syscall_32.tbl
··· 440 440 433 i386 fspick sys_fspick __ia32_sys_fspick 441 441 434 i386 pidfd_open sys_pidfd_open __ia32_sys_pidfd_open 442 442 435 i386 clone3 sys_clone3 __ia32_sys_clone3 443 + 437 i386 openat2 sys_openat2 __ia32_sys_openat2
+1
arch/x86/entry/syscalls/syscall_64.tbl
··· 357 357 433 common fspick __x64_sys_fspick 358 358 434 common pidfd_open __x64_sys_pidfd_open 359 359 435 common clone3 __x64_sys_clone3/ptregs 360 + 437 common openat2 __x64_sys_openat2 360 361 361 362 # 362 363 # x32-specific system call numbers start at 512 to avoid cache impact
+1
arch/xtensa/kernel/syscalls/syscall.tbl
··· 406 406 433 common fspick sys_fspick 407 407 434 common pidfd_open sys_pidfd_open 408 408 435 common clone3 sys_clone3 409 + 437 common openat2 sys_openat2
+117 -38
fs/open.c
··· 955 955 } 956 956 EXPORT_SYMBOL(open_with_fake_path); 957 957 958 - static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op) 958 + #define WILL_CREATE(flags) (flags & (O_CREAT | __O_TMPFILE)) 959 + #define O_PATH_FLAGS (O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC) 960 + 961 + static inline struct open_how build_open_how(int flags, umode_t mode) 959 962 { 963 + struct open_how how = { 964 + .flags = flags & VALID_OPEN_FLAGS, 965 + .mode = mode & S_IALLUGO, 966 + }; 967 + 968 + /* O_PATH beats everything else. */ 969 + if (how.flags & O_PATH) 970 + how.flags &= O_PATH_FLAGS; 971 + /* Modes should only be set for create-like flags. */ 972 + if (!WILL_CREATE(how.flags)) 973 + how.mode = 0; 974 + return how; 975 + } 976 + 977 + static inline int build_open_flags(const struct open_how *how, 978 + struct open_flags *op) 979 + { 980 + int flags = how->flags; 960 981 int lookup_flags = 0; 961 982 int acc_mode = ACC_MODE(flags); 962 983 963 - /* 964 - * Clear out all open flags we don't know about so that we don't report 965 - * them in fcntl(F_GETFD) or similar interfaces. 966 - */ 967 - flags &= VALID_OPEN_FLAGS; 968 - 969 - if (flags & (O_CREAT | __O_TMPFILE)) 970 - op->mode = (mode & S_IALLUGO) | S_IFREG; 971 - else 972 - op->mode = 0; 973 - 974 984 /* Must never be set by userspace */ 975 - flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC; 985 + flags &= ~(FMODE_NONOTIFY | O_CLOEXEC); 986 + 987 + /* 988 + * Older syscalls implicitly clear all of the invalid flags or argument 989 + * values before calling build_open_flags(), but openat2(2) checks all 990 + * of its arguments. 991 + */ 992 + if (flags & ~VALID_OPEN_FLAGS) 993 + return -EINVAL; 994 + if (how->resolve & ~VALID_RESOLVE_FLAGS) 995 + return -EINVAL; 996 + 997 + /* Deal with the mode. */ 998 + if (WILL_CREATE(flags)) { 999 + if (how->mode & ~S_IALLUGO) 1000 + return -EINVAL; 1001 + op->mode = how->mode | S_IFREG; 1002 + } else { 1003 + if (how->mode != 0) 1004 + return -EINVAL; 1005 + op->mode = 0; 1006 + } 1007 + 1008 + /* 1009 + * In order to ensure programs get explicit errors when trying to use 1010 + * O_TMPFILE on old kernels, O_TMPFILE is implemented such that it 1011 + * looks like (O_DIRECTORY|O_RDWR & ~O_CREAT) to old kernels. But we 1012 + * have to require userspace to explicitly set it. 1013 + */ 1014 + if (flags & __O_TMPFILE) { 1015 + if ((flags & O_TMPFILE_MASK) != O_TMPFILE) 1016 + return -EINVAL; 1017 + if (!(acc_mode & MAY_WRITE)) 1018 + return -EINVAL; 1019 + } 1020 + if (flags & O_PATH) { 1021 + /* O_PATH only permits certain other flags to be set. */ 1022 + if (flags & ~O_PATH_FLAGS) 1023 + return -EINVAL; 1024 + acc_mode = 0; 1025 + } 976 1026 977 1027 /* 978 1028 * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only ··· 1032 982 */ 1033 983 if (flags & __O_SYNC) 1034 984 flags |= O_DSYNC; 1035 - 1036 - if (flags & __O_TMPFILE) { 1037 - if ((flags & O_TMPFILE_MASK) != O_TMPFILE) 1038 - return -EINVAL; 1039 - if (!(acc_mode & MAY_WRITE)) 1040 - return -EINVAL; 1041 - } else if (flags & O_PATH) { 1042 - /* 1043 - * If we have O_PATH in the open flag. Then we 1044 - * cannot have anything other than the below set of flags 1045 - */ 1046 - flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH; 1047 - acc_mode = 0; 1048 - } 1049 985 1050 986 op->open_flag = flags; 1051 987 ··· 1058 1022 lookup_flags |= LOOKUP_DIRECTORY; 1059 1023 if (!(flags & O_NOFOLLOW)) 1060 1024 lookup_flags |= LOOKUP_FOLLOW; 1025 + 1026 + if (how->resolve & RESOLVE_NO_XDEV) 1027 + lookup_flags |= LOOKUP_NO_XDEV; 1028 + if (how->resolve & RESOLVE_NO_MAGICLINKS) 1029 + lookup_flags |= LOOKUP_NO_MAGICLINKS; 1030 + if (how->resolve & RESOLVE_NO_SYMLINKS) 1031 + lookup_flags |= LOOKUP_NO_SYMLINKS; 1032 + if (how->resolve & RESOLVE_BENEATH) 1033 + lookup_flags |= LOOKUP_BENEATH; 1034 + if (how->resolve & RESOLVE_IN_ROOT) 1035 + lookup_flags |= LOOKUP_IN_ROOT; 1036 + 1061 1037 op->lookup_flags = lookup_flags; 1062 1038 return 0; 1063 1039 } ··· 1088 1040 struct file *file_open_name(struct filename *name, int flags, umode_t mode) 1089 1041 { 1090 1042 struct open_flags op; 1091 - int err = build_open_flags(flags, mode, &op); 1092 - return err ? ERR_PTR(err) : do_filp_open(AT_FDCWD, name, &op); 1043 + struct open_how how = build_open_how(flags, mode); 1044 + int err = build_open_flags(&how, &op); 1045 + if (err) 1046 + return ERR_PTR(err); 1047 + return do_filp_open(AT_FDCWD, name, &op); 1093 1048 } 1094 1049 1095 1050 /** ··· 1123 1072 const char *filename, int flags, umode_t mode) 1124 1073 { 1125 1074 struct open_flags op; 1126 - int err = build_open_flags(flags, mode, &op); 1075 + struct open_how how = build_open_how(flags, mode); 1076 + int err = build_open_flags(&how, &op); 1127 1077 if (err) 1128 1078 return ERR_PTR(err); 1129 1079 return do_file_open_root(dentry, mnt, filename, &op); 1130 1080 } 1131 1081 EXPORT_SYMBOL(file_open_root); 1132 1082 1133 - long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) 1083 + static long do_sys_openat2(int dfd, const char __user *filename, 1084 + struct open_how *how) 1134 1085 { 1135 1086 struct open_flags op; 1136 - int fd = build_open_flags(flags, mode, &op); 1087 + int fd = build_open_flags(how, &op); 1137 1088 struct filename *tmp; 1138 1089 1139 1090 if (fd) ··· 1145 1092 if (IS_ERR(tmp)) 1146 1093 return PTR_ERR(tmp); 1147 1094 1148 - fd = get_unused_fd_flags(flags); 1095 + fd = get_unused_fd_flags(how->flags); 1149 1096 if (fd >= 0) { 1150 1097 struct file *f = do_filp_open(dfd, tmp, &op); 1151 1098 if (IS_ERR(f)) { ··· 1160 1107 return fd; 1161 1108 } 1162 1109 1110 + long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) 1111 + { 1112 + struct open_how how = build_open_how(flags, mode); 1113 + return do_sys_openat2(dfd, filename, &how); 1114 + } 1115 + 1116 + 1163 1117 SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) 1164 1118 { 1165 - if (force_o_largefile()) 1166 - flags |= O_LARGEFILE; 1167 - 1168 - return do_sys_open(AT_FDCWD, filename, flags, mode); 1119 + return ksys_open(filename, flags, mode); 1169 1120 } 1170 1121 1171 1122 SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, ··· 1177 1120 { 1178 1121 if (force_o_largefile()) 1179 1122 flags |= O_LARGEFILE; 1180 - 1181 1123 return do_sys_open(dfd, filename, flags, mode); 1124 + } 1125 + 1126 + SYSCALL_DEFINE4(openat2, int, dfd, const char __user *, filename, 1127 + struct open_how __user *, how, size_t, usize) 1128 + { 1129 + int err; 1130 + struct open_how tmp; 1131 + 1132 + BUILD_BUG_ON(sizeof(struct open_how) < OPEN_HOW_SIZE_VER0); 1133 + BUILD_BUG_ON(sizeof(struct open_how) != OPEN_HOW_SIZE_LATEST); 1134 + 1135 + if (unlikely(usize < OPEN_HOW_SIZE_VER0)) 1136 + return -EINVAL; 1137 + 1138 + err = copy_struct_from_user(&tmp, sizeof(tmp), how, usize); 1139 + if (err) 1140 + return err; 1141 + 1142 + /* O_LARGEFILE is only allowed for non-O_PATH. */ 1143 + if (!(tmp.flags & O_PATH) && force_o_largefile()) 1144 + tmp.flags |= O_LARGEFILE; 1145 + 1146 + return do_sys_openat2(dfd, filename, &tmp); 1182 1147 } 1183 1148 1184 1149 #ifdef CONFIG_COMPAT
+15 -1
include/linux/fcntl.h
··· 2 2 #ifndef _LINUX_FCNTL_H 3 3 #define _LINUX_FCNTL_H 4 4 5 + #include <linux/stat.h> 5 6 #include <uapi/linux/fcntl.h> 6 7 7 - /* list of all valid flags for the open/openat flags argument: */ 8 + /* List of all valid flags for the open/openat flags argument: */ 8 9 #define VALID_OPEN_FLAGS \ 9 10 (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \ 10 11 O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | __O_SYNC | O_DSYNC | \ 11 12 FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \ 12 13 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE) 14 + 15 + /* List of all valid flags for the how->upgrade_mask argument: */ 16 + #define VALID_UPGRADE_FLAGS \ 17 + (UPGRADE_NOWRITE | UPGRADE_NOREAD) 18 + 19 + /* List of all valid flags for the how->resolve argument: */ 20 + #define VALID_RESOLVE_FLAGS \ 21 + (RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \ 22 + RESOLVE_BENEATH | RESOLVE_IN_ROOT) 23 + 24 + /* List of all open_how "versions". */ 25 + #define OPEN_HOW_SIZE_VER0 24 /* sizeof first published struct */ 26 + #define OPEN_HOW_SIZE_LATEST OPEN_HOW_SIZE_VER0 13 27 14 28 #ifndef force_o_largefile 15 29 #define force_o_largefile() (!IS_ENABLED(CONFIG_ARCH_32BIT_OFF_T))
+3
include/linux/syscalls.h
··· 69 69 union bpf_attr; 70 70 struct io_uring_params; 71 71 struct clone_args; 72 + struct open_how; 72 73 73 74 #include <linux/types.h> 74 75 #include <linux/aio_abi.h> ··· 440 439 asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group); 441 440 asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, 442 441 umode_t mode); 442 + asmlinkage long sys_openat2(int dfd, const char __user *filename, 443 + struct open_how *how, size_t size); 443 444 asmlinkage long sys_close(unsigned int fd); 444 445 asmlinkage long sys_vhangup(void); 445 446
+4 -1
include/uapi/asm-generic/unistd.h
··· 851 851 __SYSCALL(__NR_clone3, sys_clone3) 852 852 #endif 853 853 854 + #define __NR_openat2 437 855 + __SYSCALL(__NR_openat2, sys_openat2) 856 + 854 857 #undef __NR_syscalls 855 - #define __NR_syscalls 436 858 + #define __NR_syscalls 438 856 859 857 860 /* 858 861 * 32 bit systems traditionally used different
+1 -1
include/uapi/linux/fcntl.h
··· 3 3 #define _UAPI_LINUX_FCNTL_H 4 4 5 5 #include <asm/fcntl.h> 6 + #include <linux/openat2.h> 6 7 7 8 #define F_SETLEASE (F_LINUX_SPECIFIC_BASE + 0) 8 9 #define F_GETLEASE (F_LINUX_SPECIFIC_BASE + 1) ··· 100 99 #define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */ 101 100 102 101 #define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */ 103 - 104 102 105 103 #endif /* _UAPI_LINUX_FCNTL_H */
+39
include/uapi/linux/openat2.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + #ifndef _UAPI_LINUX_OPENAT2_H 3 + #define _UAPI_LINUX_OPENAT2_H 4 + 5 + #include <linux/types.h> 6 + 7 + /* 8 + * Arguments for how openat2(2) should open the target path. If only @flags and 9 + * @mode are non-zero, then openat2(2) operates very similarly to openat(2). 10 + * 11 + * However, unlike openat(2), unknown or invalid bits in @flags result in 12 + * -EINVAL rather than being silently ignored. @mode must be zero unless one of 13 + * {O_CREAT, O_TMPFILE} are set. 14 + * 15 + * @flags: O_* flags. 16 + * @mode: O_CREAT/O_TMPFILE file mode. 17 + * @resolve: RESOLVE_* flags. 18 + */ 19 + struct open_how { 20 + __u64 flags; 21 + __u64 mode; 22 + __u64 resolve; 23 + }; 24 + 25 + /* how->resolve flags for openat2(2). */ 26 + #define RESOLVE_NO_XDEV 0x01 /* Block mount-point crossings 27 + (includes bind-mounts). */ 28 + #define RESOLVE_NO_MAGICLINKS 0x02 /* Block traversal through procfs-style 29 + "magic-links". */ 30 + #define RESOLVE_NO_SYMLINKS 0x04 /* Block traversal through all symlinks 31 + (implies OEXT_NO_MAGICLINKS) */ 32 + #define RESOLVE_BENEATH 0x08 /* Block "lexical" trickery like 33 + "..", symlinks, and absolute 34 + paths which escape the dirfd. */ 35 + #define RESOLVE_IN_ROOT 0x10 /* Make all jumps to "/" and ".." 36 + be scoped inside the dirfd 37 + (similar to chroot(2)). */ 38 + 39 + #endif /* _UAPI_LINUX_OPENAT2_H */