Merge tag 'acpi-5.1-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[sfrench/cifs-2.6.git] / fs / super.c
index 48e25eba846533dc0ae6b213e522d8bf52254087..583a0124bc394d2919b2fbc346948c5ab9684201 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/fsnotify.h>
 #include <linux/lockdep.h>
 #include <linux/user_namespace.h>
+#include <linux/fs_context.h>
 #include <uapi/linux/mount.h>
 #include "internal.h"
 
@@ -475,6 +476,94 @@ void generic_shutdown_super(struct super_block *sb)
 
 EXPORT_SYMBOL(generic_shutdown_super);
 
+/**
+ * sget_fc - Find or create a superblock
+ * @fc:        Filesystem context.
+ * @test: Comparison callback
+ * @set: Setup callback
+ *
+ * Find or create a superblock using the parameters stored in the filesystem
+ * context and the two callback functions.
+ *
+ * If an extant superblock is matched, then that will be returned with an
+ * elevated reference count that the caller must transfer or discard.
+ *
+ * If no match is made, a new superblock will be allocated and basic
+ * initialisation will be performed (s_type, s_fs_info and s_id will be set and
+ * the set() callback will be invoked), the superblock will be published and it
+ * will be returned in a partially constructed state with SB_BORN and SB_ACTIVE
+ * as yet unset.
+ */
+struct super_block *sget_fc(struct fs_context *fc,
+                           int (*test)(struct super_block *, struct fs_context *),
+                           int (*set)(struct super_block *, struct fs_context *))
+{
+       struct super_block *s = NULL;
+       struct super_block *old;
+       struct user_namespace *user_ns = fc->global ? &init_user_ns : fc->user_ns;
+       int err;
+
+       if (!(fc->sb_flags & SB_KERNMOUNT) &&
+           fc->purpose != FS_CONTEXT_FOR_SUBMOUNT) {
+               /* Don't allow mounting unless the caller has CAP_SYS_ADMIN
+                * over the namespace.
+                */
+               if (!(fc->fs_type->fs_flags & FS_USERNS_MOUNT)) {
+                       if (!capable(CAP_SYS_ADMIN))
+                               return ERR_PTR(-EPERM);
+               } else {
+                       if (!ns_capable(fc->user_ns, CAP_SYS_ADMIN))
+                               return ERR_PTR(-EPERM);
+               }
+       }
+
+retry:
+       spin_lock(&sb_lock);
+       if (test) {
+               hlist_for_each_entry(old, &fc->fs_type->fs_supers, s_instances) {
+                       if (test(old, fc))
+                               goto share_extant_sb;
+               }
+       }
+       if (!s) {
+               spin_unlock(&sb_lock);
+               s = alloc_super(fc->fs_type, fc->sb_flags, user_ns);
+               if (!s)
+                       return ERR_PTR(-ENOMEM);
+               goto retry;
+       }
+
+       s->s_fs_info = fc->s_fs_info;
+       err = set(s, fc);
+       if (err) {
+               s->s_fs_info = NULL;
+               spin_unlock(&sb_lock);
+               destroy_unused_super(s);
+               return ERR_PTR(err);
+       }
+       fc->s_fs_info = NULL;
+       s->s_type = fc->fs_type;
+       strlcpy(s->s_id, s->s_type->name, sizeof(s->s_id));
+       list_add_tail(&s->s_list, &super_blocks);
+       hlist_add_head(&s->s_instances, &s->s_type->fs_supers);
+       spin_unlock(&sb_lock);
+       get_filesystem(s->s_type);
+       register_shrinker_prepared(&s->s_shrink);
+       return s;
+
+share_extant_sb:
+       if (user_ns != old->s_user_ns) {
+               spin_unlock(&sb_lock);
+               destroy_unused_super(s);
+               return ERR_PTR(-EBUSY);
+       }
+       if (!grab_super(old))
+               goto retry;
+       destroy_unused_super(s);
+       return old;
+}
+EXPORT_SYMBOL(sget_fc);
+
 /**
  *     sget_userns -   find or create a superblock
  *     @type:  filesystem type superblock should belong to
@@ -835,28 +924,35 @@ rescan:
 }
 
 /**
- *     do_remount_sb - asks filesystem to change mount options.
- *     @sb:    superblock in question
- *     @sb_flags: revised superblock flags
- *     @data:  the rest of options
- *      @force: whether or not to force the change
+ * reconfigure_super - asks filesystem to change superblock parameters
+ * @fc: The superblock and configuration
  *
- *     Alters the mount options of a mounted file system.
+ * Alters the configuration parameters of a live superblock.
  */
