convert do_remount_sb() to fs_context
authorDavid Howells <dhowells@redhat.com>
Sun, 4 Nov 2018 14:28:36 +0000 (09:28 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Wed, 30 Jan 2019 22:44:26 +0000 (17:44 -0500)
Replace do_remount_sb() with a function, reconfigure_super(), that's
fs_context aware.  The fs_context is expected to be parameterised already
and have ->root pointing to the superblock to be reconfigured.

A legacy wrapper is provided that is intended to be called from the
fs_context ops when those appear, but for now is called directly from
reconfigure_super().  This wrapper invokes the ->remount_fs() superblock op
for the moment.  It is intended that the remount_fs() op will be phased
out.

The fs_context->purpose is set to FS_CONTEXT_FOR_RECONFIGURE to indicate
that the context is being used for reconfiguration.

do_umount_root() is provided to consolidate remount-to-R/O for umount and
emergency remount by creating a context and invoking reconfiguration.

do_remount(), do_umount() and do_emergency_remount_callback() are switched
to use the new process.

[AV -- fold UMOUNT and EMERGENCY_REMOUNT in; fixes the
umount / bug, gets rid of pointless complexity]
[AV -- set ->net_ns in all cases; nfs remount will need that]
[AV -- shift security_sb_remount() call into reconfigure_super(); the callers
that didn't do security_sb_remount() have NULL fc->security anyway, so it's
a no-op for them]

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/fs_context.c
fs/internal.h
fs/namespace.c
fs/super.c
include/linux/fs.h
include/linux/fs_context.h

index 857cd46..5e2c3ab 100644 (file)
@@ -69,6 +69,13 @@ static struct fs_context *alloc_fs_context(struct file_system_type *fs_type,
        case FS_CONTEXT_FOR_MOUNT:
                fc->user_ns = get_user_ns(fc->cred->user_ns);
                break;
+       case FS_CONTEXT_FOR_RECONFIGURE:
+               /* We don't pin any namespaces as the superblock's
+                * subscriptions cannot be changed at this point.
+                */
+               atomic_inc(&reference->d_sb->s_active);
+               fc->root = dget(reference);
+               break;
        }
 
        ret = legacy_init_fs_context(fc);
@@ -90,6 +97,15 @@ struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
 }
 EXPORT_SYMBOL(fs_context_for_mount);
 
+struct fs_context *fs_context_for_reconfigure(struct dentry *dentry,
+                                       unsigned int sb_flags,
+                                       unsigned int sb_flags_mask)
+{
+       return alloc_fs_context(dentry->d_sb->s_type, dentry, sb_flags,
+                               sb_flags_mask, FS_CONTEXT_FOR_RECONFIGURE);
+}
+EXPORT_SYMBOL(fs_context_for_reconfigure);
+
 void fc_drop_locked(struct fs_context *fc)
 {
        struct super_block *sb = fc->root->d_sb;
@@ -99,6 +115,7 @@ void fc_drop_locked(struct fs_context *fc)
 }
 
 static void legacy_fs_context_free(struct fs_context *fc);
+
 /**
  * put_fs_context - Dispose of a superblock configuration context.
  * @fc: The context to dispose of.
@@ -118,8 +135,7 @@ void put_fs_context(struct fs_context *fc)
                legacy_fs_context_free(fc);
 
        security_free_mnt_opts(&fc->security);
-       if (fc->net_ns)
-               put_net(fc->net_ns);
+       put_net(fc->net_ns);
        put_user_ns(fc->user_ns);
        put_cred(fc->cred);
        kfree(fc->subtype);
@@ -173,6 +189,21 @@ int legacy_get_tree(struct fs_context *fc)
 }
 
 /*
+ * Handle remount.
+ */
+int legacy_reconfigure(struct fs_context *fc)
+{
+       struct legacy_fs_context *ctx = fc->fs_private;
+       struct super_block *sb = fc->root->d_sb;
+
+       if (!sb->s_op->remount_fs)
+               return 0;
+
+       return sb->s_op->remount_fs(sb, &fc->sb_flags,
+                                   ctx ? ctx->legacy_data : NULL);
+}
+
+/*
  * Initialise a legacy context for a filesystem that doesn't support
  * fs_context.
  */
index 6af26d8..016a5b8 100644 (file)
@@ -56,6 +56,7 @@ extern void __init chrdev_init(void);
  * fs_context.c
  */
 extern int legacy_get_tree(struct fs_context *fc);
+extern int legacy_reconfigure(struct fs_context *fc);
 extern int parse_monolithic_mount_data(struct fs_context *, void *);
 extern void fc_drop_locked(struct fs_context *);
 
@@ -107,7 +108,7 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *);
 /*
  * super.c
  */
-extern int do_remount_sb(struct super_block *, int, void *, int);
+extern int reconfigure_super(struct fs_context *);
 extern bool trylock_super(struct super_block *sb);
 extern struct super_block *user_get_super(dev_t);
 
index 750500c..931228d 100644 (file)
@@ -1489,6 +1489,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;
@@ -1554,11 +1577,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();
@@ -2367,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;
@@ -2378,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;
 }
 
index 11e2a6c..5055323 100644 (file)
@@ -836,28 +836,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)) {
@@ -868,13 +875,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;
@@ -886,17 +894,17 @@ 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 (retval) {
-                       if (!force)
-                               goto cancel_readonly;
-                       /* If forced remount, go ahead despite any errors */
-                       WARN(1, "forced remount of a %s fs returned %i\n",
-                            sb->s_type->name, retval);
-               }
+       retval = legacy_reconfigure(fc);
+       if (retval) {
+               if (!force)
+                       goto cancel_readonly;
+               /* If forced remount, go ahead despite any errors */
+               WARN(1, "forced remount of a %s fs returned %i\n",
+                    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;
@@ -923,10 +931,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);
 }
@@ -1213,6 +1226,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;
@@ -1230,13 +1268,14 @@ 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);
 }
index 36fff12..c65d02c 100644 (file)
@@ -1337,6 +1337,7 @@ extern int send_sigurg(struct fown_struct *fown);
 
 /* These sb flags are internal to the kernel */
 #define SB_SUBMOUNT     (1<<26)
+#define SB_FORCE       (1<<27)
 #define SB_NOSEC       (1<<28)
 #define SB_BORN                (1<<29)
 #define SB_ACTIVE      (1<<30)
index 9805514..98772f8 100644 (file)
@@ -25,6 +25,7 @@ struct user_namespace;
 
 enum fs_context_purpose {
        FS_CONTEXT_FOR_MOUNT,           /* New superblock for explicit mount */
+       FS_CONTEXT_FOR_RECONFIGURE,     /* Superblock reconfiguration (remount) */
 };
 
 /*
@@ -57,6 +58,9 @@ struct fs_context {
  */
 extern struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
                                                unsigned int sb_flags);
+extern struct fs_context *fs_context_for_reconfigure(struct dentry *dentry,
+                                               unsigned int sb_flags,
+                                               unsigned int sb_flags_mask);
 
 extern int vfs_get_tree(struct fs_context *fc);
 extern void put_fs_context(struct fs_context *fc);