security: Remove the set_mnt_opts and clone_mnt_opts ops
diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
index ec5f0a1..824600e 100644
--- a/fs/nfs/fs_context.c
+++ b/fs/nfs/fs_context.c
@@ -1297,8 +1297,6 @@ static int nfs_get_ordinary_tree(struct fs_context *fc)
 {
 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
 
-	ctx->set_security = nfs_set_sb_security;
-
 	return ctx->nfs_mod->rpc_ops->try_get_tree(fc);
 }
 
@@ -1313,8 +1311,6 @@ static int nfs_get_xdev_tree(struct fs_context *fc)
 
 	dprintk("--> nfs_xdev_mount()\n");
 
-	ctx->set_security = nfs_clone_sb_security;
-
 	/* create a new volume representation */
 	server = ctx->nfs_mod->rpc_ops->clone_server(NFS_SB(ctx->clone_data.sb),
 						     ctx->mntfh,
@@ -1367,7 +1363,7 @@ static int nfs_get_tree(struct fs_context *fc)
 }
 
 /*
- * Handle duplication of a configuration.  The caller copied *src into *sc, but
+ * Handle duplication of a configuration.  The caller copied *src into *fc, but
  * it can't deal with resource pointers in the filesystem context, so we have
  * to do that.  We need to clear pointers, copy data or get extra refs as
  * appropriate.
@@ -1512,6 +1508,13 @@ static int nfs_init_fs_context(struct fs_context *fc, struct dentry *reference)
 		ret = -EINVAL;
 		if (!reference)
 			goto error_fh;
+		ret = -ESTALE;
+		if (d_inode(reference)->i_op !=
+		    NFS_SB(reference->d_sb)->nfs_client->rpc_ops->dir_inode_ops)
+			goto error_fh;
+		if (NFS_SB(reference->d_sb)->caps & NFS_CAP_SECURITY_LABEL)
+			fc->lsm_flags |= SECURITY_LSM_NATIVE_LABELS;
+
 		/* Fall through */
 	case FS_CONTEXT_FOR_RECONFIGURE:
 		ret = nfs_mount_init_from_ref(fc, ctx, reference);
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 80784db..82b1fe5 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -415,8 +415,6 @@ extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int mode);
 extern const struct super_operations nfs_sops;
 bool nfs_auth_info_match(const struct nfs_auth_info *, rpc_authflavor_t);
 int nfs_try_get_tree(struct fs_context *);
-int nfs_set_sb_security(struct super_block *, struct fs_context *);
-int nfs_clone_sb_security(struct super_block *, struct fs_context *);
 int nfs_get_tree_common(struct nfs_server *, struct fs_context *);
 void nfs_kill_super(struct super_block *);
 
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
index b17b31e0..ffe3b11 100644
--- a/fs/nfs/nfs4super.c
+++ b/fs/nfs/nfs4super.c
@@ -78,8 +78,6 @@ static int nfs4_get_remote_tree(struct fs_context *fc)
 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
 	struct nfs_server *server;
 
-	ctx->set_security = nfs_set_sb_security;
-
 	/* Get a volume representation */
 	server = nfs4_create_server(fc);
 	if (IS_ERR(server))
@@ -271,8 +269,6 @@ static int nfs4_get_remote_referral_tree(struct fs_context *fc)
 
 	dprintk("--> nfs4_get_remote_referral_tree()\n");
 
-	ctx->set_security = nfs_clone_sb_security;
-
 	if (!ctx->clone_data.cloned)
 		return -EINVAL;
 
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index ac77dc6..90b463b 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1169,52 +1169,6 @@ static void nfs_get_cache_cookie(struct super_block *sb,
 }
 #endif
 
