Merge branch 'work.mount' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs mount infrastructure updates from Al Viro: "The rest of core infrastructure; no new syscalls in that pile, but the old parts are switched to new infrastructure. At that point conversions of individual filesystems can happen independently; some are done here (afs, cgroup, procfs, etc.), there's also a large series outside of that pile dealing with NFS (quite a bit of option-parsing stuff is getting used there - it's one of the most convoluted filesystems in terms of mount-related logics), but NFS bits are the next cycle fodder. It got seriously simplified since the last cycle; documentation is probably the weakest bit at the moment - I considered dropping the commit introducing Documentation/filesystems/mount_api.txt (cutting the size increase by quarter ;-), but decided that it would be better to fix it up after -rc1 instead. That pile allows to do followup work in independent branches, which should make life much easier for the next cycle. fs/super.c size increase is unpleasant; there's a followup series that allows to shrink it considerably, but I decided to leave that until the next cycle" * 'work.mount' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (41 commits) afs: Use fs_context to pass parameters over automount afs: Add fs_context support vfs: Add some logging to the core users of the fs_context log vfs: Implement logging through fs_context vfs: Provide documentation for new mount API vfs: Remove kern_mount_data() hugetlbfs: Convert to fs_context cpuset: Use fs_context kernfs, sysfs, cgroup, intel_rdt: Support fs_context cgroup: store a reference to cgroup_ns into cgroup_fs_context cgroup1_get_tree(): separate "get cgroup_root to use" into a separate helper cgroup_do_mount(): massage calling conventions cgroup: stash cgroup_root reference into cgroup_fs_context cgroup2: switch to option-by-option parsing cgroup1: switch to option-by-option parsing cgroup: take options parsing into ->parse_monolithic() cgroup: fold cgroup1_mount() into cgroup1_get_tree() cgroup: start switching to fs_context ipc: Convert mqueue fs to fs_context proc: Add fs_context support to procfs ...
This commit is contained in:
397
fs/namespace.c
397
fs/namespace.c
@@ -27,6 +27,7 @@
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
#include <linux/fs_context.h>
|
||||
|
||||
#include "pnode.h"
|
||||
#include "internal.h"
|
||||
@@ -940,38 +941,81 @@ static struct mount *skip_mnt_tree(struct mount *p)
|
||||
return p;
|
||||
}
|
||||
|
||||
struct vfsmount *
|
||||
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
|
||||
/**
|
||||
* vfs_create_mount - Create a mount for a configured superblock
|
||||
* @fc: The configuration context with the superblock attached
|
||||
*
|
||||
* Create a mount to an already configured superblock. If necessary, the
|
||||
* caller should invoke vfs_get_tree() before calling this.
|
||||
*
|
||||
* Note that this does not attach the mount to anything.
|
||||
*/
|
||||
struct vfsmount *vfs_create_mount(struct fs_context *fc)
|
||||
{
|
||||
struct mount *mnt;
|
||||
struct dentry *root;
|
||||
|
||||
if (!type)
|
||||
return ERR_PTR(-ENODEV);
|
||||
if (!fc->root)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mnt = alloc_vfsmnt(name);
|
||||
mnt = alloc_vfsmnt(fc->source ?: "none");
|
||||
if (!mnt)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (flags & SB_KERNMOUNT)
|
||||
if (fc->sb_flags & SB_KERNMOUNT)
|
||||
mnt->mnt.mnt_flags = MNT_INTERNAL;
|
||||
|
||||
root = mount_fs(type, flags, name, data);
|
||||
if (IS_ERR(root)) {
|
||||
mnt_free_id(mnt);
|
||||
free_vfsmnt(mnt);
|
||||
return ERR_CAST(root);
|
||||
}
|
||||
atomic_inc(&fc->root->d_sb->s_active);
|
||||
mnt->mnt.mnt_sb = fc->root->d_sb;
|
||||
mnt->mnt.mnt_root = dget(fc->root);
|
||||
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
||||
mnt->mnt_parent = mnt;
|
||||
|
||||
mnt->mnt.mnt_root = root;
|
||||
mnt->mnt.mnt_sb = root->d_sb;
|
||||
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
||||
mnt->mnt_parent = mnt;
|
||||
lock_mount_hash();
|
||||
list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
|
||||
list_add_tail(&mnt->mnt_instance, &mnt->mnt.mnt_sb->s_mounts);
|
||||
unlock_mount_hash();
|
||||
return &mnt->mnt;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_create_mount);
|
||||
|
||||
struct vfsmount *fc_mount(struct fs_context *fc)
|
||||
{
|
||||
int err = vfs_get_tree(fc);
|
||||
if (!err) {
|
||||
up_write(&fc->root->d_sb->s_umount);
|
||||
return vfs_create_mount(fc);
|
||||
}
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL(fc_mount);
|
||||
|
||||
struct vfsmount *vfs_kern_mount(struct file_system_type *type,
|
||||
int flags, const char *name,
|
||||
void *data)
|
||||
{
|
||||
struct fs_context *fc;
|
||||
struct vfsmount *mnt;
|
||||
int ret = 0;
|
||||
|
||||
if (!type)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
fc = fs_context_for_mount(type, flags);
|
||||
if (IS_ERR(fc))
|
||||
return ERR_CAST(fc);
|
||||
|
||||
if (name)
|
||||
ret = vfs_parse_fs_string(fc, "source",
|
||||
name, strlen(name));
|
||||
if (!ret)
|
||||
ret = parse_monolithic_mount_data(fc, data);
|
||||
if (!ret)
|
||||
mnt = fc_mount(fc);
|
||||
else
|
||||
mnt = ERR_PTR(ret);
|
||||
|
||||
put_fs_context(fc);
|
||||
return mnt;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfs_kern_mount);
|
||||
|
||||
struct vfsmount *
|
||||
@@ -1013,27 +1057,6 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
||||
|
||||
mnt->mnt.mnt_flags = old->mnt.mnt_flags;
|
||||
mnt->mnt.mnt_flags &= ~(MNT_WRITE_HOLD|MNT_MARKED|MNT_INTERNAL);
|
||||
/* Don't allow unprivileged users to change mount flags */
|
||||
if (flag & CL_UNPRIVILEGED) {
|
||||
mnt->mnt.mnt_flags |= MNT_LOCK_ATIME;
|
||||
|
||||
if (mnt->mnt.mnt_flags & MNT_READONLY)
|
||||
mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
|
||||
|
||||
if (mnt->mnt.mnt_flags & MNT_NODEV)
|
||||
mnt->mnt.mnt_flags |= MNT_LOCK_NODEV;
|
||||
|
||||
if (mnt->mnt.mnt_flags & MNT_NOSUID)
|
||||
mnt->mnt.mnt_flags |= MNT_LOCK_NOSUID;
|
||||
|
||||
if (mnt->mnt.mnt_flags & MNT_NOEXEC)
|
||||
mnt->mnt.mnt_flags |= MNT_LOCK_NOEXEC;
|
||||
}
|
||||
|
||||
/* Don't allow unprivileged users to reveal what is under a mount */
|
||||
if ((flag & CL_UNPRIVILEGED) &&
|
||||
(!(flag & CL_EXPIRE) || list_empty(&old->mnt_expire)))
|
||||
mnt->mnt.mnt_flags |= MNT_LOCKED;
|
||||
|
||||
atomic_inc(&sb->s_active);
|
||||
mnt->mnt.mnt_sb = sb;
|
||||
@@ -1464,6 +1487,29 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
|
||||
|
||||
static void shrink_submounts(struct mount *mnt);
|
||||
|
||||
static int do_umount_root(struct super_block *sb)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
down_write(&sb->s_umount);
|
||||
if (!sb_rdonly(sb)) {
|
||||
struct fs_context *fc;
|
||||
|
||||
fc = fs_context_for_reconfigure(sb->s_root, SB_RDONLY,
|
||||
SB_RDONLY);
|
||||
if (IS_ERR(fc)) {
|
||||
ret = PTR_ERR(fc);
|
||||
} else {
|
||||
ret = parse_monolithic_mount_data(fc, NULL);
|
||||
if (!ret)
|
||||
ret = reconfigure_super(fc);
|
||||
put_fs_context(fc);
|
||||
}
|
||||
}
|
||||
up_write(&sb->s_umount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_umount(struct mount *mnt, int flags)
|
||||
{
|
||||
struct super_block *sb = mnt->mnt.mnt_sb;
|
||||
@@ -1529,11 +1575,7 @@ static int do_umount(struct mount *mnt, int flags)
|
||||
*/
|
||||
if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
down_write(&sb->s_umount);
|
||||
if (!sb_rdonly(sb))
|
||||
retval = do_remount_sb(sb, SB_RDONLY, NULL, 0);
|
||||
up_write(&sb->s_umount);
|
||||
return retval;
|
||||
return do_umount_root(sb);
|
||||
}
|
||||
|
||||
namespace_lock();
|
||||
@@ -1839,6 +1881,33 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lock_mnt_tree(struct mount *mnt)
|
||||
{
|
||||
struct mount *p;
|
||||
|
||||
for (p = mnt; p; p = next_mnt(p, mnt)) {
|
||||
int flags = p->mnt.mnt_flags;
|
||||
/* Don't allow unprivileged users to change mount flags */
|
||||
flags |= MNT_LOCK_ATIME;
|
||||
|
||||
if (flags & MNT_READONLY)
|
||||
flags |= MNT_LOCK_READONLY;
|
||||
|
||||
if (flags & MNT_NODEV)
|
||||
flags |= MNT_LOCK_NODEV;
|
||||
|
||||
if (flags & MNT_NOSUID)
|
||||
flags |= MNT_LOCK_NOSUID;
|
||||
|
||||
if (flags & MNT_NOEXEC)
|
||||
flags |= MNT_LOCK_NOEXEC;
|
||||
/* Don't allow unprivileged users to reveal what is under a mount */
|
||||
if (list_empty(&p->mnt_expire))
|
||||
flags |= MNT_LOCKED;
|
||||
p->mnt.mnt_flags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup_group_ids(struct mount *mnt, struct mount *end)
|
||||
{
|
||||
struct mount *p;
|
||||
@@ -1956,6 +2025,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
|
||||
struct mountpoint *dest_mp,
|
||||
struct path *parent_path)
|
||||
{
|
||||
struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
|
||||
HLIST_HEAD(tree_list);
|
||||
struct mnt_namespace *ns = dest_mnt->mnt_ns;
|
||||
struct mountpoint *smp;
|
||||
@@ -2006,6 +2076,9 @@ static int attach_recursive_mnt(struct mount *source_mnt,
|
||||
child->mnt_mountpoint);
|
||||
if (q)
|
||||
mnt_change_mountpoint(child, smp, q);
|
||||
/* Notice when we are propagating across user namespaces */
|
||||
if (child->mnt_parent->mnt_ns->user_ns != user_ns)
|
||||
lock_mnt_tree(child);
|
||||
commit_tree(child);
|
||||
}
|
||||
put_mountpoint(smp);
|
||||
@@ -2313,7 +2386,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
|
||||
int err;
|
||||
struct super_block *sb = path->mnt->mnt_sb;
|
||||
struct mount *mnt = real_mount(path->mnt);
|
||||
void *sec_opts = NULL;
|
||||
struct fs_context *fc;
|
||||
|
||||
if (!check_mnt(mnt))
|
||||
return -EINVAL;
|
||||
@@ -2324,24 +2397,22 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
|
||||
if (!can_change_locked_flags(mnt, mnt_flags))
|
||||
return -EPERM;
|
||||
|
||||
if (data && !(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)) {
|
||||
err = security_sb_eat_lsm_opts(data, &sec_opts);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = security_sb_remount(sb, sec_opts);
|
||||
security_free_mnt_opts(&sec_opts);
|
||||
if (err)
|
||||
return err;
|
||||
fc = fs_context_for_reconfigure(path->dentry, sb_flags, MS_RMT_MASK);
|
||||
if (IS_ERR(fc))
|
||||
return PTR_ERR(fc);
|
||||
|
||||
down_write(&sb->s_umount);
|
||||
err = -EPERM;
|
||||
if (ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) {
|
||||
err = do_remount_sb(sb, sb_flags, data, 0);
|
||||
if (!err)
|
||||
set_mount_attributes(mnt, mnt_flags);
|
||||
err = parse_monolithic_mount_data(fc, data);
|
||||
if (!err) {
|
||||
down_write(&sb->s_umount);
|
||||
err = -EPERM;
|
||||
if (ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) {
|
||||
err = reconfigure_super(fc);
|
||||
if (!err)
|
||||
set_mount_attributes(mnt, mnt_flags);
|
||||
}
|
||||
up_write(&sb->s_umount);
|
||||
}
|
||||
up_write(&sb->s_umount);
|
||||
put_fs_context(fc);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2425,29 +2496,6 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
|
||||
{
|
||||
int err;
|
||||
const char *subtype = strchr(fstype, '.');
|
||||
if (subtype) {
|
||||
subtype++;
|
||||
err = -EINVAL;
|
||||
if (!subtype[0])
|
||||
goto err;
|
||||
} else
|
||||
subtype = "";
|
||||
|
||||
mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL);
|
||||
err = -ENOMEM;
|
||||
if (!mnt->mnt_sb->s_subtype)
|
||||
goto err;
|
||||
return mnt;
|
||||
|
||||
err:
|
||||
mntput(mnt);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/*
|
||||
* add a mount into a namespace's mount tree
|
||||
*/
|
||||
@@ -2492,7 +2540,39 @@ unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags);
|
||||
static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags);
|
||||
|
||||
/*
|
||||
* Create a new mount using a superblock configuration and request it
|
||||
* be added to the namespace tree.
|
||||
*/
|
||||
static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
|
||||
unsigned int mnt_flags)
|
||||
{
|
||||
struct vfsmount *mnt;
|
||||
struct super_block *sb = fc->root->d_sb;
|
||||
int error;
|
||||
|
||||
error = security_sb_kern_mount(sb);
|
||||
if (!error && mount_too_revealing(sb, &mnt_flags))
|
||||
error = -EPERM;
|
||||
|
||||
if (unlikely(error)) {
|
||||
fc_drop_locked(fc);
|
||||
return error;
|
||||
}
|
||||
|
||||
up_write(&sb->s_umount);
|
||||
|
||||
mnt = vfs_create_mount(fc);
|
||||
if (IS_ERR(mnt))
|
||||
return PTR_ERR(mnt);
|
||||
|
||||
error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags);
|
||||
if (error < 0)
|
||||
mntput(mnt);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a new mount for userspace and request it to be added into the
|
||||
@@ -2502,8 +2582,9 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
|
||||
int mnt_flags, const char *name, void *data)
|
||||
{
|
||||
struct file_system_type *type;
|
||||
struct vfsmount *mnt;
|
||||
int err;
|
||||
struct fs_context *fc;
|
||||
const char *subtype = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (!fstype)
|
||||
return -EINVAL;
|
||||
@@ -2512,23 +2593,37 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
|
||||
if (!type)
|
||||
return -ENODEV;
|
||||
|
||||
mnt = vfs_kern_mount(type, sb_flags, name, data);
|
||||
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
|
||||
!mnt->mnt_sb->s_subtype)
|
||||
mnt = fs_set_subtype(mnt, fstype);
|
||||
|
||||
put_filesystem(type);
|
||||
if (IS_ERR(mnt))
|
||||
return PTR_ERR(mnt);
|
||||
|
||||
if (mount_too_revealing(mnt, &mnt_flags)) {
|
||||
mntput(mnt);
|
||||
return -EPERM;
|
||||
if (type->fs_flags & FS_HAS_SUBTYPE) {
|
||||
subtype = strchr(fstype, '.');
|
||||
if (subtype) {
|
||||
subtype++;
|
||||
if (!*subtype) {
|
||||
put_filesystem(type);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
subtype = "";
|
||||
}
|
||||
}
|
||||
|
||||
err = do_add_mount(real_mount(mnt), path, mnt_flags);
|
||||
if (err)
|
||||
mntput(mnt);
|
||||
fc = fs_context_for_mount(type, sb_flags);
|
||||
put_filesystem(type);
|
||||
if (IS_ERR(fc))
|
||||
return PTR_ERR(fc);
|
||||
|
||||
if (subtype)
|
||||
err = vfs_parse_fs_string(fc, "subtype",
|
||||
subtype, strlen(subtype));
|
||||
if (!err && name)
|
||||
err = vfs_parse_fs_string(fc, "source", name, strlen(name));
|
||||
if (!err)
|
||||
err = parse_monolithic_mount_data(fc, data);
|
||||
if (!err)
|
||||
err = vfs_get_tree(fc);
|
||||
if (!err)
|
||||
err = do_new_mount_fc(fc, path, mnt_flags);
|
||||
|
||||
put_fs_context(fc);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2863,7 +2958,8 @@ static void dec_mnt_namespaces(struct ucounts *ucounts)
|
||||
|
||||
static void free_mnt_ns(struct mnt_namespace *ns)
|
||||
{
|
||||
ns_free_inum(&ns->ns);
|
||||
if (!is_anon_ns(ns))
|
||||
ns_free_inum(&ns->ns);
|
||||
dec_mnt_namespaces(ns->ucounts);
|
||||
put_user_ns(ns->user_ns);
|
||||
kfree(ns);
|
||||
@@ -2878,7 +2974,7 @@ static void free_mnt_ns(struct mnt_namespace *ns)
|
||||
*/
|
||||
static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1);
|
||||
|
||||
static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
|
||||
static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns, bool anon)
|
||||
{
|
||||
struct mnt_namespace *new_ns;
|
||||
struct ucounts *ucounts;
|
||||
@@ -2888,28 +2984,27 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
|
||||
if (!ucounts)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
|
||||
new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
|
||||
new_ns = kzalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
|
||||
if (!new_ns) {
|
||||
dec_mnt_namespaces(ucounts);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
ret = ns_alloc_inum(&new_ns->ns);
|
||||
if (ret) {
|
||||
kfree(new_ns);
|
||||
dec_mnt_namespaces(ucounts);
|
||||
return ERR_PTR(ret);
|
||||
if (!anon) {
|
||||
ret = ns_alloc_inum(&new_ns->ns);
|
||||
if (ret) {
|
||||
kfree(new_ns);
|
||||
dec_mnt_namespaces(ucounts);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
new_ns->ns.ops = &mntns_operations;
|
||||
new_ns->seq = atomic64_add_return(1, &mnt_ns_seq);
|
||||
if (!anon)
|
||||
new_ns->seq = atomic64_add_return(1, &mnt_ns_seq);
|
||||
atomic_set(&new_ns->count, 1);
|
||||
new_ns->root = NULL;
|
||||
INIT_LIST_HEAD(&new_ns->list);
|
||||
init_waitqueue_head(&new_ns->poll);
|
||||
new_ns->event = 0;
|
||||
new_ns->user_ns = get_user_ns(user_ns);
|
||||
new_ns->ucounts = ucounts;
|
||||
new_ns->mounts = 0;
|
||||
new_ns->pending_mounts = 0;
|
||||
return new_ns;
|
||||
}
|
||||
|
||||
@@ -2933,7 +3028,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
|
||||
|
||||
old = ns->root;
|
||||
|
||||
new_ns = alloc_mnt_ns(user_ns);
|
||||
new_ns = alloc_mnt_ns(user_ns, false);
|
||||
if (IS_ERR(new_ns))
|
||||
return new_ns;
|
||||
|
||||
@@ -2941,13 +3036,18 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
|
||||
/* First pass: copy the tree topology */
|
||||
copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
|
||||
if (user_ns != ns->user_ns)
|
||||
copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
|
||||
copy_flags |= CL_SHARED_TO_SLAVE;
|
||||
new = copy_tree(old, old->mnt.mnt_root, copy_flags);
|
||||
if (IS_ERR(new)) {
|
||||
namespace_unlock();
|
||||
free_mnt_ns(new_ns);
|
||||
return ERR_CAST(new);
|
||||
}
|
||||
if (user_ns != ns->user_ns) {
|
||||
lock_mount_hash();
|
||||
lock_mnt_tree(new);
|
||||
unlock_mount_hash();
|
||||
}
|
||||
new_ns->root = new;
|
||||
list_add_tail(&new_ns->list, &new->mnt_list);
|
||||
|
||||
@@ -2988,37 +3088,25 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
|
||||
return new_ns;
|
||||
}
|
||||
|
||||
/**
|
||||
* create_mnt_ns - creates a private namespace and adds a root filesystem
|
||||
* @mnt: pointer to the new root filesystem mountpoint
|
||||
*/
|
||||
static struct mnt_namespace *create_mnt_ns(struct vfsmount *m)
|
||||
{
|
||||
struct mnt_namespace *new_ns = alloc_mnt_ns(&init_user_ns);
|
||||
if (!IS_ERR(new_ns)) {
|
||||
struct mount *mnt = real_mount(m);
|
||||
mnt->mnt_ns = new_ns;
|
||||
new_ns->root = mnt;
|
||||
new_ns->mounts++;
|
||||
list_add(&mnt->mnt_list, &new_ns->list);
|
||||
} else {
|
||||
mntput(m);
|
||||
}
|
||||
return new_ns;
|
||||
}
|
||||
|
||||
struct dentry *mount_subtree(struct vfsmount *mnt, const char *name)
|
||||
struct dentry *mount_subtree(struct vfsmount *m, const char *name)
|
||||
{
|
||||
struct mount *mnt = real_mount(m);
|
||||
struct mnt_namespace *ns;
|
||||
struct super_block *s;
|
||||
struct path path;
|
||||
int err;
|
||||
|
||||
ns = create_mnt_ns(mnt);
|
||||
if (IS_ERR(ns))
|
||||
ns = alloc_mnt_ns(&init_user_ns, true);
|
||||
if (IS_ERR(ns)) {
|
||||
mntput(m);
|
||||
return ERR_CAST(ns);
|
||||
}
|
||||
mnt->mnt_ns = ns;
|
||||
ns->root = mnt;
|
||||
ns->mounts++;
|
||||
list_add(&mnt->mnt_list, &ns->list);
|
||||
|
||||
err = vfs_path_lookup(mnt->mnt_root, mnt,
|
||||
err = vfs_path_lookup(m->mnt_root, m,
|
||||
name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path);
|
||||
|
||||
put_mnt_ns(ns);
|
||||
@@ -3228,6 +3316,7 @@ out0:
|
||||
static void __init init_mount_tree(void)
|
||||
{
|
||||
struct vfsmount *mnt;
|
||||
struct mount *m;
|
||||
struct mnt_namespace *ns;
|
||||
struct path root;
|
||||
struct file_system_type *type;
|
||||
@@ -3240,10 +3329,14 @@ static void __init init_mount_tree(void)
|
||||
if (IS_ERR(mnt))
|
||||
panic("Can't create rootfs");
|
||||
|
||||
ns = create_mnt_ns(mnt);
|
||||
ns = alloc_mnt_ns(&init_user_ns, false);
|
||||
if (IS_ERR(ns))
|
||||
panic("Can't allocate initial namespace");
|
||||
|
||||
m = real_mount(mnt);
|
||||
m->mnt_ns = ns;
|
||||
ns->root = m;
|
||||
ns->mounts = 1;
|
||||
list_add(&m->mnt_list, &ns->list);
|
||||
init_task.nsproxy->mnt_ns = ns;
|
||||
get_mnt_ns(ns);
|
||||
|
||||
@@ -3297,10 +3390,10 @@ void put_mnt_ns(struct mnt_namespace *ns)
|
||||
free_mnt_ns(ns);
|
||||
}
|
||||
|
||||
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
|
||||
struct vfsmount *kern_mount(struct file_system_type *type)
|
||||
{
|
||||
struct vfsmount *mnt;
|
||||
mnt = vfs_kern_mount(type, SB_KERNMOUNT, type->name, data);
|
||||
mnt = vfs_kern_mount(type, SB_KERNMOUNT, type->name, NULL);
|
||||
if (!IS_ERR(mnt)) {
|
||||
/*
|
||||
* it is a longterm mount, don't release mnt until
|
||||
@@ -3310,7 +3403,7 @@ struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
|
||||
}
|
||||
return mnt;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kern_mount_data);
|
||||
EXPORT_SYMBOL_GPL(kern_mount);
|
||||
|
||||
void kern_unmount(struct vfsmount *mnt)
|
||||
{
|
||||
@@ -3352,7 +3445,8 @@ bool current_chrooted(void)
|
||||
return chrooted;
|
||||
}
|
||||
|
||||
static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new,
|
||||
static bool mnt_already_visible(struct mnt_namespace *ns,
|
||||
const struct super_block *sb,
|
||||
int *new_mnt_flags)
|
||||
{
|
||||
int new_flags = *new_mnt_flags;
|
||||
@@ -3364,7 +3458,7 @@ static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new,
|
||||
struct mount *child;
|
||||
int mnt_flags;
|
||||
|
||||
if (mnt->mnt.mnt_sb->s_type != new->mnt_sb->s_type)
|
||||
if (mnt->mnt.mnt_sb->s_type != sb->s_type)
|
||||
continue;
|
||||
|
||||
/* This mount is not fully visible if it's root directory
|
||||
@@ -3415,7 +3509,7 @@ found:
|
||||
return visible;
|
||||
}
|
||||
|
||||
static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags)
|
||||
static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags)
|
||||
{
|
||||
const unsigned long required_iflags = SB_I_NOEXEC | SB_I_NODEV;
|
||||
struct mnt_namespace *ns = current->nsproxy->mnt_ns;
|
||||
@@ -3425,7 +3519,7 @@ static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags)
|
||||
return false;
|
||||
|
||||
/* Can this filesystem be too revealing? */
|
||||
s_iflags = mnt->mnt_sb->s_iflags;
|
||||
s_iflags = sb->s_iflags;
|
||||
if (!(s_iflags & SB_I_USERNS_VISIBLE))
|
||||
return false;
|
||||
|
||||
@@ -3435,7 +3529,7 @@ static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags)
|
||||
return true;
|
||||
}
|
||||
|
||||
return !mnt_already_visible(ns, mnt, new_mnt_flags);
|
||||
return !mnt_already_visible(ns, sb, new_mnt_flags);
|
||||
}
|
||||
|
||||
bool mnt_may_suid(struct vfsmount *mnt)
|
||||
@@ -3484,6 +3578,9 @@ static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns)
|
||||
!ns_capable(current_user_ns(), CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (is_anon_ns(mnt_ns))
|
||||
return -EINVAL;
|
||||
|
||||
if (fs->users != 1)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user