Skip to content

Commit 3c1b73f

Browse files
committed
fs: add init_pivot_root()
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>
1 parent a206246 commit 3c1b73f

File tree

4 files changed

+73
-47
lines changed

4 files changed

+73
-47
lines changed

fs/init.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,23 @@
1313
#include <linux/security.h>
1414
#include "internal.h"
1515

16+
int __init init_pivot_root(const char *new_root, const char *put_old)
17+
{
18+
struct path new_path __free(path_put) = {};
19+
struct path old_path __free(path_put) = {};
20+
int ret;
21+
22+
ret = kern_path(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new_path);
23+
if (ret)
24+
return ret;
25+
26+
ret = kern_path(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_path);
27+
if (ret)
28+
return ret;
29+
30+
return path_pivot_root(&new_path, &old_path);
31+
}
32+
1633
int __init init_mount(const char *dev_name, const char *dir_name,
1734
const char *type_page, unsigned long flags, void *data_page)
1835
{

fs/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ extern bool may_mount(void);
9090
int path_mount(const char *dev_name, const struct path *path,
9191
const char *type_page, unsigned long flags, void *data_page);
9292
int path_umount(const struct path *path, int flags);
93+
int path_pivot_root(struct path *new, struct path *old);
9394

9495
int show_path(struct seq_file *m, struct dentry *root);
9596

fs/namespace.c

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4498,65 +4498,27 @@ bool path_is_under(const struct path *path1, const struct path *path2)
44984498
}
44994499
EXPORT_SYMBOL(path_is_under);
45004500

4501-
/*
4502-
* pivot_root Semantics:
4503-
* Moves the root file system of the current process to the directory put_old,
4504-
* makes new_root as the new root file system of the current process, and sets
4505-
* root/cwd of all processes which had them on the current root to new_root.
4506-
*
4507-
* Restrictions:
4508-
* The new_root and put_old must be directories, and must not be on the
4509-
* same file system as the current process root. The put_old must be
4510-
* underneath new_root, i.e. adding a non-zero number of /.. to the string
4511-
* pointed to by put_old must yield the same directory as new_root. No other
4512-
* file system may be mounted on put_old. After all, new_root is a mountpoint.
4513-
*
4514-
* Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
4515-
* See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
4516-
* in this situation.
4517-
*
4518-
* Notes:
4519-
* - we don't move root/cwd if they are not at the root (reason: if something
4520-
* cared enough to change them, it's probably wrong to force them elsewhere)
4521-
* - it's okay to pick a root that isn't the root of a file system, e.g.
4522-
* /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
4523-
* though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
4524-
* first.
4525-
*/
4526-
SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
4527-
const char __user *, put_old)
4501+
int path_pivot_root(struct path *new, struct path *old)
45284502
{
4529-
struct path new __free(path_put) = {};
4530-
struct path old __free(path_put) = {};
45314503
struct path root __free(path_put) = {};
45324504
struct mount *new_mnt, *root_mnt, *old_mnt, *root_parent, *ex_parent;
45334505
int error;
45344506

45354507
if (!may_mount())
45364508
return -EPERM;
45374509

4538-
error = user_path_at(AT_FDCWD, new_root,
4539-
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new);
4540-
if (error)
4541-
return error;
4542-
4543-
error = user_path_at(AT_FDCWD, put_old,
4544-
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old);
4545-
if (error)
4546-
return error;
4547-
4548-
error = security_sb_pivotroot(&old, &new);
4510+
error = security_sb_pivotroot(old, new);
45494511
if (error)
45504512
return error;
45514513

45524514
get_fs_root(current->fs, &root);
45534515

4554-
LOCK_MOUNT(old_mp, &old);
4516+
LOCK_MOUNT(old_mp, old);
45554517
old_mnt = old_mp.parent;
45564518
if (IS_ERR(old_mnt))
45574519
return PTR_ERR(old_mnt);
45584520

4559-
new_mnt = real_mount(new.mnt);
4521+
new_mnt = real_mount(new->mnt);
45604522
root_mnt = real_mount(root.mnt);
45614523
ex_parent = new_mnt->mnt_parent;
45624524
root_parent = root_mnt->mnt_parent;
@@ -4568,23 +4530,23 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
45684530
return -EINVAL;
45694531
if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
45704532
return -EINVAL;
4571-
if (d_unlinked(new.dentry))
4533+
if (d_unlinked(new->dentry))
45724534
return -ENOENT;
45734535
if (new_mnt == root_mnt || old_mnt == root_mnt)
45744536
return -EBUSY; /* loop, on the same file system */
45754537
if (!path_mounted(&root))
45764538
return -EINVAL; /* not a mountpoint */
45774539
if (!mnt_has_parent(root_mnt))
45784540
return -EINVAL; /* absolute root */
4579-
if (!path_mounted(&new))
4541+
if (!path_mounted(new))
45804542
return -EINVAL; /* not a mountpoint */
45814543
if (!mnt_has_parent(new_mnt))
45824544
return -EINVAL; /* absolute root */
45834545
/* make sure we can reach put_old from new_root */
4584-
if (!is_path_reachable(old_mnt, old_mp.mp->m_dentry, &new))
4546+
if (!is_path_reachable(old_mnt, old_mp.mp->m_dentry, new))
45854547
return -EINVAL;
45864548
/* make certain new is below the root */
4587-
if (!is_path_reachable(new_mnt, new.dentry, &root))
4549+
if (!is_path_reachable(new_mnt, new->dentry, &root))
45884550
return -EINVAL;
45894551
lock_mount_hash();
45904552
umount_mnt(new_mnt);
@@ -4603,10 +4565,55 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
46034565
unlock_mount_hash();
46044566
mnt_notify_add(root_mnt);
46054567
mnt_notify_add(new_mnt);
4606-
chroot_fs_refs(&root, &new);
4568+
chroot_fs_refs(&root, new);
46074569
return 0;
46084570
}
46094571

