Thread (23 messages) 23 messages, 6 authors, 3d ago
WARM3d
Revisions (2)
  1. rfc current
  2. v2 [diff vs current]

[PATCH RFC 3/7] fs: add fchroot()

From: Christian Brauner <brauner@kernel.org>
Date: 2026-07-23 11:30:46
Also in: linux-doc, linux-fsdevel, lkml
Subsystem: abi/api, filesystems (vfs and infrastructure), the rest · Maintainers: Alexander Viro, Christian Brauner, Linus Torvalds

Add a file descriptor based counterpart to chroot(2). This has been
overdue for a long time. It is the natural companion to fchdir() and
avoids re-resolving a path that the caller already holds a file
descriptor to. No TOCTOU between resolving the target and changing the
root. It composes with modern fd-based APIs meaning it works with O_PATH
file descriptors and file descriptors to detached mount trees created
via open_tree(OPEN_TREE_CLONE).

The permission model is identical to chroot(2). Rhe caller must have
CAP_SYS_CHROOT in its user namespace, must pass MAY_EXEC | MAY_CHDIR
permission checks on the target directory, and LSMs are consulted via
the same security_path_chroot() hook.

The system call takes a flags argument for future extensibility which
must currently be zero.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/open.c                | 29 +++++++++++++++++++++++++++++
 include/linux/syscalls.h |  1 +
 2 files changed, 30 insertions(+)
diff --git a/fs/open.c b/fs/open.c
index 56b6032d4d81..c57f641f2e29 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -618,6 +618,35 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
 	return error;
 }
 
+SYSCALL_DEFINE2(fchroot, int, fd, unsigned int, flags)
+{
+	int error;
+
+	if (flags)
+		return -EINVAL;
+
+	CLASS(fd_raw, f)(fd);
+	if (fd_empty(f))
+		return -EBADF;
+
+	if (!d_can_lookup(fd_file(f)->f_path.dentry))
+		return -ENOTDIR;
+
+	error = file_permission(fd_file(f), MAY_EXEC | MAY_CHDIR);
+	if (error)
+		return error;
+
+	if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT))
+		return -EPERM;
+
+	error = security_path_chroot(&fd_file(f)->f_path);
+	if (error)
+		return error;
+
+	set_fs_root(current->fs, &fd_file(f)->f_path);
+	return 0;
+}
+
 int chmod_common(const struct path *path, umode_t mode)
 {
 	struct inode *inode = path->dentry->d_inode;
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 874d9067a43b..8413b624ad47 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -457,6 +457,7 @@ asmlinkage long sys_faccessat2(int dfd, const char __user *filename, int mode,
 asmlinkage long sys_chdir(const char __user *filename);
 asmlinkage long sys_fchdir(unsigned int fd);
 asmlinkage long sys_chroot(const char __user *filename);
+asmlinkage long sys_fchroot(int fd, unsigned int flags);
 asmlinkage long sys_fchmod(unsigned int fd, umode_t mode);
 asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
 			     umode_t mode);
-- 
2.53.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help