-int nfs_set_sb_security(struct super_block *sb, struct fs_context *fc)
-{
-	int error;
-	unsigned long kflags = 0, kflags_out = 0;
-
-	if (NFS_SB(sb)->caps & NFS_CAP_SECURITY_LABEL)
-		kflags |= SECURITY_LSM_NATIVE_LABELS;
-
-	error = security_sb_set_mnt_opts(sb, fc->security, kflags, &kflags_out);
-	if (error)
-		goto err;
-
-	if (NFS_SB(sb)->caps & NFS_CAP_SECURITY_LABEL &&
-	    !(kflags_out & SECURITY_LSM_NATIVE_LABELS))
-		NFS_SB(sb)->caps &= ~NFS_CAP_SECURITY_LABEL;
-err:
-	return error;
-}
-EXPORT_SYMBOL_GPL(nfs_set_sb_security);
-
-int nfs_clone_sb_security(struct super_block *sb, struct fs_context *fc)
-{
-	struct nfs_fs_context *ctx = nfs_fc2context(fc);
-	int error;
-	unsigned long kflags = 0, kflags_out = 0;
-
-	/* clone any lsm security options from the parent to the new sb */
-	if (d_inode(fc->root)->i_op !=
-	    NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops)
-		return -ESTALE;
-
-	if (NFS_SB(sb)->caps & NFS_CAP_SECURITY_LABEL)
-		kflags |= SECURITY_LSM_NATIVE_LABELS;
-
-	error = security_sb_clone_mnt_opts(ctx->clone_data.sb, sb, kflags,
-					   &kflags_out);
-	if (error)
-		return error;
-
-	if (NFS_SB(sb)->caps & NFS_CAP_SECURITY_LABEL &&
-	    !(kflags_out & SECURITY_LSM_NATIVE_LABELS))
-		NFS_SB(sb)->caps &= ~NFS_CAP_SECURITY_LABEL;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(nfs_clone_sb_security);
-
 int nfs_get_tree_common(struct nfs_server *server, struct fs_context *fc)
 {
 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
@@ -1276,9 +1230,8 @@ int nfs_get_tree_common(struct nfs_server *server, struct fs_context *fc)
 		goto error_splat_super;
 	}
 
-	error = ctx->set_security(s, fc);
-	if (error)
-		goto error_splat_root;
+	if (!(fc->lsm_flags & SECURITY_LSM_NATIVE_LABELS))
+		server->caps &= ~NFS_CAP_SECURITY_LABEL;
 
 	s->s_flags |= SB_ACTIVE;
 	error = 0;
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 227b46d..15fd4f3 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -160,18 +160,6 @@
  *	current root (put_old).
  *	@new_path contains the path for the new root (new_root).
  *	Return 0 if permission is granted.
- * @sb_set_mnt_opts:
- *	Set the security relevant mount options used for a superblock
- *	@sb the superblock to set security mount options for
- *	@opts binary data structure containing all lsm mount data
- * @sb_clone_mnt_opts:
- *	Copy all security options from a given superblock to another
- *	@oldsb old superblock which contain information to clone
- *	@newsb new superblock which needs filled in
- * @sb_parse_opts_str:
- *	Parse a string of security data filling in the opts structure
- *	@options string containing all mount options known by the LSM
- *	@opts binary data structure usable by the LSM
  * @move_mount:
  *	Check permission before a mount is moved.
  *	@from_path indicates the mount that is going to be moved.
@@ -1510,15 +1498,6 @@ union security_list_options {
 			void *data, size_t data_size);
 	int (*sb_umount)(struct vfsmount *mnt, int flags);
 	int (*sb_pivotroot)(const struct path *old_path, const struct path *new_path);
-	int (*sb_set_mnt_opts)(struct super_block *sb,
-				struct security_mnt_opts *opts,
-				unsigned long kern_flags,
-				unsigned long *set_kern_flags);
-	int (*sb_clone_mnt_opts)(const struct super_block *oldsb,
-					struct super_block *newsb,
-					unsigned long kern_flags,
-					unsigned long *set_kern_flags);
-	int (*sb_parse_opts_str)(char *options, struct security_mnt_opts *opts);
 	int (*move_mount)(const struct path *from_path, const struct path *to_path);
 	int (*dentry_init_security)(struct dentry *dentry, int mode,
 					const struct qstr *name, void **ctx,
@@ -1854,9 +1833,6 @@ struct security_hook_heads {
 	struct hlist_head sb_mount;
 	struct hlist_head sb_umount;
 	struct hlist_head sb_pivotroot;
-	struct hlist_head sb_set_mnt_opts;
-	struct hlist_head sb_clone_mnt_opts;
-	struct hlist_head sb_parse_opts_str;
 	struct hlist_head move_mount;
 	struct hlist_head dentry_init_security;
 	struct hlist_head dentry_create_files_as;
diff --git a/include/linux/security.h b/include/linux/security.h
index 6c1a54e..44368578 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -185,36 +185,10 @@ static inline const char *kernel_load_data_id_str(enum kernel_load_data_id id)
 
 #ifdef CONFIG_SECURITY
 
-struct security_mnt_opts {
-	char **mnt_opts;
-	int *mnt_opts_flags;
-	int num_mnt_opts;
-};
-
 int call_lsm_notifier(enum lsm_event event, void *data);
 int register_lsm_notifier(struct notifier_block *nb);
 int unregister_lsm_notifier(struct notifier_block *nb);
 
-static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
-{
-	opts->mnt_opts = NULL;
-	opts->mnt_opts_flags = NULL;
-	opts->num_mnt_opts = 0;
-}
-
-static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
-{
-	int i;
-	if (opts->mnt_opts)
-		for (i = 0; i < opts->num_mnt_opts; i++)
-			kfree(opts->mnt_opts[i]);
-	kfree(opts->mnt_opts);
-	opts->mnt_opts = NULL;
-	kfree(opts->mnt_opts_flags);
-	opts->mnt_opts_flags = NULL;
-	opts->num_mnt_opts = 0;
-}
-
 /* prototypes */
 extern int security_init(void);
 
@@ -266,15 +240,6 @@ int security_sb_mount(const char *dev_name, const struct path *path,
 		      const char *type, unsigned long flags, void *data, size_t data_size);
 int security_sb_umount(struct vfsmount *mnt, int flags);
 int security_sb_pivotroot(const struct path *old_path, const struct path *new_path);
-int security_sb_set_mnt_opts(struct super_block *sb,
-				struct security_mnt_opts *opts,
-				unsigned long kern_flags,
-				unsigned long *set_kern_flags);
-int security_sb_clone_mnt_opts(const struct super_block *oldsb,
-				struct super_block *newsb,
-				unsigned long kern_flags,
-				unsigned long *set_kern_flags);
-int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
 int security_move_mount(const struct path *from_path, const struct path *to_path);
 int security_dentry_init_security(struct dentry *dentry, int mode,
 					const struct qstr *name, void **ctx,
@@ -413,8 +378,6 @@ int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
 int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
 int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
 #else /* CONFIG_SECURITY */
-struct security_mnt_opts {
-};
 
 static inline int call_lsm_notifier(enum lsm_event event, void *data)
 {
@@ -431,14 +394,6 @@ static inline  int unregister_lsm_notifier(struct notifier_block *nb)
 	return 0;
 }
 
-static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
-{
-}
-
-static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
-{
-}
-
 /*
  * This is the default capabilities functionality.  Most of these functions
  * are just stubbed out, but a few must call the proper capable code.
@@ -629,27 +584,6 @@ static inline int security_sb_pivotroot(const struct path *old_path,
 	return 0;
 }
 
-static inline int security_sb_set_mnt_opts(struct super_block *sb,
-					   struct security_mnt_opts *opts,
-					   unsigned long kern_flags,
-					   unsigned long *set_kern_flags)
-{
-	return 0;
-}
-
-static inline int security_sb_clone_mnt_opts(const struct super_block *oldsb,
-					      struct super_block *newsb,
-					      unsigned long kern_flags,
-					      unsigned long *set_kern_flags)
-{
-	return 0;
-}
-
-static inline int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
-{
-	return 0;
-}
-
 static inline int security_move_mount(const struct path *from_path,
 				      const struct path *to_path)
 {
diff --git a/security/security.c b/security/security.c
index 70625ab..54eac40 100644
--- a/security/security.c
+++ b/security/security.c
@@ -453,33 +453,6 @@ int security_sb_pivotroot(const struct path *old_path, const struct path *new_pa
 	return call_int_hook(sb_pivotroot, 0, old_path, new_path);
 }
 
-int security_sb_set_mnt_opts(struct super_block *sb,
-				struct security_mnt_opts *opts,
-				unsigned long kern_flags,
-				unsigned long *set_kern_flags)
-{
-	return call_int_hook(sb_set_mnt_opts,
-				opts->num_mnt_opts ? -EOPNOTSUPP : 0, sb,
-				opts, kern_flags, set_kern_flags);
-}
-EXPORT_SYMBOL(security_sb_set_mnt_opts);
-
-int security_sb_clone_mnt_opts(const struct super_block *oldsb,
-				struct super_block *newsb,
-				unsigned long kern_flags,
-				unsigned long *set_kern_flags)
-{
-	return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb,
-				kern_flags, set_kern_flags);
-}
-EXPORT_SYMBOL(security_sb_clone_mnt_opts);
-
-int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
-{
-	return call_int_hook(sb_parse_opts_str, 0, options, opts);
-}
-EXPORT_SYMBOL(security_sb_parse_opts_str);
-
 int security_move_mount(const struct path *from_path, const struct path *to_path)
 {
 	return call_int_hook(move_mount, 0, from_path, to_path);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d24298b..925ede8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -103,6 +103,69 @@
 #include "audit.h"
 #include "avc_ss.h"
 
+/*
+ * Parameter option indices.  Keep this sorted so that it can be used as an
+ * index into selinux_param_keys[] to look up the option name.
+ */
+enum {
+	Opt_context,
+	Opt_defcontext,
+	Opt_fscontext,
+	Opt_rootcontext,
+	Opt_seclabel,
+	nr__selinux_params
+};
+
+static const struct fs_parameter_spec selinux_param_specs[nr__selinux_params] = {
+	[Opt_context]		= { fs_param_is_string },
+	[Opt_defcontext]	= { fs_param_is_string },
+	[Opt_fscontext]		= { fs_param_is_string },
+	[Opt_rootcontext]	= { fs_param_is_string },
+	[Opt_seclabel]		= { fs_param_is_flag },
+};
+
+static const char *const selinux_param_keys[nr__selinux_params] = {
+	[Opt_context]		= "context",
+	[Opt_defcontext]	= "defcontext",
+	[Opt_fscontext]		= "fscontext",
+	[Opt_rootcontext]	= "rootcontext",
+	[Opt_seclabel]		= "seclabel",
+};
+
+static const struct fs_parameter_description selinux_fs_parameters = {
+	.name		= "SELinux",
+	.nr_params	= nr__selinux_params,
+	.keys		= selinux_param_keys,
+	.specs		= selinux_param_specs,
+	.no_source	= true,
+};
+
+struct selinux_fs_context {
+	char		*context[nr__selinux_params];
+	union {
+		int		sid[nr__selinux_params];
+		struct {
+			u32 context_sid;
+			u32 defcontext_sid;
+			u32 fscontext_sid;
+			u32 __labelsupport;
+			u32 rootcontext_sid;
+		};
+
+	};
+	unsigned int	flags;
+};
+
+static const struct constant_table special_fs_sec[] = {
+	{ "cgroup",		SE_SBGENFS },
+	{ "cgroup2",		SE_SBGENFS },
+	{ "debugfs",		SE_SBGENFS },
+	{ "proc",		SE_SBGENFS | SE_SBPROC },
+	{ "pstore",		SE_SBGENFS },
+	{ "sysfs",		SE_SBGENFS },
+	{ "tracefs",		SE_SBGENFS },
+};
+
 struct selinux_state selinux_state;
 
 /* SECMARK reference count */
@@ -440,25 +503,15 @@ static inline int inode_doinit(struct inode *inode)
 	return inode_doinit_with_dentry(inode, NULL);
 }
 
-enum {
-	Opt_context = 0,
-	Opt_defcontext = 1,
-	Opt_fscontext = 2,
-	Opt_rootcontext = 3,
-	Opt_seclabel = 4,
-	nr__selinux_params
-};
+static void clear_selinux_fs_context(struct selinux_fs_context *opts)
+{
+	int i;
 
-#define NUM_SEL_MNT_OPTS	(nr__selinux_params - 1)
+	for (i = 0; i < nr__selinux_params; i++)
+		kfree(opts->context[i]);
+}
 
-static const match_table_t tokens = {
-	{Opt_context, CONTEXT_STR "=%s"},
-	{Opt_fscontext, FSCONTEXT_STR "=%s"},
-	{Opt_defcontext, DEFCONTEXT_STR "=%s"},
-	{Opt_rootcontext, ROOTCONTEXT_STR "=%s"},
-	{Opt_seclabel, SECLABEL_STR},
-	{-1, NULL},
-};
+#define NUM_SEL_MNT_OPTS	(nr__selinux_params)
 
 #define SEL_MOUNT_FAIL_MSG "SELinux:  duplicate or incompatible mount options\n"
 
@@ -600,15 +653,11 @@ static int sb_finish_set_opts(struct super_block *sb)
  * mount options, or whatever.
  */
 static int selinux_get_mnt_opts(const struct super_block *sb,
-				struct security_mnt_opts *opts)
+				struct selinux_fs_context *opts)
 {
-	int rc = 0, i;
 	struct superblock_security_struct *sbsec = sb->s_security;
-	char *context = NULL;
 	u32 len;
-	char tmp;
-
-	security_init_mnt_opts(opts);
+	int rc, i;
 
 	if (!(sbsec->flags & SE_SBINITIALIZED))
 		return -EINVAL;
@@ -616,96 +665,47 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
 	if (!selinux_state.initialized)
 		return -EINVAL;
 
-	tmp = sbsec->flags & SE_MNTMASK;
-	/* count the number of mount options for this sb */
-	for (i = 0; i < NUM_SEL_MNT_OPTS; i++) {
-		if (tmp & (1 << i))
-			opts->num_mnt_opts++;
-	}
-	/* Check if the Label support flag is set */
-	if (sbsec->flags & SBLABEL_MNT)
-		opts->num_mnt_opts++;
+	/* make sure we always check enough bits to cover the mask */
+	BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS));
 
-	opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
-	if (!opts->mnt_opts) {
-		rc = -ENOMEM;
-		goto out_free;
-	}
-
-	opts->mnt_opts_flags = kcalloc(opts->num_mnt_opts, sizeof(int), GFP_ATOMIC);
-	if (!opts->mnt_opts_flags) {
-		rc = -ENOMEM;
-		goto out_free;
-	}
-
-	i = 0;
-	if (sbsec->flags & FSCONTEXT_MNT) {
-		rc = security_sid_to_context(&selinux_state, sbsec->sid,
-					     &context, &len);
-		if (rc)
-			goto out_free;
-		opts->mnt_opts[i] = context;
-		opts->mnt_opts_flags[i++] = FSCONTEXT_MNT;
-	}
-	if (sbsec->flags & CONTEXT_MNT) {
-		rc = security_sid_to_context(&selinux_state,
-					     sbsec->mntpoint_sid,
-					     &context, &len);
-		if (rc)
-			goto out_free;
-		opts->mnt_opts[i] = context;
-		opts->mnt_opts_flags[i++] = CONTEXT_MNT;
-	}
-	if (sbsec->flags & DEFCONTEXT_MNT) {
-		rc = security_sid_to_context(&selinux_state, sbsec->def_sid,
-					     &context, &len);
-		if (rc)
-			goto out_free;
-		opts->mnt_opts[i] = context;
-		opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
-	}
-	if (sbsec->flags & ROOTCONTEXT_MNT) {
+	opts->flags = sbsec->flags & (SE_MNTMASK | SBLABEL_MNT);
+	if (opts->flags & FSCONTEXT_MNT)
+		opts->sid[Opt_fscontext] = sbsec->sid;
+	if (opts->flags & CONTEXT_MNT)
+		opts->sid[Opt_fscontext] = sbsec->mntpoint_sid;
+	if (opts->flags & DEFCONTEXT_MNT)
+		opts->sid[Opt_fscontext] = sbsec->def_sid;
+	if (opts->flags & ROOTCONTEXT_MNT) {
 		struct dentry *root = sbsec->sb->s_root;
 		struct inode_security_struct *isec = backing_inode_security(root);
 
-		rc = security_sid_to_context(&selinux_state, isec->sid,
-					     &context, &len);
-		if (rc)
-			goto out_free;
-		opts->mnt_opts[i] = context;
-		opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
-	}
-	if (sbsec->flags & SBLABEL_MNT) {
-		opts->mnt_opts[i] = NULL;
-		opts->mnt_opts_flags[i++] = SBLABEL_MNT;
+		opts->sid[Opt_fscontext] = isec->sid;
 	}
 
-	BUG_ON(i != opts->num_mnt_opts);
+	for (i = 0; i < nr__selinux_params; i++) {
+		if (!((1 << i) & SE_MNTMASK))
+			continue;
+		rc = security_sid_to_context(&selinux_state, opts->sid[i],
+					     &opts->context[i], &len);
+		if (rc < 0)
+			goto out_free;
+	}
 
 	return 0;
 
 out_free:
-	security_free_mnt_opts(opts);
+	clear_selinux_fs_context(opts);
 	return rc;
 }
 
 static int bad_option(struct superblock_security_struct *sbsec, char flag,
 		      u32 old_sid, u32 new_sid)
 {
-	char mnt_flags = sbsec->flags & SE_MNTMASK;
-
 	/* check if the old mount command had the same options */
 	if (sbsec->flags & SE_SBINITIALIZED)
 		if (!(sbsec->flags & flag) ||
 		    (old_sid != new_sid))
 			return 1;
-
-	/* check if we were passed the same options twice,
-	 * aka someone passed context=a,context=b
-	 */
-	if (!(sbsec->flags & SE_SBINITIALIZED))
-		if (mnt_flags & flag)
-			return 1;
 	return 0;
 }
 
@@ -713,58 +713,21 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
  * Allow filesystems with binary mount data to explicitly set mount point
  * labeling information.
  */
-static int selinux_set_mnt_opts(struct super_block *sb,
-				struct security_mnt_opts *opts,
-				unsigned long kern_flags,
-				unsigned long *set_kern_flags)
+static int selinux_set_mnt_opts(struct fs_context *fc)
 {
-	const struct cred *cred = current_cred();
-	int rc = 0, i;
-	struct superblock_security_struct *sbsec = sb->s_security;
-	const char *name = sb->s_type->name;
-	struct dentry *root = sbsec->sb->s_root;
+	struct superblock_security_struct *sbsec = fc->root->d_sb->s_security;
 	struct inode_security_struct *root_isec;
-	u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
-	u32 defcontext_sid = 0;
-	char **mount_options = opts->mnt_opts;
-	int *flags = opts->mnt_opts_flags;
-	int num_opts = opts->num_mnt_opts;
+	struct selinux_fs_context *opts = fc->security;
+	const struct cred *cred = current_cred();
+	struct dentry *root = sbsec->sb->s_root;
+	const char *name = fc->fs_type->name;
+	int rc = 0, i;
 
-	mutex_lock(&sbsec->lock);
-
-	if (!selinux_state.initialized) {
-		if (!num_opts) {
-			/* Defer initialization until selinux_complete_init,
-			   after the initial policy is loaded and the security
-			   server is ready to handle calls. */
-			goto out;
-		}
-		rc = -EINVAL;
-		pr_warn("SELinux: Unable to set superblock options "
-			"before the security server is initialized\n");
-		goto out;
-	}
-	if (kern_flags && !set_kern_flags) {
-		/* Specifying internal flags without providing a place to
-		 * place the results is not allowed */
-		rc = -EINVAL;
-		goto out;
-	}
-
-	/*
-	 * Binary mount data FS will come through this function twice.  Once
-	 * from an explicit call and once from the generic calls from the vfs.
-	 * Since the generic VFS calls will not contain any security mount data
-	 * we need to skip the double mount verification.
-	 *
-	 * This does open a hole in which we will not notice if the first
-	 * mount using this sb set explict options and a second mount using
-	 * this sb does not set any security options.  (The first options
-	 * will be used for both mounts)
-	 */
-	if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
-	    && (num_opts == 0))
-		goto out;
+	if (!selinux_state.initialized)
+		/* Defer initialization until selinux_complete_init,
+		   after the initial policy is loaded and the security
+		   server is ready to handle calls. */
+		return 0;
 
 	root_isec = backing_inode_security_novalidate(root);
 
@@ -773,94 +736,55 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 	 * also check if someone is trying to mount the same sb more
 	 * than once with different security options.
 	 */
-	for (i = 0; i < num_opts; i++) {
-		u32 sid;
+	for (i = 0; i < nr__selinux_params; i++) {
+		u32 sid = opts->sid[i];
 
-		if (flags[i] == SBLABEL_MNT)
+		if (!opts->context[i])
 			continue;
-		rc = security_context_str_to_sid(&selinux_state,
-						 mount_options[i], &sid,
-						 GFP_KERNEL);
-		if (rc) {
-			pr_warn("SELinux: security_context_str_to_sid"
-			       "(%s) failed for (dev %s, type %s) errno=%d\n",
-			       mount_options[i], sb->s_id, name, rc);
-			goto out;
-		}
-		switch (flags[i]) {
-		case FSCONTEXT_MNT:
-			fscontext_sid = sid;
 
-			if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
-					fscontext_sid))
+		switch (i) {
+		case Opt_fscontext:
+			if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
 				goto out_double_mount;
-
-			sbsec->flags |= FSCONTEXT_MNT;
 			break;
-		case CONTEXT_MNT:
-			context_sid = sid;
-
-			if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
-					context_sid))
+		case Opt_context:
+			if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
 				goto out_double_mount;
-
-			sbsec->flags |= CONTEXT_MNT;
 			break;
-		case ROOTCONTEXT_MNT:
-			rootcontext_sid = sid;
-
-			if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
-					rootcontext_sid))
+		case Opt_rootcontext:
+			if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
 				goto out_double_mount;
