From: Christian Brauner Date: Fri, 24 Jul 2026 13:41:19 +0000 (+0200) Subject: fs: add fchroot() X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=20370a5f5d9b1549ab3bf10a898b4e024b8841fa;p=thirdparty%2Flinux.git fs: add fchroot() 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). The 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. Link: https://patch.msgid.link/20260724-work-failfs-v2-3-485dabbae185@kernel.org Signed-off-by: Christian Brauner (Amutable) --- 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 @@ dput_and_out: 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);