aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
authorJori Koolstra <jkoolstra@xs4all.nl>2026-04-24 13:46:02 +0200
committerChristian Brauner <brauner@kernel.org>2026-05-21 10:53:33 +0200
commit31cf44efa6df72a524b40adefb80539f3a4e13ba (patch)
tree7ff56857adc3f9756dce98b28a7179256aeac61d /fs
parent3e0be8ccff090cde4e10410f8e6a3fefeeff4124 (diff)
downloadlinux-next-history-31cf44efa6df72a524b40adefb80539f3a4e13ba.tar.gz
vfs: add O_EMPTYPATH to openat(2)/openat2(2)
To get an operable version of an O_PATH file descriptor, it is possible to use openat(fd, ".", O_DIRECTORY) for directories, but other files currently require going through open("/proc/<pid>/fd/<nr>"), which depends on a functioning procfs. This patch adds the O_EMPTYPATH flag to openat(2)/openat2(2). If passed, LOOKUP_EMPTY is set at path resolution time. Note: This implies that you cannot rely anymore on disabling procfs from being mounted (e.g. inside a container without procfs mounted and with CAP_SYS_ADMIN dropped) to prevent O_PATH fds from being re-opened read-write. Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl> Link: https://patch.msgid.link/20260424114611.1678641-2-jkoolstra@xs4all.nl Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/fcntl.c2
-rw-r--r--fs/open.c6
2 files changed, 5 insertions, 3 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c
index beab8080badf6..7d2165855a9c5 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -1169,7 +1169,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(20 - 1 /* for O_RDONLY being 0 */ !=
+ BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ !=
HWEIGHT32(
(VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) |
__FMODE_EXEC));
diff --git a/fs/open.c b/fs/open.c
index 681d405bc61eb..9e0164a8c1fbe 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1158,7 +1158,7 @@ struct file *kernel_file_open(const struct path *path, int flags,
EXPORT_SYMBOL_GPL(kernel_file_open);
#define WILL_CREATE(flags) (flags & (O_CREAT | __O_TMPFILE))
-#define O_PATH_FLAGS (O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC)
+#define O_PATH_FLAGS (O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC | O_EMPTYPATH)
inline struct open_how build_open_how(int flags, umode_t mode)
{
@@ -1279,6 +1279,8 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
lookup_flags |= LOOKUP_DIRECTORY;
if (!(flags & O_NOFOLLOW))
lookup_flags |= LOOKUP_FOLLOW;
+ if (flags & O_EMPTYPATH)
+ lookup_flags |= LOOKUP_EMPTY;
if (how->resolve & RESOLVE_NO_XDEV)
lookup_flags |= LOOKUP_NO_XDEV;
@@ -1360,7 +1362,7 @@ static int do_sys_openat2(int dfd, const char __user *filename,
if (unlikely(err))
return err;
- CLASS(filename, name)(filename);
+ CLASS(filename_flags, name)(filename, op.lookup_flags);
return FD_ADD(how->flags, do_file_open(dfd, name, &op));
}