-
-			sbsec->flags |= ROOTCONTEXT_MNT;
-
 			break;
-		case DEFCONTEXT_MNT:
-			defcontext_sid = sid;
-
-			if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
-					defcontext_sid))
+		case Opt_defcontext:
+			if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
 				goto out_double_mount;
-
-			sbsec->flags |= DEFCONTEXT_MNT;
-
 			break;
 		default:
-			rc = -EINVAL;
-			goto out;
+			break;
 		}
 	}
 
+	sbsec->flags |= opts->flags;
+
 	if (sbsec->flags & SE_SBINITIALIZED) {
 		/* previously mounted with options, but not on this attempt? */
-		if ((sbsec->flags & SE_MNTMASK) && !num_opts)
+		if ((sbsec->flags & SE_MNTMASK) && !opts->flags)
 			goto out_double_mount;
-		rc = 0;
-		goto out;
+		return 0;
 	}
 
-	if (strcmp(sb->s_type->name, "proc") == 0)
-		sbsec->flags |= SE_SBPROC | SE_SBGENFS;
-
-	if (!strcmp(sb->s_type->name, "debugfs") ||
-	    !strcmp(sb->s_type->name, "tracefs") ||
-	    !strcmp(sb->s_type->name, "sysfs") ||
-	    !strcmp(sb->s_type->name, "pstore") ||
-	    !strcmp(sb->s_type->name, "cgroup") ||
-	    !strcmp(sb->s_type->name, "cgroup2"))
-		sbsec->flags |= SE_SBGENFS;
+	sbsec->flags |= lookup_constant(special_fs_sec, name, 0);
 
 	if (!sbsec->behavior) {
 		/*
 		 * Determine the labeling behavior to use for this
 		 * filesystem type.
 		 */
-		rc = security_fs_use(&selinux_state, sb);
+		rc = security_fs_use(&selinux_state, sbsec->sb);
 		if (rc) {
-			pr_warn("%s: security_fs_use(%s) returned %d\n",
-					__func__, sb->s_type->name, rc);
-			goto out;
+			errorf(fc, "%s: security_fs_use(%s) returned %d\n",
+			       __func__, name, rc);
+			return rc;
 		}
 	}
 
@@ -869,15 +793,16 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 	 * explicitly whitelisted, then no contexts are allowed on the command
 	 * line and security labels must be ignored.
 	 */
-	if (sb->s_user_ns != &init_user_ns &&
-	    strcmp(sb->s_type->name, "tmpfs") &&
-	    strcmp(sb->s_type->name, "ramfs") &&
-	    strcmp(sb->s_type->name, "devpts")) {
-		if (context_sid || fscontext_sid || rootcontext_sid ||
-		    defcontext_sid) {
-			rc = -EACCES;
-			goto out;
-		}
+	if (fc->user_ns != &init_user_ns &&
+	    strcmp(name, "tmpfs") != 0 &&
+	    strcmp(name, "ramfs") != 0 &&
+	    strcmp(name, "devpts") != 0) {
+		if (opts->context_sid ||
+		    opts->fscontext_sid ||
+		    opts->rootcontext_sid ||
+		    opts->defcontext_sid)
+			return -EACCES;
+
 		if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
 			sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
 			rc = security_transition_sid(&selinux_state,
@@ -886,18 +811,18 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 						     SECCLASS_FILE, NULL,
 						     &sbsec->mntpoint_sid);
 			if (rc)
-				goto out;
+				return rc;
 		}
 		goto out_set_opts;
 	}
 
 	/* sets the context of the superblock for the fs being mounted. */
