All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: viro@zeniv.linux.org.uk
Cc: linux-fsdevel@vger.kernel.org, dhowells@redhat.com,
	linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org,
	mszeredi@redhat.com
Subject: [PATCH 3/9] VFS: Introduce a mount context
Date: Wed, 03 May 2017 17:04:59 +0100	[thread overview]
Message-ID: <149382749941.30481.11685229083280551867.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <149382747487.30481.15428192741961545429.stgit@warthog.procyon.org.uk>

Introduce a mount context concept.  This is allocated at the beginning of
the mount procedure and into it is placed:

 (1) Filesystem type.

 (2) Namespaces.

 (3) Device name.

 (4) Superblock flags (MS_*) and mount flags (MNT_*).

 (5) Security details.

 (6) Filesystem-specific data, as set by the mount options.

It also gives a place in which to hang an error message for later retrieval
(see the mount-by-fd syscall later in this series).

Rather than calling fs_type->mount(), a mount_context struct is created and
fs_type->fsopen() is called to set it up.  fs_type->mc_size says how much
should be added on to the mount context for the filesystem's use.

A set of operations have to be set by ->fsopen() to provide freeing,
duplication, option parsing, binary data parsing, validation, mounting and
superblock filling.

It should be noted that, whilst this patch adds a lot of lines of code,
there is quite a bit of duplication with existing code that can be
eliminated should all filesystems be converted over.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 Documentation/filesystems/mounting.txt |  445 ++++++++++++++++++++++++++++++++
 fs/Makefile                            |    3 
 fs/internal.h                          |    2 
 fs/mount.h                             |    3 
 fs/mount_context.c                     |  343 +++++++++++++++++++++++++
 fs/namespace.c                         |  270 +++++++++++++++++--
 fs/super.c                             |   50 +++-
 include/linux/fs.h                     |   11 +
 include/linux/lsm_hooks.h              |   37 +++
 include/linux/mount.h                  |   67 +++++
 include/linux/security.h               |   29 ++
 security/security.c                    |   32 ++
 security/selinux/hooks.c               |  179 +++++++++++++
 13 files changed, 1435 insertions(+), 36 deletions(-)
 create mode 100644 Documentation/filesystems/mounting.txt
 create mode 100644 fs/mount_context.c