4572+
/*
4573+
* pivot_root Semantics:
4574+
* Moves the root file system of the current process to the directory put_old,
4575+
* makes new_root as the new root file system of the current process, and sets
4576+
* root/cwd of all processes which had them on the current root to new_root.
4577+
*
4578+
* Restrictions:
4579+
* The new_root and put_old must be directories, and must not be on the
4580+
* same file system as the current process root. The put_old must be
4581+
* underneath new_root, i.e. adding a non-zero number of /.. to the string
4582+
* pointed to by put_old must yield the same directory as new_root. No other
4583+
* file system may be mounted on put_old. After all, new_root is a mountpoint.
4584+
*
4585+
* Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
4586+
* See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
4587+
* in this situation.
4588+
*
4589+
* Notes:
4590+
* - we don't move root/cwd if they are not at the root (reason: if something
4591+
* cared enough to change them, it's probably wrong to force them elsewhere)
4592+
* - it's okay to pick a root that isn't the root of a file system, e.g.
4593+
* /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
4594+
* though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
4595+
* first.
4596+
*/
4597+
SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
4598+
const char __user *, put_old)
4599+
{
4600+
struct path new __free(path_put) = {};
4601+
struct path old __free(path_put) = {};
4602+
int error;
4603+
4604+
error = user_path_at(AT_FDCWD, new_root,
4605+
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new);
4606+
if (error)
4607+
return error;
4608+
4609+
error = user_path_at(AT_FDCWD, put_old,
4610+
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old);
4611+
if (error)
4612+
return error;
4613+
4614+
return path_pivot_root(&new, &old);
4615+
}
4616+
46104617
static unsigned int recalc_flags(struct mount_kattr *kattr, struct mount *mnt)
46114618
{
46124619
unsigned int flags = mnt->mnt.mnt_flags;

include/linux/init_syscalls.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ int __init init_mkdir(const char *pathname, umode_t mode);
1717
int __init init_rmdir(const char *pathname);
1818
int __init init_utimes(char *filename, struct timespec64 *ts);
1919
int __init init_dup(struct file *file);
20+
int __init init_pivot_root(const char *new_root, const char *put_old);

0 commit comments

Comments
 (0)