-	if (fscontext_sid) {
-		rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
+	if (opts->fscontext_sid) {
+		rc = may_context_mount_sb_relabel(opts->fscontext_sid, sbsec, cred);
 		if (rc)
-			goto out;
+			return rc;
 
-		sbsec->sid = fscontext_sid;
+		sbsec->sid = opts->fscontext_sid;
 	}
 
 	/*
@@ -905,380 +830,102 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 	 * sets the label used on all file below the mountpoint, and will set
 	 * the superblock context if not already set.
 	 */
-	if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) {
+	if (fc->lsm_flags & SECURITY_LSM_NATIVE_LABELS && !opts->context_sid)
 		sbsec->behavior = SECURITY_FS_USE_NATIVE;
-		*set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
-	}
+	else
+		fc->lsm_flags &= ~SECURITY_LSM_NATIVE_LABELS;
 
-	if (context_sid) {
-		if (!fscontext_sid) {
-			rc = may_context_mount_sb_relabel(context_sid, sbsec,
-							  cred);
+	if (opts->context_sid) {
+		if (!opts->fscontext_sid) {
+			rc = may_context_mount_sb_relabel(opts->context_sid,
+							  sbsec, cred);
 			if (rc)
-				goto out;
-			sbsec->sid = context_sid;
+				return rc;
+			sbsec->sid = opts->context_sid;
 		} else {
-			rc = may_context_mount_inode_relabel(context_sid, sbsec,
-							     cred);
+			rc = may_context_mount_inode_relabel(opts->context_sid,
+							     sbsec, cred);
 			if (rc)
-				goto out;
+				return rc;
 		}
-		if (!rootcontext_sid)
-			rootcontext_sid = context_sid;
+		if (!opts->rootcontext_sid)
+			opts->rootcontext_sid = opts->context_sid;
 
-		sbsec->mntpoint_sid = context_sid;
+		sbsec->mntpoint_sid = opts->context_sid;
 		sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
 	}
 
-	if (rootcontext_sid) {
-		rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec,
-						     cred);
+	if (opts->rootcontext_sid) {
+		rc = may_context_mount_inode_relabel(opts->rootcontext_sid,
+						     sbsec, cred);
 		if (rc)
-			goto out;
+			return rc;
 
-		root_isec->sid = rootcontext_sid;
+		root_isec->sid = opts->rootcontext_sid;
 		root_isec->initialized = LABEL_INITIALIZED;
 	}
 
-	if (defcontext_sid) {
+	if (opts->defcontext_sid) {
 		if (sbsec->behavior != SECURITY_FS_USE_XATTR &&
-			sbsec->behavior != SECURITY_FS_USE_NATIVE) {
-			rc = -EINVAL;
-			pr_warn("SELinux: defcontext option is "
-			       "invalid for this filesystem type\n");
-			goto out;
-		}
+		    sbsec->behavior != SECURITY_FS_USE_NATIVE)
+			return invalf(fc, "SELinux: defcontext option is "
+				      "invalid for this filesystem type\n");
 
-		if (defcontext_sid != sbsec->def_sid) {
-			rc = may_context_mount_inode_relabel(defcontext_sid,
+		if (opts->defcontext_sid != sbsec->def_sid) {
+			rc = may_context_mount_inode_relabel(opts->defcontext_sid,
 							     sbsec, cred);
 			if (rc)
-				goto out;
+				return rc;
 		}
 
-		sbsec->def_sid = defcontext_sid;
+		sbsec->def_sid = opts->defcontext_sid;
 	}
 
 out_set_opts:
-	rc = sb_finish_set_opts(sb);
-out:
-	mutex_unlock(&sbsec->lock);
-	return rc;
+	return sb_finish_set_opts(sbsec->sb);
+
 out_double_mount:
-	rc = -EINVAL;
-	pr_warn("SELinux: mount invalid.  Same superblock, different "
-	       "security settings for (dev %s, type %s)\n", sb->s_id, name);
-	goto out;
-}
-
-static int selinux_cmp_sb_context(const struct super_block *oldsb,
-				    const struct super_block *newsb)
-{
-	struct superblock_security_struct *old = oldsb->s_security;
-	struct superblock_security_struct *new = newsb->s_security;
-	char oldflags = old->flags & SE_MNTMASK;
-	char newflags = new->flags & SE_MNTMASK;
-
-	if (oldflags != newflags)
-		goto mismatch;
-	if ((oldflags & FSCONTEXT_MNT) && old->sid != new->sid)
-		goto mismatch;
-	if ((oldflags & CONTEXT_MNT) && old->mntpoint_sid != new->mntpoint_sid)
-		goto mismatch;
-	if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid)
-		goto mismatch;
-	if (oldflags & ROOTCONTEXT_MNT) {
-		struct inode_security_struct *oldroot = backing_inode_security(oldsb->s_root);
-		struct inode_security_struct *newroot = backing_inode_security(newsb->s_root);
-		if (oldroot->sid != newroot->sid)
-			goto mismatch;
-	}
-	return 0;
-mismatch:
-	pr_warn("SELinux: mount invalid.  Same superblock, "
-			    "different security settings for (dev %s, "
-			    "type %s)\n", newsb->s_id, newsb->s_type->name);
-	return -EBUSY;
-}
-
-static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
-					struct super_block *newsb,
-					unsigned long kern_flags,
-					unsigned long *set_kern_flags)
-{
-	int rc = 0;
-	const struct superblock_security_struct *oldsbsec = oldsb->s_security;
-	struct superblock_security_struct *newsbsec = newsb->s_security;
-
-	int set_fscontext =	(oldsbsec->flags & FSCONTEXT_MNT);
-	int set_context =	(oldsbsec->flags & CONTEXT_MNT);
-	int set_rootcontext =	(oldsbsec->flags & ROOTCONTEXT_MNT);
-
-	/*
-	 * if the parent was able to be mounted it clearly had no special lsm
-	 * mount options.  thus we can safely deal with this superblock later
-	 */
-	if (!selinux_state.initialized)
-		return 0;
-
-	/*
-	 * Specifying internal flags without providing a place to
-	 * place the results is not allowed.
-	 */
-	if (kern_flags && !set_kern_flags)
-		return -EINVAL;
-
-	/* how can we clone if the old one wasn't set up?? */
-	BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
-
-	/* if fs is reusing a sb, make sure that the contexts match */
-	if (newsbsec->flags & SE_SBINITIALIZED)
-		return selinux_cmp_sb_context(oldsb, newsb);
-
-	mutex_lock(&newsbsec->lock);
-
-	newsbsec->flags = oldsbsec->flags;
-
-	newsbsec->sid = oldsbsec->sid;
-	newsbsec->def_sid = oldsbsec->def_sid;
-	newsbsec->behavior = oldsbsec->behavior;
-
-	if (newsbsec->behavior == SECURITY_FS_USE_NATIVE &&
-		!(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) {
-		rc = security_fs_use(&selinux_state, newsb);
-		if (rc)
-			goto out;
-	}
-
-	if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !set_context) {
-		newsbsec->behavior = SECURITY_FS_USE_NATIVE;
-		*set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
-	}
-
-	if (set_context) {
-		u32 sid = oldsbsec->mntpoint_sid;
-
-		if (!set_fscontext)
-			newsbsec->sid = sid;
-		if (!set_rootcontext) {
-			struct inode_security_struct *newisec = backing_inode_security(newsb->s_root);
-			newisec->sid = sid;
-		}
-		newsbsec->mntpoint_sid = sid;
-	}
-	if (set_rootcontext) {
-		const struct inode_security_struct *oldisec = backing_inode_security(oldsb->s_root);
-		struct inode_security_struct *newisec = backing_inode_security(newsb->s_root);
-
-		newisec->sid = oldisec->sid;
-	}
-
-	sb_finish_set_opts(newsb);
-out:
-	mutex_unlock(&newsbsec->lock);
-	return rc;
-}
-
-static int selinux_parse_opts_str(char *options,
-				  struct security_mnt_opts *opts)
-{
-	char *p;
-	char *context = NULL, *defcontext = NULL;
-	char *fscontext = NULL, *rootcontext = NULL;
-	int rc, num_mnt_opts = 0;
-
-	opts->num_mnt_opts = 0;
-
-	/* Standard string-based options. */
-	while ((p = strsep(&options, "|")) != NULL) {
-		int token;
-		substring_t args[MAX_OPT_ARGS];
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-
-		switch (token) {
-		case Opt_context:
-			if (context || defcontext) {
-				rc = -EINVAL;
-				pr_warn(SEL_MOUNT_FAIL_MSG);
-				goto out_err;
-			}
-			context = match_strdup(&args[0]);
-			if (!context) {
-				rc = -ENOMEM;
-				goto out_err;
-			}
-			break;
-
-		case Opt_fscontext:
-			if (fscontext) {
-				rc = -EINVAL;
-				pr_warn(SEL_MOUNT_FAIL_MSG);
-				goto out_err;
-			}
-			fscontext = match_strdup(&args[0]);
-			if (!fscontext) {
-				rc = -ENOMEM;
-				goto out_err;
-			}
-			break;
-
-		case Opt_rootcontext:
-			if (rootcontext) {
-				rc = -EINVAL;
-				pr_warn(SEL_MOUNT_FAIL_MSG);
-				goto out_err;
-			}
-			rootcontext = match_strdup(&args[0]);
-			if (!rootcontext) {
-				rc = -ENOMEM;
-				goto out_err;
-			}
-			break;
-
-		case Opt_defcontext:
-			if (context || defcontext) {
-				rc = -EINVAL;
-				pr_warn(SEL_MOUNT_FAIL_MSG);
-				goto out_err;
-			}
-			defcontext = match_strdup(&args[0]);
-			if (!defcontext) {
-				rc = -ENOMEM;
-				goto out_err;
-			}
-			break;
-		case Opt_seclabel:
-			break;
-		default:
-			rc = -EINVAL;
-			pr_warn("SELinux:  unknown mount option\n");
-			goto out_err;
-
-		}
-	}
-
-	rc = -ENOMEM;
-	opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_KERNEL);
-	if (!opts->mnt_opts)
-		goto out_err;
-
-	opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int),
-				       GFP_KERNEL);
-	if (!opts->mnt_opts_flags)
-		goto out_err;
-
-	if (fscontext) {
-		opts->mnt_opts[num_mnt_opts] = fscontext;
-		opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
-	}
-	if (context) {
-		opts->mnt_opts[num_mnt_opts] = context;
-		opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
-	}
-	if (rootcontext) {
-		opts->mnt_opts[num_mnt_opts] = rootcontext;
-		opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
-	}
-	if (defcontext) {
-		opts->mnt_opts[num_mnt_opts] = defcontext;
-		opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
-	}
-
-	opts->num_mnt_opts = num_mnt_opts;
-	return 0;
-
-out_err:
-	security_free_mnt_opts(opts);
-	kfree(context);
-	kfree(defcontext);
-	kfree(fscontext);
-	kfree(rootcontext);
-	return rc;
-}
-/*
- * string mount options parsing and call set the sbsec
- */
-static int superblock_doinit(struct super_block *sb, void *data)
-{
-	int rc = 0;
-	char *options = data;
-	struct security_mnt_opts opts;
-
-	security_init_mnt_opts(&opts);
-
-	if (!data)
-		goto out;
-
-	BUG_ON(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA);
-
-	rc = selinux_parse_opts_str(options, &opts);
-	if (rc)
-		goto out_err;
-
-out:
-	rc = selinux_set_mnt_opts(sb, &opts, 0, NULL);
-
-out_err:
-	security_free_mnt_opts(&opts);
-	return rc;
+	return invalf(fc, "SELinux: mount invalid.  Same superblock, different "
+		      "security settings for (dev %s, type %s)\n",
+		      sbsec->sb->s_id, name);
 }
 
 static void selinux_write_opts(struct seq_file *m,
-			       struct security_mnt_opts *opts)
+			       struct selinux_fs_context *opts)
 {
 	int i;
-	char *prefix;
 
-	for (i = 0; i < opts->num_mnt_opts; i++) {
-		char *has_comma;
+	for (i = 0; i < nr__selinux_params; i++) {
+		const char *prefix = selinux_param_keys[i];
+		char *context = opts->context[i];
+		bool has_comma = false;
 
-		if (opts->mnt_opts[i])
-			has_comma = strchr(opts->mnt_opts[i], ',');
-		else
-			has_comma = NULL;
-
-		switch (opts->mnt_opts_flags[i]) {
-		case CONTEXT_MNT:
-			prefix = CONTEXT_STR;
-			break;
-		case FSCONTEXT_MNT:
-			prefix = FSCONTEXT_STR;
-			break;
-		case ROOTCONTEXT_MNT:
-			prefix = ROOTCONTEXT_STR;
-			break;
-		case DEFCONTEXT_MNT:
-			prefix = DEFCONTEXT_STR;
-			break;
-		case SBLABEL_MNT:
-			seq_putc(m, ',');
-			seq_puts(m, SECLABEL_STR);
+		if (!(opts->flags & (1 << i)))
 			continue;
-		default:
-			BUG();
-			return;
-		};
+
 		/* we need a comma before each option */
 		seq_putc(m, ',');
 		seq_puts(m, prefix);
-		seq_putc(m, '=');
-		if (has_comma)
-			seq_putc(m, '\"');
-		seq_escape(m, opts->mnt_opts[i], "\"\n\\");
-		if (has_comma)
-			seq_putc(m, '\"');
+
+		if (i != Opt_seclabel) {
+			seq_putc(m, '=');
+
+			has_comma = strchr(context, ',');
+			if (has_comma)
+				seq_putc(m, '\"');
+			seq_escape(m, context, "\"\n\\");
+			if (has_comma)
+				seq_putc(m, '\"');
+		}
 	}
 }
 
 static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
 {
-	struct security_mnt_opts opts;
+	struct selinux_fs_context opts;
 	int rc;
 
+	memset(&opts, 0, sizeof(opts));
 	rc = selinux_get_mnt_opts(sb, &opts);
 	if (rc) {
 		/* before policy load we may get EINVAL, don't show anything */
@@ -1289,8 +936,7 @@ static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
 
 	selinux_write_opts(m, &opts);
 
-	security_free_mnt_opts(&opts);
-
+	clear_selinux_fs_context(&opts);
 	return rc;
 }
 
@@ -2746,97 +2392,6 @@ static void selinux_sb_free_security(struct super_block *sb)
 	superblock_free_security(sb);
 }
 
-static inline int match_prefix(char *prefix, int plen, char *option, int olen)
-{
-	if (plen > olen)
-		return 0;
-
-	return !memcmp(prefix, option, plen);
-}
-
-static inline int selinux_option(char *option, int len)
-{
-	return (match_prefix(CONTEXT_STR"=", sizeof(CONTEXT_STR)-1, option, len) ||
-		match_prefix(FSCONTEXT_STR"=", sizeof(FSCONTEXT_STR)-1, option, len) ||
-		match_prefix(DEFCONTEXT_STR"=", sizeof(DEFCONTEXT_STR)-1, option, len) ||
-		match_prefix(ROOTCONTEXT_STR"=", sizeof(ROOTCONTEXT_STR)-1, option, len) ||
-		match_prefix(SECLABEL_STR"=", sizeof(SECLABEL_STR)-1, option, len));
-}
-
-static inline void take_option(char **to, char *from, int *first, int len)
-{
-	if (!*first) {
-		**to = ',';
-		*to += 1;
-	} else
-		*first = 0;
-	memcpy(*to, from, len);
-	*to += len;
-}
-
-static inline void take_selinux_option(char **to, char *from, int *first,
-				       int len)
-{
-	int current_size = 0;
-
-	if (!*first) {
-		**to = '|';
-		*to += 1;
-	} else
-		*first = 0;
-
-	while (current_size < len) {
-		if (*from != '"') {
-			**to = *from;
-			*to += 1;
-		}
-		from += 1;
-		current_size += 1;
-	}
-}
-
-static int selinux_sb_copy_data(char *orig, size_t data_size, char *copy)
-{
-	int fnosec, fsec, rc = 0;
-	char *in_save, *in_curr, *in_end;
-	char *sec_curr, *nosec_save, *nosec;
-	int open_quote = 0;
-
-	in_curr = orig;
-	sec_curr = copy;
-
-	nosec = (char *)get_zeroed_page(GFP_KERNEL);
-	if (!nosec) {
-		rc = -ENOMEM;
-		goto out;
-	}
-
-	nosec_save = nosec;
-	fnosec = fsec = 1;
-	in_save = in_end = orig;
-
-	do {
-		if (*in_end == '"')
-			open_quote = !open_quote;
-		if ((*in_end == ',' && open_quote == 0) ||
-				*in_end == '\0') {
-			int len = in_end - in_curr;
-
-			if (selinux_option(in_curr, len))
-				take_selinux_option(&sec_curr, in_curr, &fsec, len);
-			else
-				take_option(&nosec, in_curr, &fnosec, len);
-
-			in_curr = in_end + 1;
-		}
-	} while (*in_end++);
-
-	strcpy(in_save, nosec_save);
-	free_page((unsigned long)nosec_save);
-out:
-	return rc;
-}
-
 static int selinux_sb_statfs(struct dentry *dentry)
 {
 	const struct cred *cred = current_cred();
@@ -2876,7 +2431,13 @@ static int selinux_umount(struct vfsmount *mnt, int flags)
 static int selinux_fs_context_alloc(struct fs_context *fc,
 				    struct dentry *reference)
 {
-	struct security_mnt_opts *opts;
+	struct selinux_fs_context *opts;
+
+	BUILD_BUG_ON(CONTEXT_MNT	!= (1 << Opt_context) ||
+		     DEFCONTEXT_MNT	!= (1 << Opt_defcontext) ||
+		     FSCONTEXT_MNT	!= (1 << Opt_fscontext) ||
+		     SBLABEL_MNT	!= (1 << Opt_seclabel) ||
+		     ROOTCONTEXT_MNT	!= (1 << Opt_rootcontext));
 
 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
 	if (!opts)
@@ -2889,151 +2450,85 @@ static int selinux_fs_context_alloc(struct fs_context *fc,
 static int selinux_fs_context_dup(struct fs_context *fc,
 				  struct fs_context *src_fc)
 {
-	const struct security_mnt_opts *src = src_fc->security;
-	struct security_mnt_opts *opts;
-	int i, n;
+	const struct selinux_fs_context *src = src_fc->security;
+	struct selinux_fs_context *opts;
+	int i;
 
 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
 	if (!opts)
 		return -ENOMEM;
 	fc->security = opts;
 
-	if (!src || !src->num_mnt_opts)
-		return 0;
-	n = opts->num_mnt_opts = src->num_mnt_opts;
-
-	if (src->mnt_opts) {
-		opts->mnt_opts = kcalloc(n, sizeof(char *), GFP_KERNEL);
-		if (!opts->mnt_opts)
+	for (i = 0; i < nr__selinux_params; i++) {
+		if (!src->context[i])
+			continue;
+		opts->context[i] = kstrdup(src->context[i], GFP_KERNEL);
+		if (!opts->context[i])
 			return -ENOMEM;
-
-		for (i = 0; i < n; i++) {
-			if (src->mnt_opts[i]) {
-				opts->mnt_opts[i] = kstrdup(src->mnt_opts[i],
-							    GFP_KERNEL);
-				if (!opts->mnt_opts[i])
-					return -ENOMEM;
-			}
-		}
+		opts->sid[i] = src->sid[i];
 	}
 
-	if (src->mnt_opts_flags) {
-		opts->mnt_opts_flags = kmemdup(src->mnt_opts_flags,
-					       n * sizeof(int), GFP_KERNEL);
-		if (!opts->mnt_opts_flags)
-			return -ENOMEM;
-	}
-
+	opts->flags = src->flags;
 	return 0;
 }
 
 static void selinux_fs_context_free(struct fs_context *fc)
 {
-	struct security_mnt_opts *opts = fc->security;
+	struct selinux_fs_context *opts = fc->security;
 
 	if (opts) {
-		security_free_mnt_opts(opts);
+		clear_selinux_fs_context(opts);
 		fc->security = NULL;
 	}
 }
 
-static const struct fs_parameter_spec selinux_param_specs[nr__selinux_params] = {
-	[Opt_context]		= { fs_param_is_string },
-	[Opt_defcontext]	= { fs_param_is_string },
-	[Opt_fscontext]		= { fs_param_is_string },
-	[Opt_rootcontext]	= { fs_param_is_string },
-	[Opt_seclabel]		= { fs_param_is_flag },
-};
-
-static const char *const selinux_param_keys[nr__selinux_params] = {
-	[Opt_context]		= CONTEXT_STR,
-	[Opt_defcontext]	= DEFCONTEXT_STR,
-	[Opt_fscontext]		= FSCONTEXT_STR,
-	[Opt_rootcontext]	= ROOTCONTEXT_STR,
-	[Opt_seclabel]		= SECLABEL_STR,
-};
-
-static const struct fs_parameter_description selinux_fs_parameters = {
-	.name		= "SELinux",
-	.nr_params	= nr__selinux_params,
-	.keys		= selinux_param_keys,
-	.specs		= selinux_param_specs,
-	.no_source	= true,
-};
-
+/*
+ * Parse a superblock configuration parameter to see if it belongs to or is
+ * relevant to SELinux.  We return 1 to claim it.
+ */
 static int selinux_fs_context_parse_param(struct fs_context *fc,
 					  struct fs_parameter *param)
 {
-	struct security_mnt_opts *opts = fc->security;
+	struct selinux_fs_context *opts = fc->security;
 	struct fs_parse_result result;
-	unsigned int have;
-	char **oo;
-	int opt, ctx, i, *of;
+	int opt;
 
 	opt = fs_parse(fc, &selinux_fs_parameters, param, &result);
 	if (opt < 0)
 		return opt;
 
-	have = 0;
-	for (i = 0; i < opts->num_mnt_opts; i++)
-		have |= 1 << opts->mnt_opts_flags[i];
-	if (have & (1 << opt))
-		return -EINVAL;
+	if (opts->context[opt])
+		return invalf(fc, "SELinux: Duplicate %s specification",
+			      param->key);
 
 	switch (opt) {
 	case Opt_context:
-		if (have & (1 << Opt_defcontext))
-			goto incompatible;
-		ctx = CONTEXT_MNT;
-		goto copy_context_string;
+		if (opts->context[Opt_defcontext])
+			return invalf(fc, "SELinux: context not compatible with defcontext");
+		goto save_context_string;
 
 	case Opt_fscontext:
-		ctx = FSCONTEXT_MNT;
-		goto copy_context_string;
-
 	case Opt_rootcontext:
-		ctx = ROOTCONTEXT_MNT;
-		goto copy_context_string;
+		goto save_context_string;
 
 	case Opt_defcontext:
-		if (have & (1 << Opt_context))
-			goto incompatible;
-		ctx = DEFCONTEXT_MNT;
-		goto copy_context_string;
+		if (opts->context[Opt_context])
+			return invalf(fc, "SELinux: defcontext not compatible with context");
+		goto save_context_string;
 
 	case Opt_seclabel:
+		opts->flags |= SBLABEL_MNT;
 		return 1;
 
 	default:
 		return -EINVAL;
 	}
 
-copy_context_string:
-	if (opts->num_mnt_opts > 3)
-		return -EINVAL;
-
-	of = krealloc(opts->mnt_opts_flags,
-		      (opts->num_mnt_opts + 1) * sizeof(int), GFP_KERNEL);
-	if (!of)
-		return -ENOMEM;
-	of[opts->num_mnt_opts] = 0;
-	opts->mnt_opts_flags = of;
-
-	oo = krealloc(opts->mnt_opts,
-		      (opts->num_mnt_opts + 1) * sizeof(char *), GFP_KERNEL);
-	if (!oo)
-		return -ENOMEM;
-	oo[opts->num_mnt_opts] = NULL;
-	opts->mnt_opts = oo;
-
-	opts->mnt_opts[opts->num_mnt_opts] = param->string;
-	opts->mnt_opts_flags[opts->num_mnt_opts] = ctx;
-	opts->num_mnt_opts++;
+save_context_string:
+	opts->flags |= 1 << opt;
+	opts->context[result.key] = param->string;
 	param->string = NULL;
 	return 1;
-
-incompatible:
-	return -EINVAL;
 }
 
 /*
@@ -3042,81 +2537,99 @@ static int selinux_fs_context_parse_param(struct fs_context *fc,
  */
 static int selinux_validate_for_sb_reconfigure(struct fs_context *fc)
 {
+	struct selinux_fs_context *opts = fc->security;
 	struct super_block *sb = fc->root->d_sb;
 	struct superblock_security_struct *sbsec = sb->s_security;
-	struct security_mnt_opts *opts = fc->security;
-	int rc, i, *flags;
-	char **mount_options;
+	struct inode_security_struct *root_isec;
+	int i;
 
 	if (!(sbsec->flags & SE_SBINITIALIZED))
 		return 0;
 
-	mount_options = opts->mnt_opts;
-	flags = opts->mnt_opts_flags;
+	if ((opts->flags & ~sbsec->flags) & (SE_MNTMASK | SBLABEL_MNT))
+		goto cant_change;
+	
+	for (i = 0; i < nr__selinux_params; i++) {
+		const char *ctx = opts->context[i];
+		u32 sid = opts->sid[i];
 
-	for (i = 0; i < opts->num_mnt_opts; i++) {
-		u32 sid;
-
-		if (flags[i] == SBLABEL_MNT)
+		if (!ctx)
 			continue;
 
-		rc = security_context_str_to_sid(&selinux_state, mount_options[i],
-						 &sid, GFP_KERNEL);
-		if (rc) {
-			pr_warn("SELinux: security_context_str_to_sid"
-				"(%s) failed for (dev %s, type %s) errno=%d\n",
-				mount_options[i], sb->s_id, sb->s_type->name, rc);
-			goto inval;
-		}
-
-		switch (flags[i]) {
-		case FSCONTEXT_MNT:
-			if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
-				goto bad_option;
+		switch (i) {
+		case Opt_fscontext:
+			if (sid != sbsec->sid)
+				goto cant_change;
 			break;
-		case CONTEXT_MNT:
-			if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
-				goto bad_option;
+		case Opt_context:
+			if (sid != sbsec->mntpoint_sid)
+				goto cant_change;
 			break;
-		case ROOTCONTEXT_MNT: {
-			struct inode_security_struct *root_isec;
+		case Opt_rootcontext:
 			root_isec = backing_inode_security(sb->s_root);
-
-			if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
-				goto bad_option;
+			if (sid != root_isec->sid)
+				goto cant_change;
 			break;
-		}
-		case DEFCONTEXT_MNT:
-			if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
-				goto bad_option;
+		case Opt_defcontext:
+			if (sid != sbsec->def_sid)
+				goto cant_change;
 			break;
 		default:
-			goto inval;
+			return -EINVAL;
 		}
 	}
 
-	rc = 0;
-out:
-	return rc;
+	return 0;
 
-bad_option:
-	pr_warn("SELinux: unable to change security options "
-		"during remount (dev %s, type=%s)\n",
-		sb->s_id, sb->s_type->name);
-inval:
-	rc = -EINVAL;
-	goto out;
+cant_change:
+	return invalf(fc, "SELinux: unable to change security options "
+		      "during remount (dev %s, type=%s)",
+		      sb->s_id, sb->s_type->name);
 }
 
 /*
- * Validate the security context assembled from the option data supplied to
- * mount.
+ * Validate the security context assembled from the parameters supplied to
+ * mount and convert any text labels into sids.
  */
 static int selinux_fs_context_validate(struct fs_context *fc)
 {
-	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)
+	struct selinux_fs_context *opts = fc->security;
+	int rc, i;
+
+	if (!opts || !opts->flags)
+		return 0;
+
+	if (!selinux_state.initialized) {
+		pr_warn("SELinux: Unable to set superblock options "
+			"before the security server is initialized\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < nr__selinux_params; i++) {
+		const char *ctx = opts->context[i];
+		u32 sid;
+
+		if (!ctx)
+			continue;
+
+		rc = security_context_str_to_sid(&selinux_state, ctx, &sid,
+						 GFP_KERNEL);
+		if (rc)
+			goto sid_not_found;
+	}
+
+	switch (fc->purpose) {
+	case FS_CONTEXT_FOR_RECONFIGURE:
 		return selinux_validate_for_sb_reconfigure(fc);
-	return 0;
+	default:
+		return 0;
+	}
+
+sid_not_found:
+	return invalf(fc, "SELinux: security_context_str_to_sid"
+		      "(%s) failed for (dev %s, type %s) errno=%d",
+		      selinux_param_keys[i],
+		      fc->root->d_sb->s_id, fc->fs_type->name, rc);
 }
 
 /*
@@ -3124,11 +2637,14 @@ static int selinux_fs_context_validate(struct fs_context *fc)
  */
 static int selinux_sb_get_tree(struct fs_context *fc)
 {
-	const struct cred *cred = current_cred();
+	struct superblock_security_struct *sbsec = fc->root->d_sb->s_security;
 	struct common_audit_data ad;
+	const struct cred *cred = current_cred();
 	int rc;
 
-	rc = selinux_set_mnt_opts(fc->root->d_sb, fc->security, 0, NULL);
+	mutex_lock(&sbsec->lock);
+	rc = selinux_set_mnt_opts(fc);
+	mutex_unlock(&sbsec->lock);
 	if (rc)
 		return rc;
 
@@ -7109,9 +6625,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(sb_statfs, selinux_sb_statfs),
 	LSM_HOOK_INIT(sb_mount, selinux_mount),
 	LSM_HOOK_INIT(sb_umount, selinux_umount),
-	LSM_HOOK_INIT(sb_set_mnt_opts, selinux_set_mnt_opts),
-	LSM_HOOK_INIT(sb_clone_mnt_opts, selinux_sb_clone_mnt_opts),
-	LSM_HOOK_INIT(sb_parse_opts_str, selinux_parse_opts_str),
 
 	LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security),
 	LSM_HOOK_INIT(dentry_create_files_as, selinux_dentry_create_files_as),
@@ -7372,9 +6885,78 @@ static __init int selinux_init(void)
 	return 0;
 }
 
+/*
+ * string mount options parsing and call set the sbsec
+ */
+static int init_one_superblock(struct super_block *sb)
+{
+	struct superblock_security_struct *sbsec = sb->s_security;
+	const char *name = sb->s_type->name;
+	int rc = 0;
+
+	if (!selinux_state.initialized)
+		/* Defer initialization until selinux_complete_init,
+		   after the initial policy is loaded and the security
+		   server is ready to handle calls. */
+		return 0;
+
+	if (sbsec->flags & SE_SBINITIALIZED) {
+		if ((sbsec->flags & SE_MNTMASK))
+			goto out_double_mount;
+		return 0;
+	}
+
+	sbsec->flags |= lookup_constant(special_fs_sec, sb->s_type->name, 0);
+
+	if (!sbsec->behavior) {
+		/*
+		 * Determine the labeling behavior to use for this
+		 * filesystem type.
+		 */
+		rc = security_fs_use(&selinux_state, sb);
+		if (rc) {
+			pr_warn("%s: security_fs_use(%s) returned %d\n",
+				__func__, sb->s_type->name, rc);
+			return rc;
+		}
+	}
+
+	/*
+	 * If this is a user namespace mount and the filesystem type is not
+	 * explicitly whitelisted, then no contexts are allowed on the command
+	 * line and security labels must be ignored.
+	 */
+	if (sb->s_user_ns != &init_user_ns &&
+	    strcmp(sb->s_type->name, "tmpfs") != 0 &&
+	    strcmp(sb->s_type->name, "ramfs") != 0 &&
+	    strcmp(sb->s_type->name, "devpts") != 0) {
+		if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
+			sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+			rc = security_transition_sid(&selinux_state,
+						     current_sid(),
+						     current_sid(),
+						     SECCLASS_FILE, NULL,
+						     &sbsec->mntpoint_sid);
+			if (rc)
+				return rc;
+		}
+	}
+
+	return sb_finish_set_opts(sb);
+
+out_double_mount:
+	pr_warn("SELinux: mount invalid.  Same superblock, different "
+	       "security settings for (dev %s, type %s)\n", sb->s_id, name);
+	return -EINVAL;
+}
+
 static void delayed_superblock_init(struct super_block *sb, void *unused)
 {
-	superblock_doinit(sb, NULL);
+	struct superblock_security_struct *sbsec = sb->s_security;
+
+	mutex_lock(&sbsec->lock);
+	init_one_superblock(sb);
+	mutex_unlock(&sbsec->lock);
 }
 
 void selinux_complete_init(void)
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 7c10028..829dd85 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -47,24 +47,23 @@
 
 /* Mask for just the mount related flags */
 #define SE_MNTMASK	0x0f
-/* Super block security struct flags for mount options */
-/* BE CAREFUL, these need to be the low order bits for selinux_get_mnt_opts */
+
+/*
+ * Super block security struct flags for mount options.  These must
+ * match the Opt_* enum values so that those can be bit-shifted to
+ * these.
+ */
 #define CONTEXT_MNT	0x01
 #define DEFCONTEXT_MNT	0x02
 #define FSCONTEXT_MNT	0x04
 #define ROOTCONTEXT_MNT	0x08
 #define SBLABEL_MNT	0x10
+
 /* Non-mount related flags */
 #define SE_SBINITIALIZED	0x0100
 #define SE_SBPROC		0x0200
 #define SE_SBGENFS		0x0400
 
-#define CONTEXT_STR	"context"
-#define FSCONTEXT_STR	"fscontext"
-#define ROOTCONTEXT_STR	"rootcontext"
-#define DEFCONTEXT_STR	"defcontext"
-#define SECLABEL_STR	"seclabel"
-
 struct netlbl_lsm_secattr;
 
 extern int selinux_enabled;
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 891a307..c1e4771 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -191,27 +191,6 @@ struct smack_known_list_elem {
 #define FSROOT_MNT	0x08
 #define FSTRANS_MNT	0x10
 
-#define NUM_SMK_MNT_OPTS	5
-
-enum {
-	Opt_error = -1,
-	Opt_fsdefault = 0,
-	Opt_fsfloor = 1,
-	Opt_fshat = 2,
-	Opt_fsroot = 3,
-	Opt_fstransmute = 4,
-        nr__smack_params
-};
-
-/*
- * Mount options
- */
-#define SMK_FSDEFAULT	"smackfsdef"
-#define SMK_FSFLOOR	"smackfsfloor"
-#define SMK_FSHAT	"smackfshat"
-#define SMK_FSROOT	"smackfsroot"
-#define SMK_FSTRANS	"smackfstransmute"
-
 #define SMACK_DELETE_OPTION	"-DELETE"
 #define SMACK_CIPSO_OPTION 	"-CIPSO"
 
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 1bf6e6e..f82a39f 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -54,6 +54,15 @@
 #define SMK_RECEIVING	1
 #define SMK_SENDING	2
 
+enum {
+	Opt_fsdefault = 0,
+	Opt_fsfloor = 1,
+	Opt_fshat = 2,
+	Opt_fsroot = 3,
+	Opt_fstransmute = 4,
+        nr__smack_params
+};
+
 #ifdef SMACK_IPV6_PORT_LABELING
 DEFINE_MUTEX(smack_ipv6_lock);
 static LIST_HEAD(smk_ipv6_port_list);
@@ -61,15 +70,6 @@ static LIST_HEAD(smk_ipv6_port_list);
 static struct kmem_cache *smack_inode_cache;
 int smack_enabled;
 
-static const match_table_t smk_mount_tokens = {
-	{Opt_fsdefault, SMK_FSDEFAULT "=%s"},
-	{Opt_fsfloor, SMK_FSFLOOR "=%s"},
-	{Opt_fshat, SMK_FSHAT "=%s"},
-	{Opt_fsroot, SMK_FSROOT "=%s"},
-	{Opt_fstransmute, SMK_FSTRANS "=%s"},
-	{Opt_error, NULL},
-};
-
 #ifdef CONFIG_SECURITY_SMACK_BRINGUP
 static char *smk_bu_mess[] = {
 	"Bringup Error",	/* Unused */
@@ -539,8 +539,7 @@ struct smack_fs_context {
 			char		*fsroot;
 			char		*fstransmute;
 		};
-		char			*ptrs[5];
-
+		char			*labels[nr__smack_params];
 	};
 	struct superblock_smack		*sbsp;
 	struct inode_smack		*isp;
@@ -557,8 +556,8 @@ static void smack_fs_context_free(struct fs_context *fc)
 	int i;
 
 	if (ctx) {
-		for (i = 0; i < ARRAY_SIZE(ctx->ptrs); i++)
-			kfree(ctx->ptrs[i]);
+		for (i = 0; i < ARRAY_SIZE(ctx->labels); i++)
+			kfree(ctx->labels[i]);
 		kfree(ctx->isp);
 		kfree(ctx->sbsp);
 		kfree(ctx);
@@ -643,10 +642,10 @@ static int smack_fs_context_dup(struct fs_context *fc,
 	if (!dst->sbsp)
 		goto nomem_free;
 
-	for (i = 0; i < ARRAY_SIZE(dst->ptrs); i++) {
-		if (src->ptrs[i]) {
-			dst->ptrs[i] = kstrdup(src->ptrs[i], GFP_KERNEL);
-			if (!dst->ptrs[i])
+	for (i = 0; i < ARRAY_SIZE(dst->labels); i++) {
+		if (src->labels[i]) {
+			dst->labels[i] = kstrdup(src->labels[i], GFP_KERNEL);
+			if (!dst->labels[i])
 				goto nomem_free;
 		}
 	}
@@ -668,11 +667,11 @@ static const struct fs_parameter_spec smack_param_specs[nr__smack_params] = {
 };
 
 static const char *const smack_param_keys[nr__smack_params] = {
-	[Opt_fsdefault]		= SMK_FSDEFAULT,
-	[Opt_fsfloor]		= SMK_FSFLOOR,
-	[Opt_fshat]		= SMK_FSHAT,
-	[Opt_fsroot]		= SMK_FSROOT,
-	[Opt_fstransmute]	= SMK_FSTRANS,
+	[Opt_fsdefault]		= "smackfsdef",
+	[Opt_fsfloor]		= "smackfsfloor",
+	[Opt_fshat]		= "smackfshat",
+	[Opt_fsroot]		= "smackfsroot",
+	[Opt_fstransmute]	= "smackfstransmute",
 };
 
 static const struct fs_parameter_description smack_fs_parameters = {
@@ -705,41 +704,12 @@ static int smack_fs_context_parse_param(struct fs_context *fc,
 	if (opt < 0)
 		return opt;
 
-	switch (opt) {
-	case Opt_fsdefault:
-		if (ctx->fsdefault)
-			goto error_dup;
-		ctx->fsdefault = param->string;
-		break;
-	case Opt_fsfloor:
-		if (ctx->fsfloor)
-			goto error_dup;
-		ctx->fsfloor = param->string;
-		break;
-	case Opt_fshat:
-		if (ctx->fshat)
-			goto error_dup;
-		ctx->fshat = param->string;
-		break;
-	case Opt_fsroot:
-		if (ctx->fsroot)
-			goto error_dup;
-		ctx->fsroot = param->string;
-		break;
-	case Opt_fstransmute:
-		if (ctx->fstransmute)
-			goto error_dup;
-		ctx->fstransmute = param->string;
-		break;
-	default:
-		return invalf(fc, "Smack:  unknown mount option\n");
-	}
+	if (ctx->labels[opt])
+		return invalf(fc, "Smack: duplicate mount option\n");
 
+	ctx->labels[opt] = param->string;
 	param->string = NULL;
 	return 0;
-
-error_dup:
-	return invalf(fc, "Smack: duplicate mount option\n");
 }
 
 /**
@@ -883,242 +853,6 @@ static void smack_sb_free_security(struct super_block *sb)
 }
 
 /**
- * smack_parse_opts_str - parse Smack specific mount options
- * @options: mount options string
- * @opts: where to store converted mount opts
- *
- * Returns 0 on success or -ENOMEM on error.
- *
- * converts Smack specific mount options to generic security option format
- */
-static int smack_parse_opts_str(char *options,
-		struct security_mnt_opts *opts)
-{
-	char *p;
-	char *fsdefault = NULL;
-	char *fsfloor = NULL;
-	char *fshat = NULL;
-	char *fsroot = NULL;
-	char *fstransmute = NULL;
-	int rc = -ENOMEM;
-	int num_mnt_opts = 0;
-	int token;
-
-	opts->num_mnt_opts = 0;
-
-	if (!options)
-		return 0;
-
-	while ((p = strsep(&options, ",")) != NULL) {
-		substring_t args[MAX_OPT_ARGS];
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, smk_mount_tokens, args);
-
-		switch (token) {
-		case Opt_fsdefault:
-			if (fsdefault)
-				goto out_opt_err;
-			fsdefault = match_strdup(&args[0]);
-			if (!fsdefault)
-				goto out_err;
-			break;
-		case Opt_fsfloor:
-			if (fsfloor)
-				goto out_opt_err;
-			fsfloor = match_strdup(&args[0]);
-			if (!fsfloor)
-				goto out_err;
-			break;
-		case Opt_fshat:
-			if (fshat)
-				goto out_opt_err;
-			fshat = match_strdup(&args[0]);
-			if (!fshat)
-				goto out_err;
-			break;
-		case Opt_fsroot:
-			if (fsroot)
-				goto out_opt_err;
-			fsroot = match_strdup(&args[0]);
-			if (!fsroot)
-				goto out_err;
-			break;
-		case Opt_fstransmute:
-			if (fstransmute)
-				goto out_opt_err;
-			fstransmute = match_strdup(&args[0]);
-			if (!fstransmute)
-				goto out_err;
-			break;
-		default:
-			rc = -EINVAL;
-			pr_warn("Smack:  unknown mount option\n");
-			goto out_err;
-		}
-	}
-
-	opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL);
-	if (!opts->mnt_opts)
-		goto out_err;
-
-	opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
-			GFP_KERNEL);
-	if (!opts->mnt_opts_flags)
-		goto out_err;
-
-	if (fsdefault) {
-		opts->mnt_opts[num_mnt_opts] = fsdefault;
-		opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT;
-	}
-	if (fsfloor) {
-		opts->mnt_opts[num_mnt_opts] = fsfloor;
-		opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT;
-	}
-	if (fshat) {
-		opts->mnt_opts[num_mnt_opts] = fshat;
-		opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT;
-	}
-	if (fsroot) {
-		opts->mnt_opts[num_mnt_opts] = fsroot;
-		opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT;
-	}
-	if (fstransmute) {
-		opts->mnt_opts[num_mnt_opts] = fstransmute;
-		opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT;
-	}
-
-	opts->num_mnt_opts = num_mnt_opts;
-	return 0;
-
-out_opt_err:
-	rc = -EINVAL;
-	pr_warn("Smack: duplicate mount options\n");
-
-out_err:
-	kfree(fsdefault);
-	kfree(fsfloor);
-	kfree(fshat);
-	kfree(fsroot);
-	kfree(fstransmute);
-	return rc;
-}
-
-/**
- * smack_set_mnt_opts - set Smack specific mount options
- * @sb: the file system superblock
- * @opts: Smack mount options
- * @kern_flags: mount option from kernel space or user space
- * @set_kern_flags: where to store converted mount opts
- *
- * Returns 0 on success, an error code on failure
- *
- * Allow filesystems with binary mount data to explicitly set Smack mount
- * labels.
- */
-static int smack_set_mnt_opts(struct super_block *sb,
-		struct security_mnt_opts *opts,
-		unsigned long kern_flags,
-		unsigned long *set_kern_flags)
-{
-	struct dentry *root = sb->s_root;
-	struct inode *inode = d_backing_inode(root);
-	struct superblock_smack *sp = sb->s_security;
-	struct inode_smack *isp;
-	struct smack_known *skp;
-	int i;
-	int num_opts = opts->num_mnt_opts;
-	int transmute = 0;
-
-	if (sp->smk_flags & SMK_SB_INITIALIZED)
-		return 0;
-
-	if (!smack_privileged(CAP_MAC_ADMIN)) {
-		/*
-		 * Unprivileged mounts don't get to specify Smack values.
-		 */
-		if (num_opts)
-			return -EPERM;
-		/*
-		 * Unprivileged mounts get root and default from the caller.
-		 */
-		skp = smk_of_current();
-		sp->smk_root = skp;
-		sp->smk_default = skp;
-		/*
-		 * For a handful of fs types with no user-controlled
-		 * backing store it's okay to trust security labels
-		 * in the filesystem. The rest are untrusted.
-		 */
-		if (sb->s_user_ns != &init_user_ns &&
-		    sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
-		    sb->s_magic != RAMFS_MAGIC) {
-			transmute = 1;
-			sp->smk_flags |= SMK_SB_UNTRUSTED;
-		}
-	}
-
-	sp->smk_flags |= SMK_SB_INITIALIZED;
-
-	for (i = 0; i < num_opts; i++) {
-		switch (opts->mnt_opts_flags[i]) {
-		case FSDEFAULT_MNT:
-			skp = smk_import_entry(opts->mnt_opts[i], 0);
-			if (IS_ERR(skp))
-				return PTR_ERR(skp);
-			sp->smk_default = skp;
-			break;
-		case FSFLOOR_MNT:
-			skp = smk_import_entry(opts->mnt_opts[i], 0);
-			if (IS_ERR(skp))
-				return PTR_ERR(skp);
-			sp->smk_floor = skp;
-			break;
-		case FSHAT_MNT:
-			skp = smk_import_entry(opts->mnt_opts[i], 0);
-			if (IS_ERR(skp))
-				return PTR_ERR(skp);
-			sp->smk_hat = skp;
-			break;
-		case FSROOT_MNT:
-			skp = smk_import_entry(opts->mnt_opts[i], 0);
-			if (IS_ERR(skp))
-				return PTR_ERR(skp);
-			sp->smk_root = skp;
-			break;
-		case FSTRANS_MNT:
-			skp = smk_import_entry(opts->mnt_opts[i], 0);
-			if (IS_ERR(skp))
-				return PTR_ERR(skp);
-			sp->smk_root = skp;
-			transmute = 1;
-			break;
-		default:
-			break;
-		}
-	}
-
-	/*
-	 * Initialize the root inode.
-	 */
-	isp = inode->i_security;
-	if (isp == NULL) {
-		isp = new_inode_smack(sp->smk_root);
-		if (isp == NULL)
-			return -ENOMEM;
-		inode->i_security = isp;
-	} else
-		isp->smk_inode = sp->smk_root;
-
-	if (transmute)
-		isp->smk_flags |= SMK_INODE_TRANSMUTE;
-
-	return 0;
-}
-
-/**
  * smack_sb_statfs - Smack check on statfs
  * @dentry: identifies the file system in question
  *
@@ -4912,8 +4646,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
 	LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
 	LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
-	LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
-	LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str),
 
 	LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
 
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index e637ce7..2bf22f3 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -975,8 +975,7 @@ int tomoyo_mount_permission_fc(struct fs_context *fc,
 			       const struct path *mountpoint,
 			       unsigned int mnt_flags);
 int tomoyo_mount_permission(const char *dev_name, const struct path *path,
-			    const char *type, unsigned long flags,
-			    void *data_page);
+			    const char *type, unsigned long flags);
 int tomoyo_open_control(const u8 type, struct file *file);
 int tomoyo_path2_perm(const u8 operation, const struct path *path1,
 		      const struct path *path2);
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 9ec84ab..1543f01 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -188,8 +188,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
  * Returns 0 on success, negative value otherwise.
  */
 int tomoyo_mount_permission(const char *dev_name, const struct path *path,
-			    const char *type, unsigned long flags,
-			    void *data_page)
+			    const char *type, unsigned long flags)
 {
 	struct tomoyo_request_info r;
 	int error;
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index dcb7980..a9729f6 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -421,7 +421,7 @@ static int tomoyo_sb_mount(const char *dev_name, const struct path *path,
 			   const char *type, unsigned long flags,
 			   void *data, size_t data_size)
 {
-	return tomoyo_mount_permission(dev_name, path, type, flags, data);
+	return tomoyo_mount_permission(dev_name, path, type, flags);
 }
 
 /**