diff --git a/Documentation/filesystems/mounting.txt b/Documentation/filesystems/mounting.txt
new file mode 100644
index 000000000000..a942ccd08376
--- /dev/null
+++ b/Documentation/filesystems/mounting.txt
@@ -0,0 +1,445 @@
+			      ===================
+			      FILESYSTEM MOUNTING
+			      ===================
+
+CONTENTS
+
+ (1) Overview.
+
+ (2) The mount context.
+
+ (3) The mount context operations.
+
+ (4) Mount context security.
+
+ (5) VFS mount context operations.
+
+
+========
+OVERVIEW
+========
+
+The creation of new mounts is now to be done in a multistep process:
+
+ (1) Create a mount context.
+
+ (2) Parse the options and attach them to the mount context.  Options may be
+     passed individually from userspace.
+
+ (3) Validate and pre-process the mount context.
+
+ (4) Perform the mount.
+
+ (5) Return an error message attached to the mount context.
+
+ (6) Destroy the mount context.
+
+To support this, the file_system_type struct gains two new fields:
+
+	unsigned short mc_size;
+
+which indicates how much space the filesystem would like tacked onto the end of
+the mount_context struct for its own purposes, and:
+
+	int (*fsopen)(struct mount_context *mc, struct super_block *src_sb);
+
+which is invoked to set up the filesystem-specific parts of a mount context,
+including the additional space.  The src_sb parameter is used to convey the
+superblock from which the filesystem may draw extra information (such as
+namespaces), for submount (MS_SUBMOUNT) or remount (MS_REMOUNT) purposes or it
+will be NULL.
+
+Note that security initialisation is done *after* the filesystem is called so
+that the namespaces may be adjusted first.
+
+And the super_operations struct gains one:
+
+	int (*remount_fs_mc) (struct super_block *, struct mount_context *);
+
+This shadows the ->remount_fs() operation and takes a prepared mount context
+instead of the mount flags and data page.  It may modify the ms_flags in the
+context for the caller to pick up.
+
+[NOTE] remount_fs_mc is intended as a replacement for remount_fs.
+
+
+=================
+THE MOUNT CONTEXT
+=================
+
+The mount process is governed by a mount context.  This is represented by the
+mount_context structure:
+
+	struct mount_context {
+		const struct mount_context_operations *ops;
+		struct file_system_type *fs;
+		struct user_namespace	*user_ns;
+		struct mnt_namespace	*mnt_ns;
+		struct pid_namespace	*pid_ns;
+		struct net		*net_ns;
+		const struct cred	*cred;
+		char			*device;
+		char			*root_path;
+		void			*security;
+		const char		*error;
+		unsigned int		ms_flags;
+		unsigned int		mnt_flags;
+		bool			mounted;
+		bool			sloppy;
+		bool			silent;
+		enum mount_type		mount_type : 8;
+	};
+
+When allocated, the mount_context struct is extended by ->mc_size bytes as
+specified by the specified file_system_type struct.  This is for use by the
+filesystem.  The filesystem should wrap the struct in its own, e.g.:
+
+	struct nfs_mount_context {
+		struct mount_context mc;
+		...
+	};
+
+placing the mount_context struct first.  container_of() can then be used.
+
+The mount_context fields are as follows:
+
+ (*) const struct mount_context_operations *ops
+
+     These are operations that can be done on a mount context.  See below.
+     This must be set by the ->fsopen() file_system_type operation.
+
+ (*) struct file_system_type *fs
+
+     A pointer to the file_system_type of the filesystem that is being
+     mounted.  This retains a ref on the type owner.
+
+ (*) struct user_namespace *user_ns
+ (*) struct mnt_namespace *mnt_ns
+ (*) struct pid_namespace *pid_ns
+ (*) struct net *net_ns
+
+     This is a subset of the namespaces in use by the invoking process.  This
+     retains a ref on each namespace.  The subscribed namespaces may be
+     replaced by the filesystem to reflect other sources, such as the parent
+     mount superblock on an automount.
+
+ (*) struct cred *cred
+
+     The mounter's credentials.  This retains a ref on the credentials.
+
+ (*) char *device
+
+     This is the device to be mounted.  It may be a block device
+     (e.g. /dev/sda1) or something more exotic, such as the "host:/path" that
+     NFS desires.
+
+ (*) char *root_path
+
+     A path to the place inside the filesystem to actually mount.  This allows
+     a mount and bind-mount to be combined.
+
+     [NOTE] This isn't implemented yet, but NFS has the code to do this which
+     could be moved to the VFS.
+
+ (*) void *security
+
+     A place for the LSMs to hang their security data for the mount.  The
+     relevant security operations are described below.
+
+ (*) const char *error
+
+     A place for the VFS and the filesystem to hang an error message.  This
+     should be in the form of a static string that doesn't need deallocation
+     and the pointer to which can just be overwritten.  Under some
+     circumstances, this can be retrieved by userspace.
+
+     Note that the existence of the error string is expected to be guaranteed
+     by the reference on the file_system_type object held by ->fs or any
+     filesystem-specific reference held in the filesystem context until the
+     ->free() operation is called.
+
+ (*) unsigned int ms_flags
+ (*) unsigned int mnt_flags
+
+     These hold the mount flags.  ms_flags holds MS_* flags and mnt_flags holds
+     MNT_* flags.
+
+ (*) bool mounted
+
+     This is set to true once a mount attempt is made.  This causes an error to
+     be given on subsequent mount attempts with the same context and prevents
+     multiple mount attempts.
+
+ (*) bool sloppy
+ (*) bool silent
+
+     These are set if the sloppy or silent mount options are given.
+
+     [NOTE] sloppy is probably unnecessary when userspace passes over one
+     option at a time since the error can just be ignored if userspace deems it
+     to be unimportant.
+
+     [NOTE] silent is probably redundant with ms_flags & MS_SILENT.
+
+ (*) enum mount_type
+
+     This indicates the type of mount operation.  The available values are:
+
+	MOUNT_TYPE_NEW		-- New mount
+	MOUNT_TYPE_SUBMOUNT	-- New automatic submount of extant mount
+	MOUNT_TYPE_REMOUNT	-- Change an existing mount
+
+The mount context is created by calling __vfs_fsopen(), vfs_fsopen(),
+vfs_mntopen() or vfs_dup_mount_context() and is destroyed with
+put_mount_context().  Note that the structure is not refcounted.
+
+VFS, security and filesystem mount options are set individually with
+vfs_mount_option() or in bulk with generic_monolithic_mount_data().
+
+When mounting, the filesystem is allowed to take data from any of the pointers
+and attach it to the superblock (or whatever), provided it clears the pointer
+in the mount context.
+
+The filesystem is also allowed to allocate resources and pin them with the
+mount context.  For instance, NFS might pin the appropriate protocol version
+module.
+
+
+============================
+THE MOUNT CONTEXT OPERATIONS
+============================
+
+The mount context points to a table of operations:
+
+	struct mount_context_operations {
+		void (*free)(struct mount_context *mc);
+		int (*dup)(struct mount_context *mc, struct mount_context *src);
+		int (*option)(struct mount_context *mc, char *p);
+		int (*monolithic_mount_data)(struct mount_context *mc, void *data);
+		int (*validate)(struct mount_context *mc);
+		struct dentry *(*mount)(struct mount_context *mc);
+		int (*fill_super)(struct super_block *s, struct mount_context *mc);
+	};
+
+These operations are invoked by the various stages of the mount procedure to
+manage the mount context.  They are as follows:
+
+ (*) void (*free)(struct mount_context *mc);
+
+     Called to clean up the filesystem-specific part of the mount context when
+     the context is destroyed.  It should be aware that parts of the context
+     may have been removed and NULL'd out by ->mount().
+
+ (*) int (*dup)(struct mount_context *mc, struct mount_context *src);
+
+     Called when a mount context has been duplicated to get any refs or copy
+     any non-referenced resources held in the filesystem-specific part of the
+     mount context.  An error may be returned to indicate failure to do this.
+
+     [!] Note that if this fails, put_mount_context() will be called
+     	 immediately thereafter, so ->dup() *must* make the
+     	 filesystem-specific part safe for ->free().
+
+ (*) int (*option)(struct mount_context *mc, char *p);
+
+     Called when an option is to be added to the mount context.  p points to
+     the option string, likely in "key[=val]" format.  VFS-specific options
+     will have been weeded out and mc->ms_flags and mc->mnt_flags updated in
+     the context.  Security options will also have been weeded out and
+     mc->security updated.
+
+     If successful, 0 should be returned and a negative error code otherwise.
+     If an ambiguous error (such as -EINVAL) is returned, mc->error should be
+     set in the context to a string that provides more information.
+
+ (*) int (*monolithic_mount_data)(struct mount_context *mc, void *data);
+
+     Called when the mount(2) system call is invoked to pass the entire data
+     page in one go.  If this is expected to be just a list of "key[=val]"
+     items separated by commas, then this may be set to NULL.
+
+     The return value is as for ->option().
+
+     If the filesystem (eg. NFS) needs to examine the data first and then
+     finds it's the standard key-val list then it may pass it off to:
+
+	int generic_monolithic_mount_data(struct mount_context *mc, void *data);
+
+ (*) int (*validate)(struct mount_context *mc);
+
+     Called when all the options have been applied and the mount is about to
+     take place.  It is should check for inconsistencies from mount options
+     and it is also allowed to do preliminary resource acquisition.  For
+     instance, the core NFS module could load the NFS protocol module here.
+
+     Note that if mc->mount_type == MOUNT_TYPE_REMOUNT, some of the options
+     necessary for a new mount may not be set.
+
+     The return value is as for ->option().
+
+ (*) struct dentry *(*mount)(struct mount_context *mc);
+
+     Called to effect a new mount or new submount using the information stored
+     in the mount context (remounts go via a different vector).  It may detach
+     any resources it desires from the mount context and transfer them to the
+     superblock it creates.
+
+     On success it should return the dentry that's at the root of the mount.
+     In future, mc->root_path will then be applied to this.
+
+     In the case of an error, it should return a negative error code and set
+     mc->error.
+
+ (*) int (*fill_super)(struct super_block *s, struct mount_context *mc);
+
+     This is available to be used by things like mount_ns_mc() that are called
+     by ->mount() to transfer information/resources from the mount context to
+     the superblock.
+
+
+======================
+MOUNT CONTEXT SECURITY
+======================
+
+The mount context contains a security points that the LSMs can use for
+building up a security context for the superblock to be mounted.  There are a
+number of operations used by the new mount code for this purpose:
+
+ (*) int security_mount_ctx_alloc(struct mount_context *mc,
+				  struct super_block *src_sb);
+
+     Called to initialise mc->security (which is preset to NULL) and allocate
+     any resources needed.  It should return 0 on success and a negative error
+     code on failure.
+
+     src_sb is non-NULL in the case of a remount (MS_REMOUNT) in which case it
+     indicates the superblock to be remounted or in the case of a submount
+     (MS_SUBMOUNT) in which case it indicates the parent superblock.
+
+ (*) int security_mount_ctx_dup(struct mount_context *mc,
+				struct mount_context *src_mc);
+
+     Called to initialise mc->security (which is preset to NULL) and allocate
+     any resources needed.  The original mount context is pointed to by src_mc
+     and may be used for reference.  It should return 0 on success and a
+     negative error code on failure.
+
+ (*) void security_mount_ctx_free(struct mount_context *mc);
+
+     Called to clean up anything attached to mc->security.  Note that the
+     contents may have been transferred to a superblock and the pointer NULL'd
+     out during mount.
+
+ (*) int security_mount_ctx_option(struct mount_context *mc, char *opt);
+
+     Called for each mount option.  The mount options are in "key[=val]"
+     form.  An active LSM may reject one with an error, pass one over and
+     return 0 or consume one and return 1.  If consumed, the option isn't
+     passed on to the filesystem.
+
+     If it returns an error, it should set mc->error if the error is
+     ambiguous.
+
+ (*) int security_mount_ctx_kern_mount(struct mount_context *mc,
+				       struct super_block *sb);
+
+     Called during mount to verify that the specified superblock is allowed to
+     be mounted and to transfer the security data there.
+
+     On success, it should return 0; otherwise it should return an error and
+     set mc->error to indicate the problem.  It should not return -ENOMEM as
+     this should be taken care of in advance.
+
+     [NOTE] Should I add a security_mount_ctx_validate() operation so that the
+     LSM has the opportunity to allocate stuff and check the options as a
+     whole?
+
+
+============================
+VFS MOUNT CONTEXT OPERATIONS
+============================
+
+There are four operations for creating a mount context and one for destroying
+a context:
+
+ (*) struct mount_context *__vfs_fsopen(struct file_system_type *fs_type,
+					struct super_block *src_sb;
+					unsigned int ms_flags,
+					unsigned int mnt_flags);
+
+     Create a mount context given a filesystem type pointer.  This allocates
+     the mount context, sets the flags, initialises the security and calls
+     fs_type->fsopen() to initialise the filesystem context.
+
+     src_sb can be NULL or it may indicate a superblock that is going to be
+     remounted (MS_REMOUNT) or a superblock that is the parent of a submount
+     (MS_SUBMOUNT).  This superblock is provided as a source of namespace
+     information.
+
+ (*) struct mount_context *vfs_mntopen(struct vfsmount *mnt,
+				       unsigned int ms_flags,
+				       unsigned int mnt_flags);
+
+     Create a mount context from the same filesystem as an extant mount and
+     initialise the mount parameters from the superblock underlying that
+     mount.  This is used by remount.
+
+ (*) struct mount_context *vfs_fsopen(const char *fs_name);
+
+     Create a mount context given a filesystem name.  It is assumed that the
+     mount flags will be passed in as text options later.  This is intended to
+     be called from sys_fsopen().  This copies current's namespaces to the
+     mount context.
+
+ (*) struct mount_context *vfs_dup_mount_context(struct mount_context *src);
+
+     Duplicate a mount context, copying any options noted and duplicating or
+     additionally referencing any resources held therein.  This is available
+     for use where a filesystem has to get a mount within a mount, such as
+     NFS4 does by internally mounting the root of the target server and then
+     doing a private pathwalk to the target directory.
+
+ (*) void put_mount_context(struct mount_context *ctx);
+
+     Destroy a mount context, releasing any resources it holds.  This calls
+     the ->free() operation.  This is intended to be called by anyone who
+     created a mount context.
+
+     [!] Mount contexts are not refcounted, so this causes unconditional
+     	 destruction.
+
+In all the above operations, apart from the put op, the return is a mount
+context pointer or a negative error code.  No error string is saved as the
+error string is only guaranteed as long as the file_system_type is pinned (and
+thus the module).
+
+In the remaining operations, if an error occurs, a negative error code is
+returned and, if not obvious, mc->error should be set to point to a useful
+string.  The string should not be freed.
+
+ (*) struct vfsmount *vfs_kern_mount_mc(struct mount_context *mc);
+
+     Create a mount given the parameters in the specified mount context.  This
+     invokes the ->validate() op and then the ->mount() op.
+
+ (*) struct vfsmount *vfs_submount_mc(const struct dentry *mountpoint,
+				      struct mount_context *mc);
+
+     Create a mount given a mount context and set MS_SUBMOUNT on it.  A
+     wrapper around vfs_kern_mount_mc().  This is intended to be called from
+     filesystems that have automount points (NFS, AFS, ...).
+
+ (*) int vfs_mount_option(struct mount_context *mc, char *data);
+
+     Supply a single mount option to the mount context.  The mount option
+     should likely be in a "key[=val]" string form.  The option is first
+     checked to see if it corresponds to a standard mount flag (in which case
+     it is used to mark an MS_xxx flag and consumed) or a security option (in
+     which case the LSM consumes it) before it is passed on to the filesystem.
+
+ (*) int generic_monolithic_mount_data(struct mount_context *ctx, void *data);
+
+     Parse a sys_mount() data page, assuming the form to be a text list
+     consisting of key[=val] options separated by commas.  Each item in the
+     list is passed to vfs_mount_option().  This is the default when the
+     ->monolithic_mount_data() operation is NULL.
diff --git a/fs/Makefile b/fs/Makefile
index 7bbaca9c67b1..308a104a9a07 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -11,7 +11,8 @@ obj-y :=	open.o read_write.o file_table.o super.o \
 		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 \
