tizen_bcm2711_defconfig: Enable dm-bow
[platform/kernel/linux-rpi.git] / fs / namespace.c
index 659a8f3..5b42136 100644 (file)
@@ -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"
@@ -343,8 +344,24 @@ int __mnt_want_write(struct vfsmount *m)
         * incremented count after it has set MNT_WRITE_HOLD.
         */
        smp_mb();
-       while (READ_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD)
-               cpu_relax();
+       might_lock(&mount_lock.lock);
+       while (READ_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD) {
+               if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+                       cpu_relax();
+               } else {
+                       /*
+                        * This prevents priority inversion, if the task
+                        * setting MNT_WRITE_HOLD got preempted on a remote
+                        * CPU, and it prevents life lock if the task setting
+                        * MNT_WRITE_HOLD has a lower priority and is bound to
+                        * the same CPU as the task that is spinning here.
+                        */
+                       preempt_enable();
+                       lock_mount_hash();
+                       unlock_mount_hash();
+                       preempt_disable();
+               }
+       }
        /*
         * After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will
         * be set to match its requirements. So we must not load that until
@@ -561,7 +578,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 +982,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 +1000,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 +1094,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);
@@ -1904,6 +1926,7 @@ struct vfsmount *collect_mounts(const struct path *path)
                return ERR_CAST(tree);
        return &tree->mnt;
 }
+EXPORT_SYMBOL_GPL(collect_mounts);
 
 static void free_mnt_ns(struct mnt_namespace *);
 static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *, bool);
@@ -1934,6 +1957,7 @@ void drop_collected_mounts(struct vfsmount *mnt)
        unlock_mount_hash();
        namespace_unlock();
 }
+EXPORT_SYMBOL_GPL(drop_collected_mounts);
 
 static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
 {
@@ -2005,6 +2029,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
        }
        return 0;
 }
+EXPORT_SYMBOL_GPL(iterate_mounts);
 
 static void lock_mnt_tree(struct mount *mnt)
 {
@@ -3927,28 +3952,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 +4031,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,16 +4175,25 @@ 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;
+       }
+
+       /* We're not controlling the target namespace. */
+       if (!ns_capable(mnt_userns, CAP_SYS_ADMIN)) {
                err = -EPERM;
                goto out_fput;
        }
+
        kattr->mnt_userns = get_user_ns(mnt_userns);
 
 out_fput:
@@ -4263,12 +4314,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;
 }