From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org
Cc: "Alexander Viro" <viro@zeniv.linux.org.uk>,
"Jan Kara" <jack@suse.cz>, "Jeff Layton" <jlayton@kernel.org>,
"Amir Goldstein" <amir73il@gmail.com>,
"Lennart Poettering" <lennart@poettering.net>,
"Zbigniew Jędrzejewski-Szmek" <zbyszek@in.waw.pl>,
"Josef Bacik" <josef@toxicpanda.com>,
"Christian Brauner" <brauner@kernel.org>
Subject: [PATCH v2 2/4] fs: add init_pivot_root()
Date: Mon, 12 Jan 2026 16:47:09 +0100 [thread overview]
Message-ID: <20260112-work-immutable-rootfs-v2-2-88dd1c34a204@kernel.org> (raw)
In-Reply-To: <20260112-work-immutable-rootfs-v2-0-88dd1c34a204@kernel.org>
We will soon be able to pivot_root() with the introduction of the
immutable rootfs. Add a wrapper for kernel internal usage.
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
fs/init.c | 17 +++++++
fs/internal.h | 1 +
fs/namespace.c | 101 ++++++++++++++++++++++--------------------
include/linux/init_syscalls.h | 1 +
4 files changed, 73 insertions(+), 47 deletions(-)
diff --git a/fs/init.c b/fs/init.c
index e0f5429c0a49..e33b2690d851 100644
--- a/fs/init.c
+++ b/fs/init.c
@@ -13,6 +13,23 @@
#include <linux/security.h>
#include "internal.h"
+int __init init_pivot_root(const char *new_root, const char *put_old)
+{
+ struct path new_path __free(path_put) = {};
+ struct path old_path __free(path_put) = {};
+ int ret;
+
+ ret = kern_path(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new_path);
+ if (ret)
+ return ret;
+
+ ret = kern_path(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_path);
+ if (ret)
+ return ret;
+
+ return path_pivot_root(&new_path, &old_path);
+}
+
int __init init_mount(const char *dev_name, const char *dir_name,
const char *type_page, unsigned long flags, void *data_page)
{
diff --git a/fs/internal.h b/fs/internal.h
index ab638d41ab81..4b27a4b0fdef 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -90,6 +90,7 @@ extern bool may_mount(void);
int path_mount(const char *dev_name, const struct path *path,
const char *type_page, unsigned long flags, void *data_page);
int path_umount(const struct path *path, int flags);
+int path_pivot_root(struct path *new, struct path *old);
int show_path(struct seq_file *m, struct dentry *root);
diff --git a/fs/namespace.c b/fs/namespace.c
index 8b082b1de7f3..9261f56ccc81 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4498,36 +4498,8 @@ bool path_is_under(const struct path *path1, const struct path *path2)
}
EXPORT_SYMBOL(path_is_under);
-/*
- * pivot_root Semantics:
- * Moves the root file system of the current process to the directory put_old,
- * makes new_root as the new root file system of the current process, and sets
- * root/cwd of all processes which had them on the current root to new_root.
- *
- * Restrictions:
- * The new_root and put_old must be directories, and must not be on the
- * same file system as the current process root. The put_old must be
- * underneath new_root, i.e. adding a non-zero number of /.. to the string
- * pointed to by put_old must yield the same directory as new_root. No other
- * file system may be mounted on put_old. After all, new_root is a mountpoint.
- *
- * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
- * See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
- * in this situation.
- *
- * Notes:
- * - we don't move root/cwd if they are not at the root (reason: if something
- * cared enough to change them, it's probably wrong to force them elsewhere)
- * - it's okay to pick a root that isn't the root of a file system, e.g.
- * /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
- * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
- * first.
- */
-SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
- const char __user *, put_old)
+int path_pivot_root(struct path *new, struct path *old)
{
- struct path new __free(path_put) = {};
- struct path old __free(path_put) = {};
struct path root __free(path_put) = {};
struct mount *new_mnt, *root_mnt, *old_mnt, *root_parent, *ex_parent;
int error;
@@ -4535,28 +4507,18 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
if (!may_mount())
return -EPERM;
- error = user_path_at(AT_FDCWD, new_root,
- LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new);
- if (error)
- return error;
-
- error = user_path_at(AT_FDCWD, put_old,
- LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old);
- if (error)
- return error;
-
- error = security_sb_pivotroot(&old, &new);
+ error = security_sb_pivotroot(old, new);
if (error)
return error;
get_fs_root(current->fs, &root);
- LOCK_MOUNT(old_mp, &old);
+ LOCK_MOUNT(old_mp, old);
old_mnt = old_mp.parent;
if (IS_ERR(old_mnt))
return PTR_ERR(old_mnt);
- new_mnt = real_mount(new.mnt);
+ new_mnt = real_mount(new->mnt);
root_mnt = real_mount(root.mnt);
ex_parent = new_mnt->mnt_parent;
root_parent = root_mnt->mnt_parent;
@@ -4568,7 +4530,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
return -EINVAL;
if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
return -EINVAL;
- if (d_unlinked(new.dentry))
+ if (d_unlinked(new->dentry))
return -ENOENT;
if (new_mnt == root_mnt || old_mnt == root_mnt)
return -EBUSY; /* loop, on the same file system */
@@ -4576,15 +4538,15 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
return -EINVAL; /* not a mountpoint */
if (!mnt_has_parent(root_mnt))
return -EINVAL; /* absolute root */
- if (!path_mounted(&new))
+ if (!path_mounted(new))
return -EINVAL; /* not a mountpoint */
if (!mnt_has_parent(new_mnt))
return -EINVAL; /* absolute root */
/* make sure we can reach put_old from new_root */
- if (!is_path_reachable(old_mnt, old_mp.mp->m_dentry, &new))
+ if (!is_path_reachable(old_mnt, old_mp.mp->m_dentry, new))
return -EINVAL;
/* make certain new is below the root */
- if (!is_path_reachable(new_mnt, new.dentry, &root))
+ if (!is_path_reachable(new_mnt, new->dentry, &root))
return -EINVAL;
lock_mount_hash();
umount_mnt(new_mnt);
@@ -4603,10 +4565,55 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
unlock_mount_hash();
mnt_notify_add(root_mnt);
mnt_notify_add(new_mnt);
- chroot_fs_refs(&root, &new);
+ chroot_fs_refs(&root, new);
return 0;
}
+/*
+ * pivot_root Semantics:
+ * Moves the root file system of the current process to the directory put_old,
+ * makes new_root as the new root file system of the current process, and sets
+ * root/cwd of all processes which had them on the current root to new_root.
+ *
+ * Restrictions:
+ * The new_root and put_old must be directories, and must not be on the
+ * same file system as the current process root. The put_old must be
+ * underneath new_root, i.e. adding a non-zero number of /.. to the string
+ * pointed to by put_old must yield the same directory as new_root. No other
+ * file system may be mounted on put_old. After all, new_root is a mountpoint.
+ *
+ * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
+ * See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
+ * in this situation.
+ *
+ * Notes:
+ * - we don't move root/cwd if they are not at the root (reason: if something
+ * cared enough to change them, it's probably wrong to force them elsewhere)
+ * - it's okay to pick a root that isn't the root of a file system, e.g.
+ * /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
+ * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
+ * first.
+ */
+SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
+ const char __user *, put_old)
+{
+ struct path new __free(path_put) = {};
+ struct path old __free(path_put) = {};
+ int error;
+
+ error = user_path_at(AT_FDCWD, new_root,
+ LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new);
+ if (error)
+ return error;
+
+ error = user_path_at(AT_FDCWD, put_old,
+ LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old);
+ if (error)
+ return error;
+
+ return path_pivot_root(&new, &old);
+}
+
static unsigned int recalc_flags(struct mount_kattr *kattr, struct mount *mnt)
{
unsigned int flags = mnt->mnt.mnt_flags;
diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h
index 92045d18cbfc..28776ee28d8e 100644
--- a/include/linux/init_syscalls.h
+++ b/include/linux/init_syscalls.h
@@ -17,3 +17,4 @@ int __init init_mkdir(const char *pathname, umode_t mode);
int __init init_rmdir(const char *pathname);
int __init init_utimes(char *filename, struct timespec64 *ts);
int __init init_dup(struct file *file);
+int __init init_pivot_root(const char *new_root, const char *put_old);
--
2.47.3
next prev parent reply other threads:[~2026-01-12 15:47 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
2026-01-12 15:47 ` [PATCH v2 1/4] fs: ensure that internal tmpfs mount gets mount id zero Christian Brauner
2026-01-12 19:37 ` Jeff Layton
2026-01-12 15:47 ` Christian Brauner [this message]
2026-01-12 15:47 ` [PATCH v2 3/4] fs: add immutable rootfs Christian Brauner
2026-01-12 15:47 ` [PATCH v2 4/4] docs: mention nullfs Christian Brauner
2026-01-13 14:17 ` [PATCH v2 0/4] fs: add immutable rootfs Jeff Layton
2026-01-14 8:58 ` Christian Brauner
2026-01-14 10:32 ` [RFC PATCH 5/4] fs: use nullfs unconditionally as the real rootfs Christian Brauner
2026-01-14 12:05 ` Jeff Layton
2026-01-14 11:58 ` [PATCH v2 0/4] fs: add immutable rootfs Jeff Layton
2026-01-25 19:18 ` Askar Safin
2026-02-01 19:55 ` Askar Safin
2026-02-04 13:00 ` Christian Brauner
2026-02-04 14:48 ` Askar Safin
2026-02-02 14:27 ` Askar Safin
2026-02-02 16:22 ` Askar Safin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260112-work-immutable-rootfs-v2-2-88dd1c34a204@kernel.org \
--to=brauner@kernel.org \
--cc=amir73il@gmail.com \
--cc=jack@suse.cz \
--cc=jlayton@kernel.org \
--cc=josef@toxicpanda.com \
--cc=lennart@poettering.net \
--cc=linux-fsdevel@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
--cc=zbyszek@in.waw.pl \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox