attr.o bad_inode.o file.o filesystems.o namespace.o \
                seq_file.o xattr.o libfs.o fs-writeback.o \
                pnode.o splice.o sync.o utimes.o d_path.o \
-               stack.o fs_struct.o statfs.o fs_pin.o nsfs.o
+               stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
+               fs_context.o
 
 ifeq ($(CONFIG_BLOCK),y)
 obj-y +=       buffer.o block_dev.o direct-io.o mpage.o
 
--- /dev/null
+/* Provide a way to create a superblock configuration context within the kernel
+ * that allows a superblock to be set up prior to mounting.
+ *
+ * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/fs_context.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/nsproxy.h>
+#include <linux/slab.h>
+#include <linux/magic.h>
+#include <linux/security.h>
+#include <linux/mnt_namespace.h>
+#include <linux/pid_namespace.h>
+#include <linux/user_namespace.h>
+#include <net/net_namespace.h>
+#include "mount.h"
+#include "internal.h"
+
+struct legacy_fs_context {
+       char                    *legacy_data;   /* Data page for legacy filesystems */
+       size_t                  data_size;
+};
+
+static int legacy_init_fs_context(struct fs_context *fc);
+
+/**
+ * alloc_fs_context - Create a filesystem context.
+ * @fs_type: The filesystem type.
+ * @reference: The dentry from which this one derives (or NULL)
+ * @sb_flags: Filesystem/superblock flags (SB_*)
+ * @sb_flags_mask: Applicable members of @sb_flags
+ * @purpose: The purpose that this configuration shall be used for.
+ *
+ * Open a filesystem and create a mount context.  The mount context is
+ * initialised with the supplied flags and, if a submount/automount from
+ * another superblock (referred to by @reference) is supplied, may have
+ * parameters such as namespaces copied across from that superblock.
+ */
+static struct fs_context *alloc_fs_context(struct file_system_type *fs_type,
+                                     struct dentry *reference,
+                                     unsigned int sb_flags,
+                                     unsigned int sb_flags_mask,
+                                     enum fs_context_purpose purpose)
+{
+       struct fs_context *fc;
+       int ret = -ENOMEM;
+
+       fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL);
+       if (!fc)
+               return ERR_PTR(-ENOMEM);
+
+       fc->purpose     = purpose;
+       fc->sb_flags    = sb_flags;
+       fc->sb_flags_mask = sb_flags_mask;
+       fc->fs_type     = get_filesystem(fs_type);
+       fc->cred        = get_current_cred();
+       fc->net_ns      = get_net(current->nsproxy->net_ns);
+
+       switch (purpose) {
+       case FS_CONTEXT_FOR_MOUNT:
+               fc->user_ns = get_user_ns(fc->cred->user_ns);
+               break;
+       }
+
+       ret = legacy_init_fs_context(fc);
+       if (ret < 0)
+               goto err_fc;
+       fc->need_free = true;
+       return fc;
+
+err_fc:
+       put_fs_context(fc);
+       return ERR_PTR(ret);
+}
+
+struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
+                                       unsigned int sb_flags)
+{
+       return alloc_fs_context(fs_type, NULL, sb_flags, 0,
+                                       FS_CONTEXT_FOR_MOUNT);
+}
+EXPORT_SYMBOL(fs_context_for_mount);
+
+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.
+ */
+void put_fs_context(struct fs_context *fc)
+{
+       struct super_block *sb;
+
+       if (fc->root) {
+               sb = fc->root->d_sb;
+               dput(fc->root);
+               fc->root = NULL;
+               deactivate_super(sb);
+       }
+
+       if (fc->need_free)
+               legacy_fs_context_free(fc);
+
+       security_free_mnt_opts(&fc->security);
+       if (fc->net_ns)
+               put_net(fc->net_ns);
+       put_user_ns(fc->user_ns);
+       put_cred(fc->cred);
+       kfree(fc->subtype);
+       put_filesystem(fc->fs_type);
+       kfree(fc->source);
+       kfree(fc);
+}
+EXPORT_SYMBOL(put_fs_context);
+
+/*
+ * Free the config for a filesystem that doesn't support fs_context.
+ */
+static void legacy_fs_context_free(struct fs_context *fc)
+{
+       kfree(fc->fs_private);
+}
+
+/*
+ * Add monolithic mount data.
+ */
+static int legacy_parse_monolithic(struct fs_context *fc, void *data)
+{
+       struct legacy_fs_context *ctx = fc->fs_private;
+       ctx->legacy_data = data;
+       if (!ctx->legacy_data)
+               return 0;
+       if (fc->fs_type->fs_flags & FS_BINARY_MOUNTDATA)
+               return 0;
+       return security_sb_eat_lsm_opts(ctx->legacy_data, &fc->security);
+}
+
+/*
+ * Get a mountable root with the legacy mount command.
+ */
+int legacy_get_tree(struct fs_context *fc)
+{
+       struct legacy_fs_context *ctx = fc->fs_private;
+       struct super_block *sb;
+       struct dentry *root;
+
+       root = fc->fs_type->mount(fc->fs_type, fc->sb_flags,
+                                     fc->source, ctx->legacy_data);
+       if (IS_ERR(root))
+               return PTR_ERR(root);
+
+       sb = root->d_sb;
+       BUG_ON(!sb);
+
+       fc->root = root;
+       return 0;
+}
+
+/*
+ * Initialise a legacy context for a filesystem that doesn't support
+ * fs_context.
+ */
+static int legacy_init_fs_context(struct fs_context *fc)
+{
+       fc->fs_private = kzalloc(sizeof(struct legacy_fs_context), GFP_KERNEL);
+       if (!fc->fs_private)
+               return -ENOMEM;
+       return 0;
+}
+
+int parse_monolithic_mount_data(struct fs_context *fc, void *data)
+{
+       return legacy_parse_monolithic(fc, data);
+}
 
 struct path;
 struct mount;
 struct shrink_control;