-		stack.o fs_struct.o statfs.o fs_pin.o nsfs.o
+		stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
+		mount_context.o
 
 ifeq ($(CONFIG_BLOCK),y)
 obj-y +=	buffer.o block_dev.o direct-io.o mpage.o
diff --git a/fs/internal.h b/fs/internal.h
index 076751d90ba2..ef8c5e93f364 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -87,7 +87,7 @@ extern struct file *get_empty_filp(void);
 /*
  * super.c
  */
-extern int do_remount_sb(struct super_block *, int, void *, int);
+extern int do_remount_sb(struct super_block *, int, void *, int, struct mount_context *);
 extern bool trylock_super(struct super_block *sb);
 extern struct dentry *mount_fs(struct file_system_type *,
 			       int, const char *, void *);
diff --git a/fs/mount.h b/fs/mount.h
index 2826543a131d..b1e99b38f2ee 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -108,9 +108,10 @@ static inline void detach_mounts(struct dentry *dentry)
 	__detach_mounts(dentry);
 }
 
-static inline void get_mnt_ns(struct mnt_namespace *ns)
+static inline struct mnt_namespace *get_mnt_ns(struct mnt_namespace *ns)
 {
 	atomic_inc(&ns->count);
+	return ns;
 }
 
 extern seqlock_t mount_lock;
