]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fs: add init_pivot_root()
authorChristian Brauner <brauner@kernel.org>
Mon, 12 Jan 2026 15:47:09 +0000 (16:47 +0100)
committerChristian Brauner <brauner@kernel.org>
Mon, 12 Jan 2026 15:52:09 +0000 (16:52 +0100)
We will soon be able to pivot_root() with the introduction of the
immutable rootfs. Add a wrapper for kernel internal usage.

Link: https://patch.msgid.link/20260112-work-immutable-rootfs-v2-2-88dd1c34a204@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/init.c
fs/internal.h
fs/namespace.c
include/linux/init_syscalls.h

index e0f5429c0a49d046bd3f231a260954ed0f90ef44..e33b2690d851bbd5d51941b875422a1daf99235b 100644 (file)
--- a/fs/init.c
+++ b/fs/init.c
 #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)
 {
index ab638d41ab81db8de33a895defd6e3538a170223..4b27a4b0fdefa244152f1d348b608aa22c45937b 100644 (file)
@@ -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);
 
index 8b082b1de7f3ec6b1b0f787cffed59261fef75f9..9261f56ccc81927e32942ab7e5e2a26a4320ecaf 100644 (file)
@@ -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;
index 92045d18cbfc994a9524733cd1f5febdd3019f92..28776ee28d8e8fa1dc84798c4b54e7cb25e00655 100644 (file)
@@ -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);