+struct fs_context;
 
 /*
  * block_dev.c
  */
 extern void __init chrdev_init(void);
 
+/*
+ * fs_context.c
+ */
+extern int legacy_get_tree(struct fs_context *fc);
+extern int parse_monolithic_mount_data(struct fs_context *, void *);
+
 /*
  * namei.c
  */
  */
 extern int do_remount_sb(struct super_block *, int, void *, int);
 extern bool trylock_super(struct super_block *sb);
-extern struct dentry *mount_fs(struct file_system_type *,
-                              int, const char *, void *);
 extern struct super_block *user_get_super(dev_t);
 
 /*
 
 #include <linux/task_work.h>
 #include <linux/sched/task.h>
 #include <uapi/linux/mount.h>
+#include <linux/fs_context.h>
 
 #include "pnode.h"
 #include "internal.h"
        return p;
 }
 
-struct vfsmount *
-vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
+struct vfsmount *vfs_kern_mount(struct file_system_type *type,
+                               int flags, const char *name,
+                               void *data)
 {
+       struct fs_context *fc;
        struct mount *mnt;
-       struct dentry *root;
+       int ret = 0;
 
        if (!type)
                return ERR_PTR(-ENODEV);
 
+       fc = fs_context_for_mount(type, flags);
+       if (IS_ERR(fc))
+               return ERR_CAST(fc);
+
+       if (name) {
+               fc->source = kstrdup(name, GFP_KERNEL);
+               if (!fc->source)
+                       ret = -ENOMEM;
+       }
+       if (!ret)
+               ret = parse_monolithic_mount_data(fc, data);
+       if (!ret)
+               ret = vfs_get_tree(fc);
+       if (ret) {
+               put_fs_context(fc);
+               return ERR_PTR(ret);
+       }
+       up_write(&fc->root->d_sb->s_umount);
        mnt = alloc_vfsmnt(name);
-       if (!mnt)
+       if (!mnt) {
+               put_fs_context(fc);
                return ERR_PTR(-ENOMEM);
+       }
 
        if (flags & SB_KERNMOUNT)
                mnt->mnt.mnt_flags = MNT_INTERNAL;
 
-       root = mount_fs(type, flags, name, data);
-       if (IS_ERR(root)) {
-               mnt_free_id(mnt);
-               free_vfsmnt(mnt);
-               return ERR_CAST(root);
-       }
-
-       mnt->mnt.mnt_root = root;
-       mnt->mnt.mnt_sb = root->d_sb;
+       atomic_inc(&fc->root->d_sb->s_active);
+       mnt->mnt.mnt_root = dget(fc->root);
+       mnt->mnt.mnt_sb = fc->root->d_sb;
        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
        mnt->mnt_parent = mnt;
        lock_mount_hash();
-       list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
+       list_add_tail(&mnt->mnt_instance, &fc->root->d_sb->s_mounts);
        unlock_mount_hash();
+       put_fs_context(fc);
        return &mnt->mnt;
 }
 EXPORT_SYMBOL_GPL(vfs_kern_mount);
 
 #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"
 
 }
 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);
-       }
+       error = legacy_get_tree(fc);
+       if (error < 0)
+               return error;
 
-       root = type->mount(type, flags, name, data);
-       if (IS_ERR(root)) {
-               error = PTR_ERR(root);
-               goto out_free_secdata;
-       }
-       sb = root->d_sb;
-       BUG_ON(!sb);
+       sb = fc->root->d_sb;
        WARN_ON(!sb->s_bdi);
 
        /*
        smp_wmb();
        sb->s_flags |= SB_BORN;
 
-       error = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL);
+       error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL);
        if (error)
                goto out_sb;
 
-       if (!(flags & (MS_KERNMOUNT|MS_SUBMOUNT))) {
+       if (!(fc->sb_flags & (MS_KERNMOUNT|MS_SUBMOUNT))) {
                error = security_sb_kern_mount(sb);
                if (error)
                        goto out_sb;
         * 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;
+       return 0;
 out_sb:
-       dput(root);
+       dput(fc->root);
+       fc->root = NULL;
        deactivate_locked_super(sb);
-out_free_secdata:
-       security_free_mnt_opts(&sec_opts);
-       return ERR_PTR(error);
+       return error;
 }
+EXPORT_SYMBOL(vfs_get_tree);
 
 /*
  * Setup private BDI for given superblock. It gets automatically cleaned up
 
--- /dev/null
+/* Filesystem superblock creation and reconfiguration context.
+ *
+ * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_FS_CONTEXT_H
+#define _LINUX_FS_CONTEXT_H
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/security.h>
+
+struct cred;
+struct dentry;
+struct file_operations;
+struct file_system_type;
+struct net;
+struct user_namespace;
+
+enum fs_context_purpose {
+       FS_CONTEXT_FOR_MOUNT,           /* New superblock for explicit mount */
+};
+
+/*
+ * Filesystem context for holding the parameters used in the creation or
+ * reconfiguration of a superblock.
+ *
+ * Superblock creation fills in ->root whereas reconfiguration begins with this
+ * already set.
+ *
+ * See Documentation/filesystems/mounting.txt
+ */
+struct fs_context {
+       struct file_system_type *fs_type;
+       void                    *fs_private;    /* The filesystem's context */
+       struct dentry           *root;          /* The root and superblock */
+       struct user_namespace   *user_ns;       /* The user namespace for this mount */
+       struct net              *net_ns;        /* The network namespace for this mount */
+       const struct cred       *cred;          /* The mounter's credentials */
+       const char              *source;        /* The source name (eg. dev path) */
+       const char              *subtype;       /* The subtype to set on the superblock */
+       void                    *security;      /* Linux S&M options */
+       unsigned int            sb_flags;       /* Proposed superblock flags (SB_*) */
+       unsigned int            sb_flags_mask;  /* Superblock flags that were changed */
+       enum fs_context_purpose purpose:8;
+       bool                    need_free:1;    /* Need to call ops->free() */
+};
+
+/*
+ * fs_context manipulation functions.
+ */
+extern struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
+                                               unsigned int sb_flags);
+
+extern int vfs_get_tree(struct fs_context *fc);
+extern void put_fs_context(struct fs_context *fc);
+
+#endif /* _LINUX_FS_CONTEXT_H */