]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - fs/namespace.c
net/mlx5: Fix some error handling paths in 'mlx5e_tc_add_fdb_flow()'
[mirror_ubuntu-jammy-kernel.git] / fs / namespace.c
index 659a8f39c61afbf1477ddc207f117894684f1ba3..ae07a9a7117b3adc55164ffcba7279816d09ed22 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"
@@ -439,6 +440,7 @@ void __mnt_drop_write(struct vfsmount *mnt)
        mnt_dec_writers(real_mount(mnt));
        preempt_enable();
 }
+EXPORT_SYMBOL_GPL(__mnt_drop_write);
 
 /**
  * mnt_drop_write - give up write access to a mount
@@ -561,7 +563,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
@@ -808,6 +810,13 @@ static inline int check_mnt(struct mount *mnt)
        return mnt->mnt_ns == current->nsproxy->mnt_ns;
 }
 
+/* for aufs, CONFIG_AUFS_BR_FUSE */
+int is_current_mnt_ns(struct vfsmount *mnt)
+{
+       return check_mnt(real_mount(mnt));
+}
+EXPORT_SYMBOL_GPL(is_current_mnt_ns);
+
 /*
  * vfsmount lock must be held for write
  */
@@ -965,6 +974,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 +992,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 +1086,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);
@@ -2005,6 +2019,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 +3942,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 +4021,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 +4165,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 +4297,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;
 }