-int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
+int reconfigure_super(struct fs_context *fc)
 {
+       struct super_block *sb = fc->root->d_sb;
        int retval;
-       int remount_ro;
+       bool remount_ro = false;
+       bool force = fc->sb_flags & SB_FORCE;
 
+       if (fc->sb_flags_mask & ~MS_RMT_MASK)
+               return -EINVAL;
        if (sb->s_writers.frozen != SB_UNFROZEN)
                return -EBUSY;
 
+       retval = security_sb_remount(sb, fc->security);
+       if (retval)
+               return retval;
+
+       if (fc->sb_flags_mask & SB_RDONLY) {
 #ifdef CONFIG_BLOCK
-       if (!(sb_flags & SB_RDONLY) && bdev_read_only(sb->s_bdev))
-               return -EACCES;
+               if (!(fc->sb_flags & SB_RDONLY) && bdev_read_only(sb->s_bdev))
+                       return -EACCES;
 #endif
 
-       remount_ro = (sb_flags & SB_RDONLY) && !sb_rdonly(sb);
+               remount_ro = (fc->sb_flags & SB_RDONLY) && !sb_rdonly(sb);
+       }
 
        if (remount_ro) {
                if (!hlist_empty(&sb->s_pins)) {
@@ -867,13 +963,14 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
                                return 0;
                        if (sb->s_writers.frozen != SB_UNFROZEN)
                                return -EBUSY;
-                       remount_ro = (sb_flags & SB_RDONLY) && !sb_rdonly(sb);
+                       remount_ro = !sb_rdonly(sb);
                }
        }
        shrink_dcache_sb(sb);
 
-       /* If we are remounting RDONLY and current sb is read/write,
-          make sure there are no rw files opened */
+       /* If we are reconfiguring to RDONLY and current sb is read/write,
+        * make sure there are no files open for writing.
+        */
        if (remount_ro) {
                if (force) {
                        sb->s_readonly_remount = 1;
@@ -885,8 +982,8 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
                }
        }
 
-       if (sb->s_op->remount_fs) {
-               retval = sb->s_op->remount_fs(sb, &sb_flags, data);
+       if (fc->ops->reconfigure) {
+               retval = fc->ops->reconfigure(fc);
                if (retval) {
                        if (!force)
                                goto cancel_readonly;
@@ -895,7 +992,9 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
                             sb->s_type->name, retval);
                }
        }
-       sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (sb_flags & MS_RMT_MASK);
+
+       WRITE_ONCE(sb->s_flags, ((sb->s_flags & ~fc->sb_flags_mask) |
+                                (fc->sb_flags & fc->sb_flags_mask)));
        /* Needs to be ordered wrt mnt_is_readonly() */
        smp_wmb();
        sb->s_readonly_remount = 0;
@@ -922,10 +1021,15 @@ static void do_emergency_remount_callback(struct super_block *sb)
        down_write(&sb->s_umount);
        if (sb->s_root && sb->s_bdev && (sb->s_flags & SB_BORN) &&
            !sb_rdonly(sb)) {
-               /*
-                * What lock protects sb->s_flags??
-                */
-               do_remount_sb(sb, SB_RDONLY, NULL, 1);
+               struct fs_context *fc;
+
+               fc = fs_context_for_reconfigure(sb->s_root,
+                                       SB_RDONLY | SB_FORCE, SB_RDONLY);
+               if (!IS_ERR(fc)) {
+                       if (parse_monolithic_mount_data(fc, NULL) == 0)
+                               (void)reconfigure_super(fc);
+                       put_fs_context(fc);
+               }
        }
        up_write(&sb->s_umount);
 }
