This patch adds a new flag O_EMPTY_PATH that allows openat and open system calls to open a file referenced by fd if the path is empty, and it is very similar to the FreeBSD O_EMPTY_PATH flag. This can be beneficial in some cases since it would avoid having to grant /proc access to things like samba containers for reopening files to change flags in a race-free way. Signed-off-by: Ameer Hamza <ahamza@xxxxxxxxxxxxx> --- Change in v3: resolve O_EMPTY_PATH conflict with __FMODE_NONOTIFY for sparc. Change in v2: add nonconflicting values for O_EMPTY_PATH on architectures where default conflicts with existing flags. --- --- arch/alpha/include/uapi/asm/fcntl.h | 1 + arch/parisc/include/uapi/asm/fcntl.h | 1 + arch/sparc/include/uapi/asm/fcntl.h | 1 + fs/fcntl.c | 2 +- fs/namei.c | 4 ++-- fs/open.c | 2 +- include/linux/fcntl.h | 2 +- include/uapi/asm-generic/fcntl.h | 4 ++++ tools/include/uapi/asm-generic/fcntl.h | 4 ++++ 9 files changed, 16 insertions(+), 5 deletions(-) diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h index 50bdc8e8a271..ea08341bb9fe 100644 --- a/arch/alpha/include/uapi/asm/fcntl.h +++ b/arch/alpha/include/uapi/asm/fcntl.h @@ -34,6 +34,7 @@ #define O_PATH 040000000 #define __O_TMPFILE 0100000000 +#define O_EMPTY_PATH 0200000000 #define F_GETLK 7 #define F_SETLK 8 diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h index 03dee816cb13..e6144823ee5b 100644 --- a/arch/parisc/include/uapi/asm/fcntl.h +++ b/arch/parisc/include/uapi/asm/fcntl.h @@ -19,6 +19,7 @@ #define O_PATH 020000000 #define __O_TMPFILE 040000000 +#define O_EMPTY_PATH 0100000000 #define F_GETLK64 8 #define F_SETLK64 9 diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h index 67dae75e5274..ed99e4e4a717 100644 --- a/arch/sparc/include/uapi/asm/fcntl.h +++ b/arch/sparc/include/uapi/asm/fcntl.h @@ -37,6 +37,7 @@ #define O_PATH 0x1000000 #define __O_TMPFILE 0x2000000 +#define O_EMPTY_PATH 0x8000000 #define F_GETOWN 5 /* for sockets. */ #define F_SETOWN 6 /* for sockets. */ diff --git a/fs/fcntl.c b/fs/fcntl.c index 146c9ab0cd4b..7aac650e16e2 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -1027,7 +1027,7 @@ static int __init fcntl_init(void) * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY * is defined as O_NONBLOCK on some platforms and not on others. */ - BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ != + BUILD_BUG_ON(22 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( (VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) | __FMODE_EXEC | __FMODE_NONOTIFY)); diff --git a/fs/namei.c b/fs/namei.c index 309ae6fc8c99..2b2735af6d03 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -192,7 +192,7 @@ getname_flags(const char __user *filename, int flags, int *empty) if (unlikely(!len)) { if (empty) *empty = 1; - if (!(flags & LOOKUP_EMPTY)) { + if (!(flags & (LOOKUP_EMPTY | O_EMPTY_PATH))) { putname(result); return ERR_PTR(-ENOENT); } @@ -2347,7 +2347,7 @@ static const char *path_init(struct nameidata *nd, unsigned flags) if ((flags & (LOOKUP_RCU | LOOKUP_CACHED)) == LOOKUP_CACHED) return ERR_PTR(-EAGAIN); - if (!*s) + if (!*s && unlikely(!(flags & O_EMPTY_PATH))) flags &= ~LOOKUP_RCU; if (flags & LOOKUP_RCU) rcu_read_lock(); diff --git a/fs/open.c b/fs/open.c index 82c1a28b3308..b4ec054a418f 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1301,7 +1301,7 @@ static long do_sys_openat2(int dfd, const char __user *filename, if (fd) return fd; - tmp = getname(filename); + tmp = getname_flags(filename, how->flags & O_EMPTY_PATH, NULL); if (IS_ERR(tmp)) return PTR_ERR(tmp); diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index a332e79b3207..bf8467bb0bd2 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -10,7 +10,7 @@ (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \ O_APPEND | O_NDELAY | O_NONBLOCK | __O_SYNC | O_DSYNC | \ FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \ - O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE) + O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTY_PATH) /* List of all valid flags for the how->resolve argument: */ #define VALID_RESOLVE_FLAGS \ diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 1ecdb911add8..a03f4275517b 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -89,6 +89,10 @@ #define __O_TMPFILE 020000000 #endif +#ifndef O_EMPTY_PATH +#define O_EMPTY_PATH 040000000 +#endif + /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) diff --git a/tools/include/uapi/asm-generic/fcntl.h b/tools/include/uapi/asm-generic/fcntl.h index b02c8e0f4057..f32a81604296 100644 --- a/tools/include/uapi/asm-generic/fcntl.h +++ b/tools/include/uapi/asm-generic/fcntl.h @@ -89,6 +89,10 @@ #define __O_TMPFILE 020000000 #endif +#ifndef O_EMPTY_PATH +#define O_EMPTY_PATH 040000000 +#endif + /* a horrid kludge trying to make sure that this will fail on old kernels */ #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #define O_TMPFILE_MASK (__O_TMPFILE | O_DIRECTORY | O_CREAT) -- 2.25.1