diff --git a/fs/mount_context.c b/fs/mount_context.c
new file mode 100644
index 000000000000..7d765c100bf1
--- /dev/null
+++ b/fs/mount_context.c
@@ -0,0 +1,343 @@
+/* Provide a way to create a mount context within the kernel that can be
+ * configured before 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.h>
+#include <linux/mount.h>
+#include <linux/nsproxy.h>
+#include <linux/slab.h>
+#include <linux/magic.h>
+#include <linux/security.h>
+#include <linux/parser.h>
+#include <linux/mnt_namespace.h>
+#include <linux/pid_namespace.h>
+#include <linux/user_namespace.h>
+#include <net/net_namespace.h>
+#include "mount.h"
+
+static const match_table_t common_set_mount_options = {
+	{ MS_DIRSYNC,		"dirsync" },
+	{ MS_I_VERSION,		"iversion" },
+	{ MS_LAZYTIME,		"lazytime" },
+	{ MS_MANDLOCK,		"mand" },
+	{ MS_NOATIME,		"noatime" },
+	{ MS_NODEV,		"nodev" },
+	{ MS_NODIRATIME,	"nodiratime" },
+	{ MS_NOEXEC,		"noexec" },
+	{ MS_NOSUID,		"nosuid" },
+	{ MS_POSIXACL,		"posixacl" },
+	{ MS_RDONLY,		"ro" },
+	{ MS_REC,		"rec" },
+	{ MS_RELATIME,		"relatime" },
+	{ MS_STRICTATIME,	"strictatime" },
+	{ MS_SYNCHRONOUS,	"sync" },
+	{ MS_VERBOSE,		"verbose" },
+	{ },
+};
+
+static const match_table_t common_clear_mount_options = {
+	{ MS_LAZYTIME,		"nolazytime" },
+	{ MS_MANDLOCK,		"nomand" },
+	{ MS_NODEV,		"dev" },
+	{ MS_NOEXEC,		"exec" },
+	{ MS_NOSUID,		"suid" },
+	{ MS_RDONLY,		"rw" },
+	{ MS_RELATIME,		"norelatime" },
+	{ MS_SILENT,		"silent" },
+	{ MS_STRICTATIME,	"nostrictatime" },
+	{ MS_SYNCHRONOUS,	"async" },
+	{ },
+};
+
+static const match_table_t forbidden_mount_options = {
+	{ MS_BIND,		"bind" },
+	{ MS_KERNMOUNT,		"ro" },
+	{ MS_MOVE,		"move" },
+	{ MS_PRIVATE,		"private" },
+	{ MS_REMOUNT,		"remount" },
+	{ MS_SHARED,		"shared" },
+	{ MS_SLAVE,		"slave" },
+	{ MS_UNBINDABLE,	"unbindable" },
+	{ },
+};
+
+/*
+ * Check for a common mount option.
+ */
+static noinline int vfs_common_mount_option(struct mount_context *mc, char *data)
+{
+	substring_t args[MAX_OPT_ARGS];
+	unsigned int token;
+
+	token = match_token(data, common_set_mount_options, args);
+	if (token) {
+		mc->ms_flags |= token;
+		return 1;
+	}
+
+	token = match_token(data, common_clear_mount_options, args);
+	if (token) {
+		mc->ms_flags &= ~token;
+		return 1;
+	}
+
+	token = match_token(data, forbidden_mount_options, args);
+	if (token) {
+		mc->error = "Mount option, not superblock option";
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * vfs_mount_option - Add a single mount option to a mount context
+ * @mc: The mount context to modify
+ * @option: The option to apply.
+ *
+ * A single mount option in string form is applied to the mount being set up in
+ * the mount context.  Certain standard options (for example "ro") are
+ * translated into flag bits without going to the filesystem.  The active
+ * security module allowed to observe and poach options.  Any other options are
+ * passed over to the filesystem to parse.
+ *
+ * This may be called multiple times for a context.
+ *
+ * Returns 0 on success and a negative error code on failure.  In the event of
+ * failure, mc->error may have been set to a non-allocated string that gives
+ * more information.
+ */
+int vfs_mount_option(struct mount_context *mc, char *data)
+{
+	int ret;
+
+	if (mc->mounted)
+		return -EBUSY;
+
+	ret = vfs_common_mount_option(mc, data);
+	if (ret < 0)
+		return ret;
+	if (ret == 1)
+		return 0;
+
+	ret = security_mount_ctx_option(mc, data);
+	if (ret < 0)
+		return ret;
+	if (ret == 1)
+		return 0;
+
+	return mc->ops->option(mc, data);
+}
+EXPORT_SYMBOL(vfs_mount_option);
+
+/**
+ * generic_monolithic_mount_data - Parse key[=val][,key[=val]]* mount data
+ * @mc: The mount context to populate
+ * @data: The data to parse
+ *
+ * Parse a blob of data that's in key[=val][,key[=val]]* form.  This can be
+ * called from the ->monolithic_mount_data() mount context operation.
+ *
+ * Returns 0 on success or the error returned by the ->option() mount context
+ * operation on failure.
+ */
+int generic_monolithic_mount_data(struct mount_context *ctx, void *data)
+{
+	char *options = data, *p;
+	int ret;
+
+	if (!options)
+		return 0;
+
+	while ((p = strsep(&options, ",")) != NULL) {
+		if (*p) {
+			ret = vfs_mount_option(ctx, p);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(generic_monolithic_mount_data);
+
+/**
+ * __vfs_fsopen - Open a filesystem and create a mount context
+ * @fs_type: The filesystem type
+ * @src_sb: A superblock from which this one derives (or NULL)
+ * @ms_flags: Superblock flags and op flags (such as MS_REMOUNT)
+ * @mnt_flags: Mountpoint flags, such as MNT_READONLY
+ * @mount_type: Type of mount
+ *
+ * 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 (@src_sb), may have parameters such as namespaces copied
+ * across from that superblock.
+ */
+struct mount_context *__vfs_fsopen(struct file_system_type *fs_type,
+				   struct super_block *src_sb,
+				   unsigned int ms_flags, unsigned int mnt_flags,
+				   enum mount_type mount_type)
+{
+	struct mount_context *mc;
+	int ret;
+
+	if (fs_type->fsopen && fs_type->mc_size < sizeof(*mc))
+		BUG();
+
+	mc = kzalloc(max_t(size_t, fs_type->mc_size, sizeof(*mc)), GFP_KERNEL);
+	if (!mc)
+		return ERR_PTR(-ENOMEM);
+
+	mc->mount_type = mount_type;
+	mc->ms_flags = ms_flags;
+	mc->mnt_flags = mnt_flags;
+	mc->fs_type = fs_type;
+	get_filesystem(fs_type);
+	mc->mnt_ns = get_mnt_ns(current->nsproxy->mnt_ns);
+	mc->pid_ns = get_pid_ns(task_active_pid_ns(current));
+	mc->net_ns = get_net(current->nsproxy->net_ns);
+	mc->user_ns = get_user_ns(current_user_ns());
+	mc->cred = get_current_cred();
+
+
+	/* TODO: Make all filesystems support this unconditionally */
+	if (mc->fs_type->fsopen) {
+		ret = mc->fs_type->fsopen(mc, src_sb);
+		if (ret < 0)
+			goto err_mc;
+	}
+
+	/* Do the security check last because ->fsopen may change the
+	 * namespace subscriptions.
+	 */
+	ret = security_mount_ctx_alloc(mc, src_sb);
+	if (ret < 0)
+		goto err_mc;
+
+	return mc;
+
+err_mc:
+	put_mount_context(mc);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(__vfs_fsopen);
+
+/**
+ * vfs_fsopen - Open a filesystem and create a mount context
+ * @fs_name: The name of the filesystem
+ *
+ * Open a filesystem and create a mount context that will hold the mount
+ * options, device name, security details, etc..  Note that the caller should
+ * check the ->ops pointer in the returned context to determine whether the
+ * filesystem actually supports the mount context itself.
+ */
+struct mount_context *vfs_fsopen(const char *fs_name)
+{
+	struct file_system_type *fs_type;
+	struct mount_context *mc;
+
+	fs_type = get_fs_type(fs_name);
+	if (!fs_type)
+		return ERR_PTR(-ENODEV);
+
+	mc = __vfs_fsopen(fs_type, NULL, 0, 0, MOUNT_TYPE_NEW);
+	put_filesystem(fs_type);
+	return mc;
+}
+EXPORT_SYMBOL(vfs_fsopen);
+
+/**
+ * vfs_mntopen - Create a mount context and initialise it from an extant mount
+ * @mnt: The mountpoint to open
+ * @ms_flags: Superblock flags and op flags (such as MS_REMOUNT)
+ * @mnt_flags: Mountpoint flags, such as MNT_READONLY
+ * @mount_type: Type of mount
+ *
+ * Open a mounted filesystem and create a mount context such that a remount can
+ * be effected.
+ */
+struct mount_context *vfs_mntopen(struct vfsmount *mnt,
+				  unsigned int ms_flags,
+				  unsigned int mnt_flags,
+				  enum mount_type mount_type)
+{
+	return __vfs_fsopen(mnt->mnt_sb->s_type, mnt->mnt_sb,
+			    ms_flags, mnt_flags, mount_type);
+}
+
+/**
+ * vfs_dup_mount_context: Duplicate a mount context.
+ * @src: The mount context to copy.
+ */
+struct mount_context *vfs_dup_mount_context(struct mount_context *src)
+{
+	struct mount_context *mc;
+	int ret;
+
+	if (!src->ops->dup)
+		return ERR_PTR(-ENOTSUPP);
+
+	mc = kmemdup(src, src->fs_type->mc_size, GFP_KERNEL);
+	if (!mc)
+		return ERR_PTR(-ENOMEM);
+
+	mc->device	= NULL;
+	mc->root_path	= NULL;
+	mc->security	= NULL;
+	mc->error	= NULL;
+	get_filesystem(mc->fs_type);
+	get_mnt_ns(mc->mnt_ns);
+	get_pid_ns(mc->pid_ns);
+	get_net(mc->net_ns);
+	get_user_ns(mc->user_ns);
+	get_cred(mc->cred);
+
+	/* Can't call put until we've called ->dup */
+	ret = mc->ops->dup(mc, src);
+	if (ret < 0)
+		goto err_mc;
+
+	ret = security_mount_ctx_dup(mc, src);
+	if (ret < 0)
+		goto err_mc;
+	return mc;
+
+err_mc:
+	put_mount_context(mc);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(vfs_dup_mount_context);
+
+/*
+ * Dispose of a mount context.
+ */
+void put_mount_context(struct mount_context *mc)
+{
+	if (mc->ops && mc->ops->free)
+		mc->ops->free(mc);
+	security_mount_ctx_free(mc);
+	if (mc->mnt_ns)
+		put_mnt_ns(mc->mnt_ns);
+	if (mc->pid_ns)
+		put_pid_ns(mc->pid_ns);
+	if (mc->net_ns)
+		put_net(mc->net_ns);
+	put_user_ns(mc->user_ns);
+	if (mc->cred)
+		put_cred(mc->cred);
+	put_filesystem(mc->fs_type);
+	kfree(mc->device);
+	kfree(mc->root_path);
+	kfree(mc);
+}
+EXPORT_SYMBOL(put_mount_context);
diff --git a/fs/namespace.c b/fs/namespace.c
index db034b6afd43..e0edab9af308 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -25,6 +25,7 @@
 #include <linux/magic.h>
 #include <linux/bootmem.h>
 #include <linux/task_work.h>
+#include <linux/file.h>
 #include <linux/sched/task.h>
 
 #include "pnode.h"
@@ -783,9 +784,14 @@ static void put_mountpoint(struct mountpoint *mp)
 	}
 }
 
+static inline int __check_mnt(struct mount *mnt, struct mnt_namespace *mnt_ns)
+{
+	return mnt->mnt_ns == mnt_ns;
+}
+
 static inline int check_mnt(struct mount *mnt)
 {
-	return mnt->mnt_ns == current->nsproxy->mnt_ns;
+	return __check_mnt(mnt, current->nsproxy->mnt_ns);
 }
 
 /*
@@ -1596,7 +1602,7 @@ static int do_umount(struct mount *mnt, int flags)
 			return -EPERM;
 		down_write(&sb->s_umount);
 		if (!(sb->s_flags & MS_RDONLY))
-			retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
+			retval = do_remount_sb(sb, MS_RDONLY, NULL, 0, NULL);
 		up_write(&sb->s_umount);
 		return retval;
 	}
@@ -2279,6 +2285,26 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
 }
 
 /*
+ * Parse the monolithic page of mount data given to sys_mount().
+ */
+static int parse_monolithic_mount_data(struct mount_context *mc, void *data)
+{
+	int (*monolithic_mount_data)(struct mount_context *, void *);
+	int ret;
+
+	monolithic_mount_data = mc->ops->monolithic_mount_data;
+	if (!monolithic_mount_data)
+		monolithic_mount_data = generic_monolithic_mount_data;
+
+	ret = monolithic_mount_data(mc, data);
+	if (ret < 0)
+		return ret;
+	if (mc->ops->validate)
+		return mc->ops->validate(mc);
+	return 0;
+}
+
+/*
  * change filesystem flags. dir should be a physical root of filesystem.
  * If you've mounted a non-root directory somewhere and want to do remount
  * on it - tough luck.
@@ -2286,13 +2312,14 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
 static int do_remount(struct path *path, int flags, int mnt_flags,
 		      void *data)
 {
+	struct mount_context *mc = NULL;
 	int err;
 	struct super_block *sb = path->mnt->mnt_sb;
 	struct mount *mnt = real_mount(path->mnt);
+	struct file_system_type *type = sb->s_type;
 
 	if (!check_mnt(mnt))
 		return -EINVAL;
-
 	if (path->dentry != path->mnt->mnt_root)
 		return -EINVAL;
 
@@ -2323,9 +2350,19 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
 		return -EPERM;
 	}
 
-	err = security_sb_remount(sb, data);
-	if (err)
-		return err;
+	if (type->fsopen) {
+		mc = vfs_mntopen(path->mnt, flags, mnt_flags, MOUNT_TYPE_REMOUNT);
+		if (IS_ERR(mc))
+			return PTR_ERR(mc);
+
+		err = parse_monolithic_mount_data(mc, data);
+		if (err < 0)
+			goto err_mc;
+	} else {
+		err = security_sb_remount(sb, data);
+		if (err)
+			return err;
+	}
 
 	down_write(&sb->s_umount);
 	if (flags & MS_BIND)
@@ -2333,7 +2370,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
 	else if (!capable(CAP_SYS_ADMIN))
 		err = -EPERM;
 	else
-		err = do_remount_sb(sb, flags, data, 0);
+		err = do_remount_sb(sb, flags, data, 0, mc);
 	if (!err) {
 		lock_mount_hash();
 		mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
@@ -2342,6 +2379,9 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
 		unlock_mount_hash();
 	}
 	up_write(&sb->s_umount);
+err_mc:
+	if (mc)
+		put_mount_context(mc);
 	return err;
 }
 
@@ -2451,7 +2491,8 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
 /*
  * add a mount into a namespace's mount tree
  */
-static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
+static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags,
+			struct mnt_namespace *mnt_ns)
 {
 	struct mountpoint *mp;
 	struct mount *parent;
@@ -2465,7 +2506,7 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
 
 	parent = real_mount(path->mnt);
 	err = -EINVAL;
-	if (unlikely(!check_mnt(parent))) {
+	if (unlikely(!__check_mnt(parent, mnt_ns))) {
 		/* that's acceptable only for automounts done in private ns */
 		if (!(mnt_flags & MNT_SHRINKABLE))
 			goto unlock;
@@ -2493,42 +2534,73 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
 }
 
 static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags);
+static int do_new_mount_mc(struct mount_context *mc, struct path *mountpoint,
+			   unsigned int mnt_flags);
 
 /*
  * create a new mount for userspace and request it to be added into the
  * namespace's tree
  */
-static int do_new_mount(struct path *path, const char *fstype, int flags,
+static int do_new_mount(struct path *mountpoint, const char *fstype, int flags,
 			int mnt_flags, const char *name, void *data)
 {
-	struct file_system_type *type;
+	struct mount_context *mc;
 	struct vfsmount *mnt;
 	int err;
 
 	if (!fstype)
 		return -EINVAL;
 
-	type = get_fs_type(fstype);
-	if (!type)
-		return -ENODEV;
+	mc = vfs_fsopen(fstype);
+	if (IS_ERR(mc))
+		return PTR_ERR(mc);
+	mc->ms_flags = flags;
+	mc->mnt_flags = mnt_flags;
 
-	mnt = vfs_kern_mount(type, flags, name, data);
-	if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
-	    !mnt->mnt_sb->s_subtype)
-		mnt = fs_set_subtype(mnt, fstype);
+	err = -ENOMEM;
+	mc->device = kstrdup(name, GFP_KERNEL);
+	if (!mc->device)
+		goto err_mc;
 
-	put_filesystem(type);
-	if (IS_ERR(mnt))
-		return PTR_ERR(mnt);
+	if (mc->ops) {
+		err = parse_monolithic_mount_data(mc, data);
+		if (err < 0)
+			goto err_mc;
 
-	if (mount_too_revealing(mnt, &mnt_flags)) {
-		mntput(mnt);
-		return -EPERM;
+		err = do_new_mount_mc(mc, mountpoint, mnt_flags);
+		if (err)
+			goto err_mc;
+
+	} else {
+		mnt = vfs_kern_mount(mc->fs_type, flags, name, data);
+		if (!IS_ERR(mnt) && (mc->fs_type->fs_flags & FS_HAS_SUBTYPE) &&
+		    !mnt->mnt_sb->s_subtype)
+			mnt = fs_set_subtype(mnt, fstype);
+
+		if (IS_ERR(mnt)) {
+			err = PTR_ERR(mnt);
+			goto err_mc;
+		}
+
+		err = -EPERM;
+		if (mount_too_revealing(mnt, &mnt_flags))
+			goto err_mnt;
+
+		err = do_add_mount(real_mount(mnt), mountpoint, mnt_flags,
+				   mc->mnt_ns);
+		if (err)
+			goto err_mnt;
 	}
 
-	err = do_add_mount(real_mount(mnt), path, mnt_flags);
-	if (err)
-		mntput(mnt);
+	put_mount_context(mc);
+	return 0;
+
+err_mnt:
+	mntput(mnt);
+err_mc:
+	if (mc->error)
+		pr_info("Mount failed: %s\n", mc->error);
+	put_mount_context(mc);
 	return err;
 }
 
@@ -2547,7 +2619,8 @@ int finish_automount(struct vfsmount *m, struct path *path)
 		goto fail;
 	}
 
-	err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
+	err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE,
+			   current->nsproxy->mnt_ns);
 	if (!err)
 		return 0;
 fail:
@@ -3061,6 +3134,130 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
 	return ret;
 }
 
+static struct dentry *__do_mount_mc(struct mount_context *mc)
+{
+	struct super_block *sb;
+	struct dentry *root;
+	int ret;
+
+	root = mc->ops->mount(mc);
+	if (IS_ERR(root))
+		return root;
+
+	sb = root->d_sb;
+	BUG_ON(!sb);
+	WARN_ON(!sb->s_bdi);
+	sb->s_flags |= MS_BORN;
+
+	ret = security_mount_ctx_kern_mount(mc, sb);
+	if (ret < 0)
+		goto err_sb;
+
+	/*
+	 * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE
+	 * but s_maxbytes was an unsigned long long for many releases. Throw
+	 * this warning for a little while to try and catch filesystems that
+	 * violate this rule.
+	 */
+	WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
+		"negative value (%lld)\n", mc->fs_type->name, sb->s_maxbytes);
+
+	up_write(&sb->s_umount);
+	return root;
+
+err_sb:
+	dput(root);
+	deactivate_locked_super(sb);
+	return ERR_PTR(ret);
+}
+
+struct vfsmount *vfs_kern_mount_mc(struct mount_context *mc)
+{
+	struct dentry *root;
+	struct mount *mnt;
+	int ret;
+
+	if (mc->ops->validate) {
+		ret = mc->ops->validate(mc);
+		if (ret < 0)
+			return ERR_PTR(ret);
+	}
+
+	mnt = alloc_vfsmnt(mc->device ?: "none");
+	if (!mnt)
+		return ERR_PTR(-ENOMEM);
+
+	if (mc->ms_flags & MS_KERNMOUNT)
+		mnt->mnt.mnt_flags = MNT_INTERNAL;
+
+	root = __do_mount_mc(mc);
+	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;
+	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);
+	unlock_mount_hash();
+	return &mnt->mnt;
+}
+EXPORT_SYMBOL_GPL(vfs_kern_mount_mc);
+
+struct vfsmount *
+vfs_submount_mc(const struct dentry *mountpoint, struct mount_context *mc)
+{
+	/* Until it is worked out how to pass the user namespace
+	 * through from the parent mount to the submount don't support
+	 * unprivileged mounts with submounts.
+	 */
+	if (mountpoint->d_sb->s_user_ns != &init_user_ns)
+		return ERR_PTR(-EPERM);
+
+	mc->ms_flags = MS_SUBMOUNT;
+	return vfs_kern_mount_mc(mc);
+}
+EXPORT_SYMBOL_GPL(vfs_submount_mc);
+
+static int do_new_mount_mc(struct mount_context *mc, struct path *mountpoint,
+			   unsigned int mnt_flags)
+{
+	struct vfsmount *mnt;
+	int ret;
+
+	mnt = vfs_kern_mount_mc(mc);
+	if (IS_ERR(mnt))
+		return PTR_ERR(mnt);
+
+	if ((mc->fs_type->fs_flags & FS_HAS_SUBTYPE) &&
+	    !mnt->mnt_sb->s_subtype) {
+		mnt = fs_set_subtype(mnt, mc->fs_type->name);
+		if (IS_ERR(mnt))
+			return PTR_ERR(mnt);
+	}
+
+	ret = -EPERM;
+	if (mount_too_revealing(mnt, &mnt_flags)) {
+		mc->error = "VFS: Mount too revealing";
+		goto err_mnt;
+	}
+
+	ret = do_add_mount(real_mount(mnt), mountpoint, mnt_flags, mc->mnt_ns);
+	if (ret < 0) {
+		mc->error = "VFS: Failed to add mount";
+		goto err_mnt;
+	}
+	return ret;
+
+err_mnt:
+	mntput(mnt);
+	return ret;
+}
+
 /*
  * Return true if path is reachable from root
  *
@@ -3302,6 +3499,23 @@ struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
 }
 EXPORT_SYMBOL_GPL(kern_mount_data);
 
+struct vfsmount *kern_mount_data_mc(struct mount_context *mc)
+{
+	struct vfsmount *mnt;
+
+	mc->ms_flags = MS_KERNMOUNT;
+	mnt = vfs_kern_mount_mc(mc);
+	if (!IS_ERR(mnt)) {
+		/*
+		 * it is a longterm mount, don't release mnt until
+		 * we unmount before file sys is unregistered
+		*/
+		real_mount(mnt)->mnt_ns = MNT_NS_INTERNAL;
+	}
+	return mnt;
+}
+EXPORT_SYMBOL_GPL(kern_mount_data_mc);
+
 void kern_unmount(struct vfsmount *mnt)
 {
 	/* release long term mount so mount point can be released */
diff --git a/fs/super.c b/fs/super.c
index adb0c0de428c..6e7b86520337 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -805,10 +805,13 @@ struct super_block *user_get_super(dev_t dev)
  *	@flags:	numeric part of options
  *	@data:	the rest of options
  *      @force: whether or not to force the change
+ *	@mc:	the mount context for filesystems that support it
+ *		(NULL if called from emergency or umount)
  *
  *	Alters the mount options of a mounted file system.
  */
-int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+int do_remount_sb(struct super_block *sb, int flags, void *data, int force,
+		  struct mount_context *mc)
 {
 	int retval;
 	int remount_ro;
@@ -850,8 +853,14 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
 		}
 	}
 