@@ -1087,6 +1191,89 @@ struct dentry *mount_ns(struct file_system_type *fs_type,
 
 EXPORT_SYMBOL(mount_ns);
 
+int set_anon_super_fc(struct super_block *sb, struct fs_context *fc)
+{
+       return set_anon_super(sb, NULL);
+}
+EXPORT_SYMBOL(set_anon_super_fc);
+
+static int test_keyed_super(struct super_block *sb, struct fs_context *fc)
+{
+       return sb->s_fs_info == fc->s_fs_info;
+}
+
+static int test_single_super(struct super_block *s, struct fs_context *fc)
+{
+       return 1;
+}
+
+/**
+ * vfs_get_super - Get a superblock with a search key set in s_fs_info.
+ * @fc: The filesystem context holding the parameters
+ * @keying: How to distinguish superblocks
+ * @fill_super: Helper to initialise a new superblock
+ *
+ * Search for a superblock and create a new one if not found.  The search
+ * criterion is controlled by @keying.  If the search fails, a new superblock
+ * is created and @fill_super() is called to initialise it.
+ *
+ * @keying can take one of a number of values:
+ *
+ * (1) vfs_get_single_super - Only one superblock of this type may exist on the
+ *     system.  This is typically used for special system filesystems.
+ *
+ * (2) vfs_get_keyed_super - Multiple superblocks may exist, but they must have
+ *     distinct keys (where the key is in s_fs_info).  Searching for the same
+ *     key again will turn up the superblock for that key.
+ *
+ * (3) vfs_get_independent_super - Multiple superblocks may exist and are
+ *     unkeyed.  Each call will get a new superblock.
+ *
+ * A permissions check is made by sget_fc() unless we're getting a superblock
+ * for a kernel-internal mount or a submount.
+ */
+int vfs_get_super(struct fs_context *fc,
+                 enum vfs_get_super_keying keying,
+                 int (*fill_super)(struct super_block *sb,
+                                   struct fs_context *fc))
+{
+       int (*test)(struct super_block *, struct fs_context *);
+       struct super_block *sb;
+
+       switch (keying) {
+       case vfs_get_single_super:
+               test = test_single_super;
+               break;
+       case vfs_get_keyed_super:
+               test = test_keyed_super;
+               break;
+       case vfs_get_independent_super:
+               test = NULL;
+               break;
+       default:
+               BUG();
+       }
+
+       sb = sget_fc(fc, test, set_anon_super_fc);
+       if (IS_ERR(sb))
+               return PTR_ERR(sb);
+
+       if (!sb->s_root) {
+               int err = fill_super(sb, fc);
+               if (err) {
+                       deactivate_locked_super(sb);
+                       return err;
+               }
+
+               sb->s_flags |= SB_ACTIVE;
+       }
+
+       BUG_ON(fc->root);
+       fc->root = dget(sb->s_root);
+       return 0;
+}
+EXPORT_SYMBOL(vfs_get_super);
+
 #ifdef CONFIG_BLOCK
 static int set_bdev_super(struct super_block *s, void *data)
 {
@@ -1212,6 +1399,31 @@ struct dentry *mount_nodev(struct file_system_type *fs_type,
 }
 EXPORT_SYMBOL(mount_nodev);
 
+static int reconfigure_single(struct super_block *s,
+                             int flags, void *data)
+{
+       struct fs_context *fc;
+       int ret;
+
+       /* The caller really need to be passing fc down into mount_single(),
+        * then a chunk of this can be removed.  [Bollocks -- AV]
+        * Better yet, reconfiguration shouldn't happen, but rather the second
+        * mount should be rejected if the parameters are not compatible.
+        */
+       fc = fs_context_for_reconfigure(s->s_root, flags, MS_RMT_MASK);
+       if (IS_ERR(fc))
+               return PTR_ERR(fc);
+
+       ret = parse_monolithic_mount_data(fc, data);
+       if (ret < 0)
+               goto out;
+
+       ret = reconfigure_super(fc);
+out:
+       put_fs_context(fc);
+       return ret;
+}
+
 static int compare_single(struct super_block *s, void *p)
 {
        return 1;
@@ -1229,41 +1441,64 @@ struct dentry *mount_single(struct file_system_type *fs_type,
                return ERR_CAST(s);
        if (!s->s_root) {
                error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
-               if (error) {
-                       deactivate_locked_super(s);
-                       return ERR_PTR(error);
-               }
-               s->s_flags |= SB_ACTIVE;
+               if (!error)
+                       s->s_flags |= SB_ACTIVE;
        } else {
-               do_remount_sb(s, flags, data, 0);
+               error = reconfigure_single(s, flags, data);
+       }
+       if (unlikely(error)) {
+               deactivate_locked_super(s);
+               return ERR_PTR(error);
        }
        return dget(s->s_root);
 }
 EXPORT_SYMBOL(mount_single);
 
-struct dentry *
-mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
+/**
+ * vfs_get_tree - Get the mountable root
+ * @fc: The superblock configuration context.
+ *
+ * The filesystem is invoked to get or create a superblock which can then later
+ * be used for mounting.  The filesystem places a pointer to the root to be
+ * used for mounting in @fc->root.
+ */
+int vfs_get_tree(struct fs_context *fc)
 {
-       struct dentry *root;
        struct super_block *sb;
-       int error = -ENOMEM;
-       void *sec_opts = NULL;
+       int error;
 
-       if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
-               error = security_sb_eat_lsm_opts(data, &sec_opts);
-               if (error)
-                       return ERR_PTR(error);
+       if (fc->fs_type->fs_flags & FS_REQUIRES_DEV && !fc->source) {
+               errorf(fc, "Filesystem requires source device");
+               return -ENOENT;
        }
 
-       root = type->mount(type, flags, name, data);
-       if (IS_ERR(root)) {
-               error = PTR_ERR(root);
-               goto out_free_secdata;
+       if (fc->root)
+               return -EBUSY;
+
+       /* Get the mountable root in fc->root, with a ref on the root and a ref
+        * on the superblock.
+        */
+       error = fc->ops->get_tree(fc);
+       if (error < 0)
+               return error;
+
+       if (!fc->root) {
+               pr_err("Filesystem %s get_tree() didn't set fc->root\n",
+                      fc->fs_type->name);
+               /* We don't know what the locking state of the superblock is -
+                * if there is a superblock.
+                */
+               BUG();
        }
-       sb = root->d_sb;
-       BUG_ON(!sb);
+
+       sb = fc->root->d_sb;
        WARN_ON(!sb->s_bdi);
 
+       if (fc->subtype && !sb->s_subtype) {
+               sb->s_subtype = fc->subtype;
+               fc->subtype = NULL;
+       }
+
        /*
         * Write barrier is for super_cache_count(). We place it before setting
         * SB_BORN as the data dependency between the two functions is the
@@ -1273,14 +1508,10 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
        smp_wmb();
        sb->s_flags |= SB_BORN;
 
-       error = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL);
-       if (error)
-               goto out_sb;
-
-       if (!(flags & (MS_KERNMOUNT|MS_SUBMOUNT))) {
-               error = security_sb_kern_mount(sb);
-               if (error)
-                       goto out_sb;
+       error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL);
+       if (unlikely(error)) {
+               fc_drop_locked(fc);
+               return error;
        }
 
        /*
@@ -1290,18 +1521,11 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
         * violate this rule.
         */
        WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
-               "negative value (%lld)\n", type->name, sb->s_maxbytes);
+               "negative value (%lld)\n", fc->fs_type->name, sb->s_maxbytes);
 
-       up_write(&sb->s_umount);
-       security_free_mnt_opts(&sec_opts);
-       return root;
-out_sb:
-       dput(root);
-       deactivate_locked_super(sb);
-out_free_secdata:
-       security_free_mnt_opts(&sec_opts);
-       return ERR_PTR(error);
+       return 0;
 }
+EXPORT_SYMBOL(vfs_get_tree);
 
 /*
  * Setup private BDI for given superblock. It gets automatically cleaned up