diff options
author | Andy Lutomirski <luto@amacapital.net> | 2014-10-08 18:08:44 -0700 |
---|---|---|
committer | Andy Lutomirski <luto@amacapital.net> | 2014-10-08 22:07:06 -0700 |
commit | 4d7ba5c1f685b2bd6cbf93453ee81511277b115a (patch) | |
tree | 01cb6af6dca6881e25af406568a8155593289919 | |
parent | a1480dcc3c706e309a88884723446f2e84fedd5b (diff) | |
download | devel-fs/umount.tar.gz |
[wip] mntns_change_rootfs/umount
-rw-r--r-- | fs/namespace.c | 107 |
1 files changed, 102 insertions, 5 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 7f67b463a5b4d..15d2ab1967ef5 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2855,19 +2855,116 @@ out0: return error; } -static void __init init_mount_tree(void) +static struct vfsmount *create_rootfs(int flags) { - struct vfsmount *mnt; - struct mnt_namespace *ns; - struct path root; struct file_system_type *type; + struct vfsmount *mnt; type = get_fs_type("rootfs"); if (!type) + return NULL; panic("Can't find rootfs type"); - mnt = vfs_kern_mount(type, 0, "rootfs", NULL); + mnt = vfs_kern_mount(type, flags, "rootfs", NULL); put_filesystem(type); if (IS_ERR(mnt)) + return NULL; + + return mnt; +} + +#define MNTNS_CHANGE_ROOT_CHDIR 1 +#define MNTNS_CHANGE_ROOT_CHROOT 2 + +/* + * Rough notes: + * + * - Constant time. + * - Flexible chroot/chdir. + * - Independent of prior rootfs state (and detaches rootfs). + * - Easier to use than pivot_root (no worrying about put_old). + */ +SYSCALL_DEFINE3(mntns_change_root, + int, dfd, const char __user *, name, + int, flags) +{ + struct path path, parent_path; + struct mount *mnt; + struct vfsmount *newnsroot; + struct mnt_namespace *ns = current->nsproxy->mnt_ns; + int retval; + + if (flags & ~(MNTNS_CHANGE_ROOT_CHDIR | MNTNS_CHANGE_ROOT_CHROOT)) + return -EINVAL; + + if (!may_mount()) + return -EPERM; + + if (current_chrooted()) + return -EPERM; + + if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + + retval = user_path_mountpoint_at(dfd, name, 0, &path); + if (retval) + return retval; + mnt = real_mount(path.mnt); + + retval = security_path_chroot(&path); + if (retval) + goto put_and_out; + + newnsroot = create_rootfs(MS_RDONLY); + if (!newnsroot) + return -ENOMEM; /* This is the only reasonable cause. */ + + namespace_lock(); + + retval = -EINVAL; + if (!check_mnt(mnt)) + goto unlock_and_out; + if (!mnt_has_parent(mnt)) + goto unlock_and_out; + if (path.dentry != path.mnt->mnt_root) + goto unlock_and_out; + if (path.mnt->mnt_flags & MNT_LOCKED) + goto unlock_and_out; + + lock_mount_hash(); + detach_mnt(mnt, &parent_path); + path_put(&parent_path); + + mntput(&ns->root->mnt); + mnt->mnt_ns = ns; + ns->root = mnt; + list_del(&mnt->mnt_list); + list_add(&mnt->mnt_list, &ns->list); + + if (flags & MNTNS_CHANGE_ROOT_CHROOT) + set_fs_root(current->fs, &path); + if (flags & MNTNS_CHANGE_ROOT_CHDIR) + set_fs_pwd(current->fs, &path); + + retval = 0; + +unlock_and_out: + namespace_unlock(); + mntput(newnsroot); + +put_and_out: + path_put(&path); + + return retval; +} + +static void __init init_mount_tree(void) +{ + struct vfsmount *mnt; + struct mnt_namespace *ns; + struct path root; + + mnt = create_rootfs(0); + if (!mnt) panic("Can't create rootfs"); ns = create_mnt_ns(mnt); |