-	if (sb->s_op->remount_fs) {
-		retval = sb->s_op->remount_fs(sb, &flags, data);
+	if (sb->s_op->remount_fs_mc ||
+	    sb->s_op->remount_fs) {
+		if (sb->s_op->remount_fs_mc) {
+		    retval = sb->s_op->remount_fs_mc(sb, mc);
+		    flags = mc->ms_flags;
+		} else {
+			retval = sb->s_op->remount_fs(sb, &flags, data);
+		}
 		if (retval) {
 			if (!force)
 				goto cancel_readonly;
@@ -898,7 +907,7 @@ static void do_emergency_remount(struct work_struct *work)
 			/*
 			 * What lock protects sb->s_flags??
 			 */
-			do_remount_sb(sb, MS_RDONLY, NULL, 1);
+			do_remount_sb(sb, MS_RDONLY, NULL, 1, NULL);
 		}
 		up_write(&sb->s_umount);
 		spin_lock(&sb_lock);
@@ -1048,6 +1057,37 @@ struct dentry *mount_ns(struct file_system_type *fs_type,
 
 EXPORT_SYMBOL(mount_ns);
 
+struct dentry *mount_ns_mc(struct mount_context *mc, void *ns)
+{
+	struct super_block *sb;
+
+	/* Don't allow mounting unless the caller has CAP_SYS_ADMIN
+	 * over the namespace.
+	 */
+	if (!(mc->ms_flags & MS_KERNMOUNT) &&
+	    !ns_capable(mc->user_ns, CAP_SYS_ADMIN))
+		return ERR_PTR(-EPERM);
+
+	sb = sget_userns(mc->fs_type, ns_test_super, ns_set_super,
+			 mc->ms_flags, mc->user_ns, ns);
+	if (IS_ERR(sb))
+		return ERR_CAST(sb);
+
+	if (!sb->s_root) {
+		int err;
+		err = mc->ops->fill_super(sb, mc);
+		if (err) {
+			deactivate_locked_super(sb);
+			return ERR_PTR(err);
+		}
+
+		sb->s_flags |= MS_ACTIVE;
+	}
+
+	return dget(sb->s_root);
+}
+EXPORT_SYMBOL(mount_ns_mc);
+
 #ifdef CONFIG_BLOCK
 static int set_bdev_super(struct super_block *s, void *data)
 {
@@ -1196,7 +1236,7 @@ struct dentry *mount_single(struct file_system_type *fs_type,
 		}
 		s->s_flags |= MS_ACTIVE;
 	} else {
-		do_remount_sb(s, flags, data, 0);
+		do_remount_sb(s, flags, data, 0, NULL);
 	}
 	return dget(s->s_root);
 }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 30e5c14bd743..40fe5c5054ec 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -55,6 +55,7 @@ struct workqueue_struct;
 struct iov_iter;
 struct fscrypt_info;
 struct fscrypt_operations;
+struct mount_context;
 
 extern void __init inode_init(void);
 extern void __init inode_init_early(void);
@@ -701,6 +702,11 @@ static inline void inode_unlock(struct inode *inode)
 	up_write(&inode->i_rwsem);
 }
 
+static inline int inode_lock_killable(struct inode *inode)
+{
+	return down_write_killable(&inode->i_rwsem);
+}
+
 static inline void inode_lock_shared(struct inode *inode)
 {
 	down_read(&inode->i_rwsem);
@@ -1786,6 +1792,7 @@ struct super_operations {
 	int (*unfreeze_fs) (struct super_block *);
 	int (*statfs) (struct dentry *, struct kstatfs *);
 	int (*remount_fs) (struct super_block *, int *, char *);
+	int (*remount_fs_mc) (struct super_block *, struct mount_context *);
 	void (*umount_begin) (struct super_block *);
 
 	int (*show_options)(struct seq_file *, struct dentry *);
@@ -2020,8 +2027,10 @@ struct file_system_type {
 #define FS_HAS_SUBTYPE		4
 #define FS_USERNS_MOUNT		8	/* Can be mounted by userns root */
 #define FS_RENAME_DOES_D_MOVE	32768	/* FS will handle d_move() during rename() internally. */
+	unsigned short mc_size;		/* Size of mount context to allocate */
 	struct dentry *(*mount) (struct file_system_type *, int,
 		       const char *, void *);
+	int (*fsopen)(struct mount_context *, struct super_block *);
 	void (*kill_sb) (struct super_block *);
 	struct module *owner;
 	struct file_system_type * next;
@@ -2039,6 +2048,7 @@ struct file_system_type {
 
 #define MODULE_ALIAS_FS(NAME) MODULE_ALIAS("fs-" NAME)
 
+extern struct dentry *mount_ns_mc(struct mount_context *mc, void *ns);
 extern struct dentry *mount_ns(struct file_system_type *fs_type,
 	int flags, void *data, void *ns, struct user_namespace *user_ns,
 	int (*fill_super)(struct super_block *, void *, int));
@@ -2105,6 +2115,7 @@ extern int register_filesystem(struct file_system_type *);
 extern int unregister_filesystem(struct file_system_type *);
 extern struct vfsmount *kern_mount_data(struct file_system_type *, void *data);
 #define kern_mount(type) kern_mount_data(type, NULL)
+extern struct vfsmount *kern_mount_data_mc(struct mount_context *);
 extern void kern_unmount(struct vfsmount *mnt);
 extern int may_umount_tree(struct vfsmount *);
 extern int may_umount(struct vfsmount *);
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index e29d4c62a3c8..f6aa68b8e68e 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -75,6 +75,32 @@
  *	should enable secure mode.
  *	@bprm contains the linux_binprm structure.
  *
+ * Security hooks for mount using fd context.
+ *
+ * @mount_ctx_alloc:
+ *	Allocate and attach a security structure to mc->security.  This pointer
+ *	is initialised to NULL by the caller.
+ *	@mc indicates the new mount context.
+ *	@src_sb indicates the source superblock of a submount.
+ * @mount_ctx_dup:
+ *	Allocate and attach a security structure to mc->security.  This pointer
+ *	is initialised to NULL by the caller.
+ *	@mc indicates the new mount context.
+ *	@src_mc indicates the original mount context.
+ * @mount_ctx_free:
+ *	Clean up a mount context.
+ *	@mc indicates the mount context.
+ * @mount_ctx_option:
+ *	Userspace provided an option to configure a mount.  The LSM may reject
+ *	it with an error and may use it for itself, in which case it should
+ *	return 1; otherwise it should return 0 to pass it on to the filesystem.
+ *	@mc indicates the mount context.
+ *	@p indicates the option in "key[=val]" form.
+ * @mount_ctx_kern_mount:
+ *	Equivalent of sb_kern_mount, but with a mount_context.
+ *	@mc indicates the mount context.
+ *	@src_sb indicates the new superblock.
+ *
  * Security hooks for filesystem operations.
  *
  * @sb_alloc_security:
@@ -1358,6 +1384,12 @@ union security_list_options {
 	void (*bprm_committing_creds)(struct linux_binprm *bprm);
 	void (*bprm_committed_creds)(struct linux_binprm *bprm);
 
+	int (*mount_ctx_alloc)(struct mount_context *mc, struct super_block *src_sb);
+	int (*mount_ctx_dup)(struct mount_context *mc, struct mount_context *src_mc);
+	void (*mount_ctx_free)(struct mount_context *mc);
+	int (*mount_ctx_option)(struct mount_context *mc, char *opt);
+	int (*mount_ctx_kern_mount)(struct mount_context *mc, struct super_block *sb);
+
 	int (*sb_alloc_security)(struct super_block *sb);
 	void (*sb_free_security)(struct super_block *sb);
 	int (*sb_copy_data)(char *orig, char *copy);
@@ -1666,6 +1698,11 @@ struct security_hook_heads {
 	struct list_head bprm_secureexec;
 	struct list_head bprm_committing_creds;
 	struct list_head bprm_committed_creds;
+	struct list_head mount_ctx_alloc;
+	struct list_head mount_ctx_dup;
+	struct list_head mount_ctx_free;
+	struct list_head mount_ctx_option;
+	struct list_head mount_ctx_kern_mount;
 	struct list_head sb_alloc_security;
 	struct list_head sb_free_security;
 	struct list_head sb_copy_data;
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 8e0352af06b7..cf2583406986 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -69,6 +69,56 @@ struct vfsmount {
 	int mnt_flags;
 };
 
+struct mount_context;
+struct mount_context_operations {
+	void (*free)(struct mount_context *mc);
+	int (*dup)(struct mount_context *mc, struct mount_context *src);
+	/* An option has been specified. */
+	int (*option)(struct mount_context *mc, char *p);
+	/* Parse monolithic mount data. */
+	int (*monolithic_mount_data)(struct mount_context *mc, void *data);
+	/* Validate the mount options */
+	int (*validate)(struct mount_context *mc);
+	/* Perform the mount. */
+	struct dentry *(*mount)(struct mount_context *mc);
+	/* Fill in a superblock */
+	int (*fill_super)(struct super_block *s, struct mount_context *mc);
+};
+
+enum mount_type {
+	MOUNT_TYPE_NEW,		/* New mount made directly */
+	MOUNT_TYPE_SUBMOUNT,	/* New mount made automatically */
+	MOUNT_TYPE_REMOUNT,	/* Change of an existing mount */
+};
+
+/*
+ * Mount context as allocated and constructed by fsopen().  The filesystem must
+ * support the ->ctx_*() operations.  The size of the object allocated is in
+ * struct file_system_type::mount_context_size; this must be embedded as the
+ * fist thing in the filesystem's own context.
+ */
+struct mount_context {
+	const struct mount_context_operations *ops;
+	struct file_system_type	*fs_type;
+	struct user_namespace	*user_ns;	/* The user namespace for this mount */
+	struct mnt_namespace	*mnt_ns;	/* The mount namespace for this mount */
+	struct pid_namespace	*pid_ns;	/* The process ID namespace for this mount */
+	struct net		*net_ns;	/* The network namespace for this mount */
+	const struct cred	*cred;		/* The mounter's credentials */
+	char			*device;	/* The device name or mount target */
+	char			*root_path;	/* The path within the mount to mount */
+	void			*security;	/* The LSM context */
+	const char		*error;		/* Error string to be read by read() */
+	unsigned int		ms_flags;	/* The superblock flags (MS_*) */
+	unsigned int		mnt_flags;	/* The mount flags (MNT_*) */
+	bool			mounted;	/* Set when mounted */
+	bool			sloppy;		/* Unrecognised options are okay */
+	bool			silent;
+	enum mount_type		mount_type : 8;
+};
+
+extern const struct file_operations fs_fs_fops;
+
 struct file; /* forward dec */
 struct path;
 
@@ -90,9 +140,26 @@ struct file_system_type;
 extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
 				      int flags, const char *name,
 				      void *data);
+extern struct vfsmount *vfs_kern_mount_mc(struct mount_context *mc);
 extern struct vfsmount *vfs_submount(const struct dentry *mountpoint,
 				     struct file_system_type *type,
 				     const char *name, void *data);
+extern struct vfsmount *vfs_submount_mc(const struct dentry *mountpoint,
+					struct mount_context *mc);
+extern struct mount_context *vfs_fsopen(const char *fs_name);
+extern struct mount_context *__vfs_fsopen(struct file_system_type *fs_type,
+					  struct super_block *src_sb,
+					  unsigned int ms_flags,
+					  unsigned int mnt_flags,
+					  enum mount_type mount_type);
+extern struct mount_context *vfs_mntopen(struct vfsmount *mnt,
+					 unsigned int ms_flags,
+					 unsigned int mnt_flags,
+					 enum mount_type mount_type);
+extern struct mount_context *vfs_dup_mount_context(struct mount_context *src);
+extern int vfs_mount_option(struct mount_context *mc, char *data);
+extern int generic_monolithic_mount_data(struct mount_context *ctx, void *data);
+extern void put_mount_context(struct mount_context *ctx);
 
 extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
 extern void mark_mounts_for_expiry(struct list_head *mounts);
diff --git a/include/linux/security.h b/include/linux/security.h
index 96899fad7016..91efe3039bff 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -55,6 +55,7 @@ struct msg_queue;
 struct xattr;
 struct xfrm_sec_ctx;
 struct mm_struct;
+struct mount_context;
 
 /* If capable should audit the security request */
 #define SECURITY_CAP_NOAUDIT 0
@@ -220,6 +221,11 @@ int security_bprm_check(struct linux_binprm *bprm);
 void security_bprm_committing_creds(struct linux_binprm *bprm);
 void security_bprm_committed_creds(struct linux_binprm *bprm);
 int security_bprm_secureexec(struct linux_binprm *bprm);
+int security_mount_ctx_alloc(struct mount_context *mc, struct super_block *sb);
+int security_mount_ctx_dup(struct mount_context *mc, struct mount_context *src);
+void security_mount_ctx_free(struct mount_context *mc);
+int security_mount_ctx_option(struct mount_context *mc, char *opt);
+int security_mount_ctx_kern_mount(struct mount_context *mc, struct super_block *sb);
 int security_sb_alloc(struct super_block *sb);
 void security_sb_free(struct super_block *sb);
 int security_sb_copy_data(char *orig, char *copy);
@@ -513,6 +519,29 @@ static inline int security_bprm_secureexec(struct linux_binprm *bprm)
 	return cap_bprm_secureexec(bprm);
 }
 
+static inline int security_mount_ctx_alloc(struct mount_context *mc,
+					   struct super_block *src_sb)
+{
+	return 0;
+}
+static inline int security_mount_ctx_dup(struct mount_context *mc,
+					 struct mount_context *src)
+{
+	return 0;
+}
+static inline void security_mount_ctx_free(struct mount_context *mc)
+{
+}
+static inline int security_mount_ctx_option(struct mount_context *mc, char *opt)
+{
+	return 0;
+}
+static inline int security_mount_ctx_kern_mount(struct mount_context *mc,
+						struct super_block *sb)
+{
+	return 0;
+}
+
 static inline int security_sb_alloc(struct super_block *sb)
 {
 	return 0;
diff --git a/security/security.c b/security/security.c
index 23555c5504f6..2e522361df66 100644
--- a/security/security.c
+++ b/security/security.c
@@ -309,6 +309,31 @@ int security_bprm_secureexec(struct linux_binprm *bprm)
 	return call_int_hook(bprm_secureexec, 0, bprm);
 }
 
+int security_mount_ctx_alloc(struct mount_context *mc, struct super_block *src_sb)
+{
+	return call_int_hook(mount_ctx_alloc, 0, mc, src_sb);
+}
+
+int security_mount_ctx_dup(struct mount_context *mc, struct mount_context *src_mc)
+{
+	return call_int_hook(mount_ctx_dup, 0, mc, src_mc);
+}
+
+void security_mount_ctx_free(struct mount_context *mc)
+{
+	call_void_hook(mount_ctx_free, mc);
+}
+
+int security_mount_ctx_option(struct mount_context *mc, char *opt)
+{
+	return call_int_hook(mount_ctx_option, 0, mc, opt);
+}
+
+int security_mount_ctx_kern_mount(struct mount_context *mc, struct super_block *sb)
+{
+	return call_int_hook(mount_ctx_kern_mount, 0, mc, sb);
+}
+
 int security_sb_alloc(struct super_block *sb)
 {
 	return call_int_hook(sb_alloc_security, 0, sb);
@@ -1659,6 +1684,13 @@ struct security_hook_heads security_hook_heads = {
 		LIST_HEAD_INIT(security_hook_heads.bprm_committing_creds),
 	.bprm_committed_creds =
 		LIST_HEAD_INIT(security_hook_heads.bprm_committed_creds),
+	.mount_ctx_alloc = LIST_HEAD_INIT(security_hook_heads.mount_ctx_alloc),
+	.mount_ctx_dup = LIST_HEAD_INIT(security_hook_heads.mount_ctx_dup),
+	.mount_ctx_free = LIST_HEAD_INIT(security_hook_heads.mount_ctx_free),
+	.mount_ctx_option =
+		LIST_HEAD_INIT(security_hook_heads.mount_ctx_option),
+	.mount_ctx_kern_mount =
+		LIST_HEAD_INIT(security_hook_heads.mount_ctx_kern_mount),
 	.sb_alloc_security =
 		LIST_HEAD_INIT(security_hook_heads.sb_alloc_security),
 	.sb_free_security =
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 0c2ac318aa7f..cf38db840f71 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2826,6 +2826,179 @@ static int selinux_umount(struct vfsmount *mnt, int flags)
 				   FILESYSTEM__UNMOUNT, NULL);
 }
 
+/* fsopen mount context operations */
+
+static int selinux_mount_ctx_alloc(struct mount_context *mc,
+				   struct super_block *src_sb)
+{
+	struct security_mnt_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return -ENOMEM;
+
+	mc->security = opts;
+	return 0;
+}
+
+static int selinux_mount_ctx_dup(struct mount_context *mc,
+				 struct mount_context *src_mc)
+{
+	const struct security_mnt_opts *src = src_mc->security;
+	struct security_mnt_opts *opts;
+	int i, n;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return -ENOMEM;
+	mc->security = opts;
+
+	if (!src || !src->num_mnt_opts)
+		return 0;
+	n = opts->num_mnt_opts = src->num_mnt_opts;
+
+	if (opts->mnt_opts) {
+		opts->mnt_opts = kcalloc(n, sizeof(char *), GFP_KERNEL);
+		if (!opts->mnt_opts)
+			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;
+			}
+		}
+	}
+
+	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;
+	}
+
+	return 0;
+}
+
+static void selinux_mount_ctx_free(struct mount_context *mc)
+{
+	struct security_mnt_opts *opts = mc->security;
+
+	security_free_mnt_opts(opts);
+	mc->security = NULL;
+}
+
+static int selinux_mount_ctx_option(struct mount_context *mc, char *opt)
+{
+	struct security_mnt_opts *opts = mc->security;
+	substring_t args[MAX_OPT_ARGS];
+	unsigned int have;
+	char *c, **oo;
+	void *old;
+	int token, ctx, i;
+
+	token = match_token(opt, tokens, args);
+	if (token == Opt_error)
+		return 0; /* Doesn't belong to us. */
+
+	have = 0;
+	for (i = 0; i < opts->num_mnt_opts; i++)
+		have |= 1 << opts->mnt_opts_flags[i];
+	if (have & (1 << token)) {
+		mc->error = "SELinux: Duplicate mount options";
+		return -EINVAL;
+	}
+
+	switch (token) {
+	case Opt_context:
+		if (have & (1 << Opt_defcontext))
+			goto incompatible;
+		ctx = CONTEXT_MNT;
+		goto copy_context_string;
+
+	case Opt_fscontext:
+		ctx = FSCONTEXT_MNT;
+		goto copy_context_string;
+
+	case Opt_rootcontext:
+		ctx = ROOTCONTEXT_MNT;
+		goto copy_context_string;
+
+	case Opt_defcontext:
+		if (have & (1 << Opt_context))
+			goto incompatible;
+		ctx = DEFCONTEXT_MNT;
+		goto copy_context_string;
+
+	case Opt_labelsupport:
+		return 1;
+
+	default:
+		mc->error = "SELinux: Unknown mount option";
+		return -EINVAL;
+	}
+
+copy_context_string:
+	if (opts->num_mnt_opts > 3) {
+		mc->error = "SELinux: Too many options";
+		return -EINVAL;
+	}
+	if (!opts->mnt_opts_flags) {
+		opts->mnt_opts_flags = kcalloc(3, sizeof(int), GFP_KERNEL);
+		if (!opts->mnt_opts_flags)
+			return -ENOMEM;
+	}
+
+	if (opts->mnt_opts) {
+		oo = kmalloc((opts->num_mnt_opts + 1) * sizeof(char *),
+			     GFP_KERNEL);
+		if (!oo)
+			return -ENOMEM;
+		memcpy(oo, opts->mnt_opts, opts->num_mnt_opts * sizeof(char *));
+		oo[opts->num_mnt_opts] = NULL;
+		old = opts->mnt_opts;
+		opts->mnt_opts = oo;
+		kfree(old);
+	}
+
+	c = match_strdup(&args[0]);
+	if (!c)
+		return -ENOMEM;
+	opts->mnt_opts[opts->num_mnt_opts] = c;
+	opts->mnt_opts_flags[opts->num_mnt_opts] = ctx;
+	opts->num_mnt_opts++;
+	return 1;
+
+incompatible:
+	mc->error = "SELinux: Incompatible mount options";
+	return -EINVAL;
+}
+
+static int selinux_mount_ctx_kern_mount(struct mount_context *mc,
+					struct super_block *sb)
+{
+	const struct cred *cred = current_cred();
+	struct common_audit_data ad;
+	int rc;
+
+	rc = selinux_set_mnt_opts(sb, mc->security, 0, NULL);
+	if (rc)
+		return rc;
+
+	/* Allow all mounts performed by the kernel */
+	if (mc->ms_flags & MS_KERNMOUNT)
+		return 0;
+
+	ad.type = LSM_AUDIT_DATA_DENTRY;
+	ad.u.dentry = sb->s_root;
+	rc = superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
+	if (rc < 0)
+		mc->error = "SELinux: Mount of superblock not permitted";
+	return rc;
+}
+
 /* inode security operations */
 
 static int selinux_inode_alloc_security(struct inode *inode)
@@ -6131,6 +6304,12 @@ static struct security_hook_list selinux_hooks[] = {
 	LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
 	LSM_HOOK_INIT(bprm_secureexec, selinux_bprm_secureexec),
 
+	LSM_HOOK_INIT(mount_ctx_alloc, selinux_mount_ctx_alloc),
+	LSM_HOOK_INIT(mount_ctx_dup, selinux_mount_ctx_dup),
+	LSM_HOOK_INIT(mount_ctx_free, selinux_mount_ctx_free),
+	LSM_HOOK_INIT(mount_ctx_option, selinux_mount_ctx_option),
+	LSM_HOOK_INIT(mount_ctx_kern_mount, selinux_mount_ctx_kern_mount),
+
 	LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
 	LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
 	LSM_HOOK_INIT(sb_copy_data, selinux_sb_copy_data),

  parent reply	other threads:[~2017-05-03 16:07 UTC|newest]

Thread overview: 71+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-03 16:04 [RFC][PATCH 0/9] VFS: Introduce mount context David Howells
2017-05-03 16:04 ` [PATCH 1/9] Provide a function to create a NUL-terminated string from unterminated data David Howells
2017-05-03 16:55   ` Jeff Layton
2017-05-03 19:26   ` Rasmus Villemoes
2017-05-03 20:13   ` David Howells
2017-05-03 16:04 ` [PATCH 2/9] Clean up whitespace in fs/namespace.c David Howells
2017-05-03 16:04 ` David Howells [this message]
2017-05-03 18:13   ` [PATCH 3/9] VFS: Introduce a mount context Jeff Layton
2017-05-03 18:26     ` Joe Perches
2017-05-03 20:38       ` Matthew Wilcox
2017-05-03 21:36         ` Joe Perches
2017-05-03 21:36           ` [Cocci] " Joe Perches
2017-05-03 21:36           ` Joe Perches
2017-05-04  6:28           ` Julia Lawall
2017-05-04  6:28             ` [Cocci] " Julia Lawall
2017-05-04  6:28             ` Julia Lawall
2017-05-03 21:17       ` David Howells
2017-05-03 18:37     ` David Howells
2017-05-03 18:43       ` Joe Perches
2017-05-03 20:11       ` David Howells
2017-05-04  9:27     ` David Howells
2017-05-04 14:34       ` Joe Perches
2017-05-03 21:43   ` Rasmus Villemoes
2017-05-04 10:22   ` David Howells
2017-05-08 15:05   ` Miklos Szeredi
2017-05-08 22:57   ` David Howells
2017-05-09  8:03     ` Miklos Szeredi
2017-05-10 12:41       ` Karel Zak
2017-05-09  9:32     ` David Howells
2017-05-09 11:04       ` Miklos Szeredi
2017-05-09  9:41     ` David Howells
2017-05-09 12:02       ` Miklos Szeredi
2017-05-09 18:51         ` Jeff Layton
2017-05-10  7:24           ` Miklos Szeredi
2017-05-10  8:05           ` David Howells
2017-05-10 13:20             ` Jeff Layton
2017-05-10 13:30               ` Miklos Szeredi
2017-05-10 13:33                 ` Miklos Szeredi
2017-05-10 13:48                 ` Jeff Layton
2017-05-12  8:15                   ` Miklos Szeredi
2017-05-10 13:31             ` David Howells
2017-05-10 13:37               ` Jeff Layton
2017-05-09  9:56     ` David Howells
2017-05-09 12:38       ` Miklos Szeredi
2017-05-03 16:05 ` [PATCH 4/9] Implement fsopen() to prepare for a mount David Howells
2017-05-03 18:37   ` Jeff Layton
2017-05-03 18:41   ` David Howells
2017-05-03 20:44   ` Rasmus Villemoes
2017-05-04 10:40   ` Karel Zak
2017-05-04 12:55   ` David Howells
2017-05-04 12:58   ` David Howells
2017-05-04 13:06   ` David Howells
2017-05-04 13:34     ` Karel Zak
2017-05-09 18:40       ` Jeff Layton
2017-05-08 15:10   ` Miklos Szeredi
2017-05-08 23:09   ` David Howells
2017-05-03 16:05 ` [PATCH 5/9] Implement fsmount() to effect a pre-configured mount David Howells
2017-05-03 16:05 ` [PATCH 6/9] Sample program for driving fsopen/fsmount David Howells
2017-05-03 16:05 ` [PATCH 7/9] procfs: Move proc_fill_super() to fs/proc/root.c David Howells
2017-05-03 16:05 ` [PATCH 8/9] proc: Support the mount context in procfs David Howells
2017-05-03 16:05 ` [PATCH 9/9] NFS: Support the mount context and fsopen() David Howells
2017-05-03 16:44 ` [RFC][PATCH 0/9] VFS: Introduce mount context Jeff Layton
2017-05-03 16:50 ` David Howells
2017-05-03 17:27   ` Jeff Layton
2017-05-05 14:35 ` Miklos Szeredi
2017-05-05 15:47 ` David Howells
2017-05-08  8:25   ` Miklos Szeredi
2017-05-08  8:35 ` David Howells
2017-05-08  8:43   ` Miklos Szeredi
2017-05-08 17:03 ` Djalal Harouni
2017-05-08 17:03   ` Djalal Harouni

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=149382749941.30481.11685229083280551867.stgit@warthog.procyon.org.uk \
    --to=dhowells@redhat.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=mszeredi@redhat.com \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.