mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
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>
This commit is contained in:
parent
a2062463e8
commit
3c1b73fc6a
4 changed files with 91 additions and 65 deletions
17
fs/init.c
17
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
137
fs/namespace.c
137
fs/namespace.c
|
|
@ -4498,6 +4498,77 @@ bool path_is_under(const struct path *path1, const struct path *path2)
|
|||
}
|
||||
EXPORT_SYMBOL(path_is_under);
|
||||
|
||||
int path_pivot_root(struct path *new, struct path *old)
|
||||
{
|
||||
struct path root __free(path_put) = {};
|
||||
struct mount *new_mnt, *root_mnt, *old_mnt, *root_parent, *ex_parent;
|
||||
int error;
|
||||
|
||||
if (!may_mount())
|
||||
return -EPERM;
|
||||
|
||||
error = security_sb_pivotroot(old, new);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
get_fs_root(current->fs, &root);
|
||||
|
||||
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);
|
||||
root_mnt = real_mount(root.mnt);
|
||||
ex_parent = new_mnt->mnt_parent;
|
||||
root_parent = root_mnt->mnt_parent;
|
||||
if (IS_MNT_SHARED(old_mnt) ||
|
||||
IS_MNT_SHARED(ex_parent) ||
|
||||
IS_MNT_SHARED(root_parent))
|
||||
return -EINVAL;
|
||||
if (!check_mnt(root_mnt) || !check_mnt(new_mnt))
|
||||
return -EINVAL;
|
||||
if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
|
||||
return -EINVAL;
|
||||
if (d_unlinked(new->dentry))
|
||||
return -ENOENT;
|
||||
if (new_mnt == root_mnt || old_mnt == root_mnt)
|
||||
return -EBUSY; /* loop, on the same file system */
|
||||
if (!path_mounted(&root))
|
||||
return -EINVAL; /* not a mountpoint */
|
||||
if (!mnt_has_parent(root_mnt))
|
||||
return -EINVAL; /* absolute root */
|
||||
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))
|
||||
return -EINVAL;
|
||||
/* make certain new is below the root */
|
||||
if (!is_path_reachable(new_mnt, new->dentry, &root))
|
||||
return -EINVAL;
|
||||
lock_mount_hash();
|
||||
umount_mnt(new_mnt);
|
||||
if (root_mnt->mnt.mnt_flags & MNT_LOCKED) {
|
||||
new_mnt->mnt.mnt_flags |= MNT_LOCKED;
|
||||
root_mnt->mnt.mnt_flags &= ~MNT_LOCKED;
|
||||
}
|
||||
/* mount new_root on / */
|
||||
attach_mnt(new_mnt, root_parent, root_mnt->mnt_mp);
|
||||
umount_mnt(root_mnt);
|
||||
/* mount old root on put_old */
|
||||
attach_mnt(root_mnt, old_mnt, old_mp.mp);
|
||||
touch_mnt_namespace(current->nsproxy->mnt_ns);
|
||||
/* A moved mount should not expire automatically */
|
||||
list_del_init(&new_mnt->mnt_expire);
|
||||
unlock_mount_hash();
|
||||
mnt_notify_add(root_mnt);
|
||||
mnt_notify_add(new_mnt);
|
||||
chroot_fs_refs(&root, new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* pivot_root Semantics:
|
||||
* Moves the root file system of the current process to the directory put_old,
|
||||
|
|
@ -4528,13 +4599,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
|
|||
{
|
||||
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;
|
||||
|
||||
if (!may_mount())
|
||||
return -EPERM;
|
||||
|
||||
error = user_path_at(AT_FDCWD, new_root,
|
||||
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new);
|
||||
if (error)
|
||||
|
|
@ -4545,66 +4611,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
error = security_sb_pivotroot(&old, &new);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
get_fs_root(current->fs, &root);
|
||||
|
||||
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);
|
||||
root_mnt = real_mount(root.mnt);
|
||||
ex_parent = new_mnt->mnt_parent;
|
||||
root_parent = root_mnt->mnt_parent;
|
||||
if (IS_MNT_SHARED(old_mnt) ||
|
||||
IS_MNT_SHARED(ex_parent) ||
|
||||
IS_MNT_SHARED(root_parent))
|
||||
return -EINVAL;
|
||||
if (!check_mnt(root_mnt) || !check_mnt(new_mnt))
|
||||
return -EINVAL;
|
||||
if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
|
||||
return -EINVAL;
|
||||
if (d_unlinked(new.dentry))
|
||||
return -ENOENT;
|
||||
if (new_mnt == root_mnt || old_mnt == root_mnt)
|
||||
return -EBUSY; /* loop, on the same file system */
|
||||
if (!path_mounted(&root))
|
||||
return -EINVAL; /* not a mountpoint */
|
||||
if (!mnt_has_parent(root_mnt))
|
||||
return -EINVAL; /* absolute root */
|
||||
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))
|
||||
return -EINVAL;
|
||||
/* make certain new is below the root */
|
||||
if (!is_path_reachable(new_mnt, new.dentry, &root))
|
||||
return -EINVAL;
|
||||
lock_mount_hash();
|
||||
umount_mnt(new_mnt);
|
||||
if (root_mnt->mnt.mnt_flags & MNT_LOCKED) {
|
||||
new_mnt->mnt.mnt_flags |= MNT_LOCKED;
|
||||
root_mnt->mnt.mnt_flags &= ~MNT_LOCKED;
|
||||
}
|
||||
/* mount new_root on / */
|
||||
attach_mnt(new_mnt, root_parent, root_mnt->mnt_mp);
|
||||
umount_mnt(root_mnt);
|
||||
/* mount old root on put_old */
|
||||
attach_mnt(root_mnt, old_mnt, old_mp.mp);
|
||||
touch_mnt_namespace(current->nsproxy->mnt_ns);
|
||||
/* A moved mount should not expire automatically */
|
||||
list_del_init(&new_mnt->mnt_expire);
|
||||
unlock_mount_hash();
|
||||
mnt_notify_add(root_mnt);
|
||||
mnt_notify_add(new_mnt);
|
||||
chroot_fs_refs(&root, &new);
|
||||
return 0;
|
||||
return path_pivot_root(&new, &old);
|
||||
}
|
||||
|
||||
static unsigned int recalc_flags(struct mount_kattr *kattr, struct mount *mnt)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue