diff options
Diffstat (limited to 'fs/namespace.c')
| -rw-r--r-- | fs/namespace.c | 62 | 
1 files changed, 43 insertions, 19 deletions
| diff --git a/fs/namespace.c b/fs/namespace.c index 659a8f39c61a..dc31ad6b370f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -31,6 +31,7 @@  #include <uapi/linux/mount.h>  #include <linux/fs_context.h>  #include <linux/shmem_fs.h> +#include <linux/mnt_idmapping.h>  #include "pnode.h"  #include "internal.h" @@ -561,7 +562,7 @@ static void free_vfsmnt(struct mount *mnt)  	struct user_namespace *mnt_userns;  	mnt_userns = mnt_user_ns(&mnt->mnt); -	if (mnt_userns != &init_user_ns) +	if (!initial_idmapping(mnt_userns))  		put_user_ns(mnt_userns);  	kfree_const(mnt->mnt_devname);  #ifdef CONFIG_SMP @@ -965,6 +966,7 @@ static struct mount *skip_mnt_tree(struct mount *p)  struct vfsmount *vfs_create_mount(struct fs_context *fc)  {  	struct mount *mnt; +	struct user_namespace *fs_userns;  	if (!fc->root)  		return ERR_PTR(-EINVAL); @@ -982,6 +984,10 @@ struct vfsmount *vfs_create_mount(struct fs_context *fc)  	mnt->mnt_mountpoint	= mnt->mnt.mnt_root;  	mnt->mnt_parent		= mnt; +	fs_userns = mnt->mnt.mnt_sb->s_user_ns; +	if (!initial_idmapping(fs_userns)) +		mnt->mnt.mnt_userns = get_user_ns(fs_userns); +  	lock_mount_hash();  	list_add_tail(&mnt->mnt_instance, &mnt->mnt.mnt_sb->s_mounts);  	unlock_mount_hash(); @@ -1072,7 +1078,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,  	atomic_inc(&sb->s_active);  	mnt->mnt.mnt_userns = mnt_user_ns(&old->mnt); -	if (mnt->mnt.mnt_userns != &init_user_ns) +	if (!initial_idmapping(mnt->mnt.mnt_userns))  		mnt->mnt.mnt_userns = get_user_ns(mnt->mnt.mnt_userns);  	mnt->mnt.mnt_sb = sb;  	mnt->mnt.mnt_root = dget(root); @@ -3927,28 +3933,32 @@ static unsigned int recalc_flags(struct mount_kattr *kattr, struct mount *mnt)  static int can_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)  {  	struct vfsmount *m = &mnt->mnt; +	struct user_namespace *fs_userns = m->mnt_sb->s_user_ns;  	if (!kattr->mnt_userns)  		return 0;  	/* +	 * Creating an idmapped mount with the filesystem wide idmapping +	 * doesn't make sense so block that. We don't allow mushy semantics. +	 */ +	if (kattr->mnt_userns == fs_userns) +		return -EINVAL; + +	/*  	 * Once a mount has been idmapped we don't allow it to change its  	 * mapping. It makes things simpler and callers can just create  	 * another bind-mount they can idmap if they want to.  	 */ -	if (mnt_user_ns(m) != &init_user_ns) +	if (is_idmapped_mnt(m))  		return -EPERM;  	/* The underlying filesystem doesn't support idmapped mounts yet. */  	if (!(m->mnt_sb->s_type->fs_flags & FS_ALLOW_IDMAP))  		return -EINVAL; -	/* Don't yet support filesystem mountable in user namespaces. */ -	if (m->mnt_sb->s_user_ns != &init_user_ns) -		return -EINVAL; -  	/* We're not controlling the superblock. */ -	if (!capable(CAP_SYS_ADMIN)) +	if (!ns_capable(fs_userns, CAP_SYS_ADMIN))  		return -EPERM;  	/* Mount has already been visible in the filesystem hierarchy. */ @@ -4002,14 +4012,27 @@ out:  static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)  { -	struct user_namespace *mnt_userns; +	struct user_namespace *mnt_userns, *old_mnt_userns;  	if (!kattr->mnt_userns)  		return; +	/* +	 * We're the only ones able to change the mount's idmapping. So +	 * mnt->mnt.mnt_userns is stable and we can retrieve it directly. +	 */ +	old_mnt_userns = mnt->mnt.mnt_userns; +  	mnt_userns = get_user_ns(kattr->mnt_userns);  	/* Pairs with smp_load_acquire() in mnt_user_ns(). */  	smp_store_release(&mnt->mnt.mnt_userns, mnt_userns); + +	/* +	 * If this is an idmapped filesystem drop the reference we've taken +	 * in vfs_create_mount() before. +	 */ +	if (!initial_idmapping(old_mnt_userns)) +		put_user_ns(old_mnt_userns);  }  static void mount_setattr_commit(struct mount_kattr *kattr, @@ -4133,13 +4156,15 @@ static int build_mount_idmapped(const struct mount_attr *attr, size_t usize,  	}  	/* -	 * The init_user_ns is used to indicate that a vfsmount is not idmapped. -	 * This is simpler than just having to treat NULL as unmapped. Users -	 * wanting to idmap a mount to init_user_ns can just use a namespace -	 * with an identity mapping. +	 * The initial idmapping cannot be used to create an idmapped +	 * mount. We use the initial idmapping as an indicator of a mount +	 * that is not idmapped. It can simply be passed into helpers that +	 * are aware of idmapped mounts as a convenient shortcut. A user +	 * can just create a dedicated identity mapping to achieve the same +	 * result.  	 */  	mnt_userns = container_of(ns, struct user_namespace, ns); -	if (mnt_userns == &init_user_ns) { +	if (initial_idmapping(mnt_userns)) {  		err = -EPERM;  		goto out_fput;  	} @@ -4263,12 +4288,11 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path,  		return err;  	err = user_path_at(dfd, path, kattr.lookup_flags, &target); -	if (err) -		return err; - -	err = do_mount_setattr(&target, &kattr); +	if (!err) { +		err = do_mount_setattr(&target, &kattr); +		path_put(&target); +	}  	finish_mount_kattr(&kattr); -	path_put(&target);  	return err;  } |