linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
@ 2019-03-27 23:40 ` David Howells
  2019-03-28  0:53   ` Masahiro Yamada
  2019-04-10  9:54   ` David Howells
  2019-03-27 23:40 ` [RFC PATCH 02/68] vfs: Update mount API docs David Howells
                   ` (66 subsequent siblings)
  67 siblings, 2 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:40 UTC (permalink / raw)
  To: viro
  Cc: Andreas Schwab, Masahiro Yamada, linux-fsdevel, linux-kernel, dhowells

From: Masahiro Yamada <yamada.masahiro@socionext.com>

Commit 2b50f7ab6368 ("kbuild: add workaround for Debian make-kpkg")
annoyed people who want to wrap the top Makefile with GNUmakefile
or something in order to customize it for their use.

On second thought, we do not need to run the sub-make for in-tree
build with Make 4.x because the 'MAKEFLAGS += -rR' issue only happens
on GNU Make 3.x.

With this commit, people will get back the workflow, and the Debian
make-kpkg will still work.

Fixes: 2b50f7ab6368 ("kbuild: add workaround for Debian make-kpkg")
Reported-by: Andreas Schwab <schwab@suse.de>
Reported-by: David Howells <dhowells@redhat.com>
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 Makefile |   31 +++++++++++++++----------------
 1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/Makefile b/Makefile
index c0a34064c574..cd38d203e71a 100644
--- a/Makefile
+++ b/Makefile
@@ -31,26 +31,12 @@ _all:
 # descending is started. They are now explicitly listed as the
 # prepare rule.
 
-# Ugly workaround for Debian make-kpkg:
-# make-kpkg directly includes the top Makefile of Linux kernel. In such a case,
-# skip sub-make to support debian_* targets in ruleset/kernel_version.mk, but
-# displays warning to discourage such abusage.
-ifneq ($(word 2, $(MAKEFILE_LIST)),)
-$(warning Do not include top Makefile of Linux Kernel)
-sub-make-done := 1
-MAKEFLAGS += -rR
-endif
-
 ifneq ($(sub-make-done),1)
 
 # Do not use make's built-in rules and variables
 # (this increases performance and avoids hard-to-debug behaviour)
 MAKEFLAGS += -rR
 
-# 'MAKEFLAGS += -rR' does not become immediately effective for old
-# GNU Make versions. Cancel implicit rules for this Makefile.
-$(lastword $(MAKEFILE_LIST)): ;
-
 # Avoid funny character set dependencies
 unexport LC_ALL
 LC_COLLATE=C
@@ -153,6 +139,7 @@ $(if $(KBUILD_OUTPUT),, \
 # 'sub-make' below.
 MAKEFLAGS += --include-dir=$(CURDIR)
 
+need-sub-make := 1
 else
 
 # Do not print "Entering directory ..." at all for in-tree build.
@@ -160,6 +147,15 @@ MAKEFLAGS += --no-print-directory
 
 endif # ifneq ($(KBUILD_OUTPUT),)
 
+ifneq ($(filter 3.%,$(MAKE_VERSION)),)
+# 'MAKEFLAGS += -rR' does not immediately become effective for GNU Make 3.x
+# We need to run sub-make to avoid implicit rules in the top Makefile.
+need-sub-make := 1
+# Cancel implicit rules for this Makefile.
+$(lastword $(MAKEFILE_LIST)): ;
+endif
+
+ifeq ($(need-sub-make),1)
 PHONY += $(MAKECMDGOALS) sub-make
 
 $(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
@@ -171,8 +167,11 @@ sub-make:
 	$(if $(KBUILD_OUTPUT),-C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR)) \
 	-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))
 
-else # sub-make-done
+endif # need-sub-make
+endif # sub-make-done
+
 # We process the rest of the Makefile if this is the final invocation of make
+ifeq ($(need-sub-make),)
 
 # Do not print "Entering directory ...",
 # but we want to display it when entering to the output directory
@@ -1757,7 +1756,7 @@ existing-targets := $(wildcard $(sort $(targets)))
 
 endif   # ifeq ($(config-targets),1)
 endif   # ifeq ($(mixed-targets),1)
-endif   # sub-make-done
+endif   # need-sub-make
 
 PHONY += FORCE
 FORCE:


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 02/68] vfs: Update mount API docs
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
  2019-03-27 23:40 ` [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x David Howells
@ 2019-03-27 23:40 ` David Howells
  2019-03-27 23:40 ` [RFC PATCH 03/68] vfs: Fix refcounting of filenames in fs_parser David Howells
                   ` (65 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:40 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Update the mount API docs to reflect recent changes to the code.

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

 Documentation/filesystems/mount_api.txt |  367 ++++++++++++++++---------------
 1 file changed, 195 insertions(+), 172 deletions(-)

diff --git a/Documentation/filesystems/mount_api.txt b/Documentation/filesystems/mount_api.txt
index 944d1965e917..6a902767bf27 100644
--- a/Documentation/filesystems/mount_api.txt
+++ b/Documentation/filesystems/mount_api.txt
@@ -12,11 +12,13 @@ CONTENTS
 
  (4) Filesystem context security.
 
- (5) VFS filesystem context operations.
+ (5) VFS filesystem context API.
 
- (6) Parameter description.
+ (6) Superblock creation helpers.
 
- (7) Parameter helper functions.
+ (7) Parameter description.
+
+ (8) Parameter helper functions.
 
 
 ========
@@ -41,12 +43,15 @@ The creation of new mounts is now to be done in a multistep process:
 
  (7) Destroy the context.
 
-To support this, the file_system_type struct gains a new field:
+To support this, the file_system_type struct gains two new fields:
 
 	int (*init_fs_context)(struct fs_context *fc);
+	const struct fs_parameter_description *parameters;
 
-which is invoked to set up the filesystem-specific parts of a filesystem
-context, including the additional space.
+The first is invoked to set up the filesystem-specific parts of a filesystem
+context, including the additional space, and the second points to the
+parameter description for validation at registration time and querying by a
+future system call.
 
 Note that security initialisation is done *after* the filesystem is called so
 that the namespaces may be adjusted first.
@@ -73,9 +78,9 @@ context.  This is represented by the fs_context structure:
 		void			*s_fs_info;
 		unsigned int		sb_flags;
 		unsigned int		sb_flags_mask;
+		unsigned int		s_iflags;
+		unsigned int		lsm_flags;
 		enum fs_context_purpose	purpose:8;
-		bool			sloppy:1;
-		bool			silent:1;
 		...
 	};
 
@@ -141,6 +146,10 @@ The fs_context fields are as follows:
 
      Which bits SB_* flags are to be set/cleared in super_block::s_flags.
 
+ (*) unsigned int s_iflags
+
+     These will be bitwise-OR'd with s->s_iflags when a superblock is created.
+
  (*) enum fs_context_purpose
 
      This indicates the purpose for which the context is intended.  The
@@ -150,17 +159,6 @@ The fs_context fields are as follows:
 	FS_CONTEXT_FOR_SUBMOUNT		-- New automatic submount of extant mount
 	FS_CONTEXT_FOR_RECONFIGURE	-- Change an existing mount
 
- (*) 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 sb_flags & SB_SILENT.
-
 The mount context is created by calling vfs_new_fs_context() or
 vfs_dup_fs_context() and is destroyed with put_fs_context().  Note that the
 structure is not refcounted.
@@ -342,28 +340,47 @@ number of operations used by the new mount code for this purpose:
      It should return 0 on success or a negative error code on failure.
 
 
-=================================
-VFS FILESYSTEM CONTEXT OPERATIONS
-=================================
+==========================
+VFS FILESYSTEM CONTEXT API
+==========================
 
-There are four operations for creating a filesystem context and
-one for destroying a context:
+There are four operations for creating a filesystem context and one for
+destroying a context:
 
- (*) struct fs_context *vfs_new_fs_context(struct file_system_type *fs_type,
-					   struct dentry *reference,
-					   unsigned int sb_flags,
-					   unsigned int sb_flags_mask,
-					   enum fs_context_purpose purpose);
+ (*) struct fs_context *fs_context_for_mount(
+		struct file_system_type *fs_type,
+		unsigned int sb_flags);
 
-     Create a filesystem context for a given filesystem type and purpose.  This
-     allocates the filesystem context, sets the superblock flags, initialises
-     the security and calls fs_type->init_fs_context() to initialise the
-     filesystem private data.
+     Allocate a filesystem context for the purpose of setting up a new mount,
+     whether that be with a new superblock or sharing an existing one.  This
+     sets the superblock flags, initialises the security and calls
+     fs_type->init_fs_context() to initialise the filesystem private data.
 
-     reference can be NULL or it may indicate the root dentry of a superblock
-     that is going to be reconfigured (FS_CONTEXT_FOR_RECONFIGURE) or
-     the automount point that triggered a submount (FS_CONTEXT_FOR_SUBMOUNT).
-     This is provided as a source of namespace information.
+     fs_type specifies the filesystem type that will manage the context and
+     sb_flags presets the superblock flags stored therein.
+
+ (*) struct fs_context *fs_context_for_reconfigure(
+		struct dentry *dentry,
+		unsigned int sb_flags,
+		unsigned int sb_flags_mask);
+
+     Allocate a filesystem context for the purpose of reconfiguring an
+     existing superblock.  dentry provides a reference to the superblock to be
+     configured.  sb_flags and sb_flags_mask indicate which superblock flags
+     need changing and to what.
+
+ (*) struct fs_context *fs_context_for_submount(
+		struct file_system_type *fs_type,
+		struct dentry *reference);
+
+     Allocate a filesystem context for the purpose of creating a new mount for
+     an automount point or other derived superblock.  fs_type specifies the
+     filesystem type that will manage the context and the reference dentry
+     supplies the parameters.  Namespaces are propagated from the reference
+     dentry's superblock also.
+
+     Note that it's not a requirement that the reference dentry be of the same
+     filesystem type as fs_type.
 
  (*) struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc);
 
@@ -390,20 +407,6 @@ context pointer or a negative error code.
 For the remaining operations, if an error occurs, a negative error code will be
 returned.
 
- (*) int vfs_get_tree(struct fs_context *fc);
-
-     Get or create the mountable root and superblock, using the parameters in
-     the filesystem context to select/configure the superblock.  This invokes
-     the ->validate() op and then the ->get_tree() op.
-
-     [NOTE] ->validate() could perhaps be rolled into ->get_tree() and
-     ->reconfigure().
-
- (*) struct vfsmount *vfs_create_mount(struct fs_context *fc);
-
-     Create a mount given the parameters in the specified filesystem context.
-     Note that this does not attach the mount to anything.
-
  (*) int vfs_parse_fs_param(struct fs_context *fc,
 			    struct fs_parameter *param);
 
@@ -432,17 +435,80 @@ returned.
      clear the pointer, but then becomes responsible for disposing of the
      object.
 
- (*) int vfs_parse_fs_string(struct fs_context *fc, char *key,
+ (*) int vfs_parse_fs_string(struct fs_context *fc, const char *key,
 			     const char *value, size_t v_size);
 
-     A wrapper around vfs_parse_fs_param() that just passes a constant string.
+     A wrapper around vfs_parse_fs_param() that copies the value string it is
+     passed.
 
  (*) int generic_parse_monolithic(struct fs_context *fc, 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
-     ->parse_monolithic() operation is NULL.
+     ->parse_monolithic() method is NULL.
+
+ (*) int vfs_get_tree(struct fs_context *fc);
+
+     Get or create the mountable root and superblock, using the parameters in
+     the filesystem context to select/configure the superblock.  This invokes
+     the ->get_tree() method.
+
+ (*) struct vfsmount *vfs_create_mount(struct fs_context *fc);
+
+     Create a mount given the parameters in the specified filesystem context.
+     Note that this does not attach the mount to anything.
+
+
+===========================
+SUPERBLOCK CREATION HELPERS
+===========================
+
+A number of VFS helpers are available for use by filesystems for the creation
+or looking up of superblocks.
+
+ (*) struct super_block *
+     sget_fc(struct fs_context *fc,
+	     int (*test)(struct super_block *sb, struct fs_context *fc),
+	     int (*set)(struct super_block *sb, struct fs_context *fc));
+
+     This is the core routine.  If test is non-NULL, it searches for an
+     existing superblock matching the criteria held in the fs_context, using
+     the test function to match them.  If no match is found, a new superblock
+     is created and the set function is called to set it up.
+
+     Prior to the set function being called, fc->s_fs_info will be transferred
+     to sb->s_fs_info - and fc->s_fs_info will be cleared if set returns
+     success (ie. 0).
+
+The following helpers all wrap sget_fc():
+
+ (*) int vfs_get_super(struct fs_context *fc,
+		       enum vfs_get_super_keying keying,
+		       int (*fill_super)(struct super_block *sb,
+					 struct fs_context *fc))
+
+     This creates/looks up a deviceless superblock.  The keying indicates how
+     many superblocks of this type may exist and in what manner they may be
+     shared:
+
+	(1) vfs_get_single_super
+
+	    Only one such superblock may exist in the system.  Any further
+	    attempt to get a new superblock gets this one (and any parameter
+	    differences are ignored).
+
+	(2) vfs_get_keyed_super
+
+	    Multiple superblocks of this type may exist and they're keyed on
+	    their s_fs_info pointer (for example this may refer to a
+	    namespace).
+
+	(3) vfs_get_independent_super
+
+	    Multiple independent superblocks of this type may exist.  This
+	    function never matches an existing one and always creates a new
+	    one.
 
 
 =====================
@@ -454,35 +520,22 @@ There's a core description struct that links everything together:
 
 	struct fs_parameter_description {
 		const char	name[16];
-		u8		nr_params;
-		u8		nr_alt_keys;
-		u8		nr_enums;
-		bool		ignore_unknown;
-		bool		no_source;
-		const char *const *keys;
-		const struct constant_table *alt_keys;
 		const struct fs_parameter_spec *specs;
 		const struct fs_parameter_enum *enums;
 	};
 
 For example:
 
-	enum afs_param {
+	enum {
 		Opt_autocell,
 		Opt_bar,
 		Opt_dyn,
 		Opt_foo,
 		Opt_source,
-		nr__afs_params
 	};
 
 	static const struct fs_parameter_description afs_fs_parameters = {
 		.name		= "kAFS",
-		.nr_params	= nr__afs_params,
-		.nr_alt_keys	= ARRAY_SIZE(afs_param_alt_keys),
-		.nr_enums	= ARRAY_SIZE(afs_param_enums),
-		.keys		= afs_param_keys,
-		.alt_keys	= afs_param_alt_keys,
 		.specs		= afs_param_specs,
 		.enums		= afs_param_enums,
 	};
@@ -494,28 +547,24 @@ The members are as follows:
      The name to be used in error messages generated by the parse helper
      functions.
 
- (2) u8 nr_params;
-
-     The number of discrete parameter identifiers.  This indicates the number
-     of elements in the ->types[] array and also limits the values that may be
-     used in the values that the ->keys[] array maps to.
-
-     It is expected that, for example, two parameters that are related, say
-     "acl" and "noacl" with have the same ID, but will be flagged to indicate
-     that one is the inverse of the other.  The value can then be picked out
-     from the parse result.
+ (2) const struct fs_parameter_specification *specs;
 
- (3) const struct fs_parameter_specification *specs;
+     Table of parameter specifications, terminated with a null entry, where the
+     entries are of type:
 
-     Table of parameter specifications, where the entries are of type:
-
-	struct fs_parameter_type {
-		enum fs_parameter_spec	type:8;
-		u8			flags;
+	struct fs_parameter_spec {
+		const char		*name;
+		u8			opt;
+		enum fs_parameter_type	type:8;
+		unsigned short		flags;
 	};
 
-     and the parameter identifier is the index to the array.  'type' indicates
-     the desired value type and must be one of:
+     The 'name' field is a string to match exactly to the parameter key (no
+     wildcards, patterns and no case-independence) and 'opt' is the value that
+     will be returned by the fs_parser() function in the case of a successful
+     match.
+
+     The 'type' field indicates the desired value type and must be one of:
 
 	TYPE NAME		EXPECTED VALUE		RESULT IN
 	=======================	=======================	=====================
@@ -525,85 +574,65 @@ The members are as follows:
 	fs_param_is_u32_octal	32-bit octal int	result->uint_32
 	fs_param_is_u32_hex	32-bit hex int		result->uint_32
 	fs_param_is_s32		32-bit signed int	result->int_32
+	fs_param_is_u64		64-bit unsigned int	result->uint_64
 	fs_param_is_enum	Enum value name 	result->uint_32
 	fs_param_is_string	Arbitrary string	param->string
 	fs_param_is_blob	Binary blob		param->blob
 	fs_param_is_blockdev	Blockdev path		* Needs lookup
 	fs_param_is_path	Path			* Needs lookup
-	fs_param_is_fd		File descriptor		param->file
-
-     And each parameter can be qualified with 'flags':
-
-     	fs_param_v_optional	The value is optional
-	fs_param_neg_with_no	If key name is prefixed with "no", it is false
-	fs_param_neg_with_empty	If value is "", it is false
-	fs_param_deprecated	The parameter is deprecated.
-
-     For example:
-
-	static const struct fs_parameter_spec afs_param_specs[nr__afs_params] = {
-		[Opt_autocell]	= { fs_param_is flag },
-		[Opt_bar]	= { fs_param_is_enum },
-		[Opt_dyn]	= { fs_param_is flag },
-		[Opt_foo]	= { fs_param_is_bool, fs_param_neg_with_no },
-		[Opt_source]	= { fs_param_is_string },
-	};
+	fs_param_is_fd		File descriptor		result->int_32
 
      Note that if the value is of fs_param_is_bool type, fs_parse() will try
      to match any string value against "0", "1", "no", "yes", "false", "true".
 
-     [!] NOTE that the table must be sorted according to primary key name so
-     	 that ->keys[] is also sorted.
-
- (4) const char *const *keys;
-
-     Table of primary key names for the parameters.  There must be one entry
-     per defined parameter.  The table is optional if ->nr_params is 0.  The
-     table is just an array of names e.g.:
-
-	static const char *const afs_param_keys[nr__afs_params] = {
-		[Opt_autocell]	= "autocell",
-		[Opt_bar]	= "bar",
-		[Opt_dyn]	= "dyn",
-		[Opt_foo]	= "foo",
-		[Opt_source]	= "source",
-	};
-
-     [!] NOTE that the table must be sorted such that the table can be searched
-     	 with bsearch() using strcmp().  This means that the Opt_* values must
-     	 correspond to the entries in this table.
-
- (5) const struct constant_table *alt_keys;
-     u8 nr_alt_keys;
+     Each parameter can also be qualified with 'flags':
 
-     Table of additional key names and their mappings to parameter ID plus the
-     number of elements in the table.  This is optional.  The table is just an
-     array of { name, integer } pairs, e.g.:
+     	fs_param_v_optional	The value is optional
+	fs_param_neg_with_no	result->negated set if key is prefixed with "no"
+	fs_param_neg_with_empty	result->negated set if value is ""
+	fs_param_deprecated	The parameter is deprecated.
 
-	static const struct constant_table afs_param_keys[] = {
-		{ "baz",	Opt_bar },
-		{ "dynamic",	Opt_dyn },
+     These are wrapped with a number of convenience wrappers:
+
+	MACRO			SPECIFIES
+	=======================	===============================================
+	fsparam_flag()		fs_param_is_flag
+	fsparam_flag_no()	fs_param_is_flag, fs_param_neg_with_no
+	fsparam_bool()		fs_param_is_bool
+	fsparam_u32()		fs_param_is_u32
+	fsparam_u32oct()	fs_param_is_u32_octal
+	fsparam_u32hex()	fs_param_is_u32_hex
+	fsparam_s32()		fs_param_is_s32
+	fsparam_u64()		fs_param_is_u64
+	fsparam_enum()		fs_param_is_enum
+	fsparam_string()	fs_param_is_string
+	fsparam_blob()		fs_param_is_blob
+	fsparam_bdev()		fs_param_is_blockdev
+	fsparam_path()		fs_param_is_path
+	fsparam_fd()		fs_param_is_fd
+
+     all of which take two arguments, name string and option number - for
+     example:
+
+	static const struct fs_parameter_spec afs_param_specs[] = {
+		fsparam_flag	("autocell",	Opt_autocell),
+		fsparam_flag	("dyn",		Opt_dyn),
+		fsparam_string	("source",	Opt_source),
+		fsparam_flag_no	("foo",		Opt_foo),
+		{}
 	};
 
-     [!] NOTE that the table must be sorted such that strcmp() can be used with
-     	 bsearch() to search the entries.
-
-     The parameter ID can also be fs_param_key_removed to indicate that a
-     deprecated parameter has been removed and that an error will be given.
-     This differs from fs_param_deprecated where the parameter may still have
-     an effect.
-
-     Further, the behaviour of the parameter may differ when an alternate name
-     is used (for instance with NFS, "v3", "v4.2", etc. are alternate names).
+     An addition macro, __fsparam() is provided that takes an additional pair
+     of arguments to specify the type and the flags for anything that doesn't
+     match one of the above macros.
 
  (6) const struct fs_parameter_enum *enums;
-     u8 nr_enums;
 
-     Table of enum value names to integer mappings and the number of elements
-     stored therein.  This is of type:
+     Table of enum value names to integer mappings, terminated with a null
+     entry.  This is of type:
 
 	struct fs_parameter_enum {
-		u8		param_id;
+		u8		opt;
 		char		name[14];
 		u8		value;
 	};
@@ -621,11 +650,6 @@ The members are as follows:
      try to look the value up in the enum table and the result will be stored
      in the parse result.
 
- (7) bool no_source;
-
-     If this is set, fs_parse() will ignore any "source" parameter and not
-     pass it to the filesystem.
-
 The parser should be pointed to by the parser pointer in the file_system_type
 struct as this will provide validation on registration (if
 CONFIG_VALIDATE_FS_PARSER=y) and will allow the description to be queried from
@@ -650,9 +674,8 @@ process the parameters it is given.
 		int		value;
 	};
 
-     and it must be sorted such that it can be searched using bsearch() using
-     strcmp().  If a match is found, the corresponding value is returned.  If a
-     match isn't found, the not_found value is returned instead.
+     If a match is found, the corresponding value is returned.  If a match
+     isn't found, the not_found value is returned instead.
 
  (*) bool validate_constant_table(const struct constant_table *tbl,
 				  size_t tbl_size,
@@ -665,36 +688,36 @@ process the parameters it is given.
      should just be set to lie inside the low-to-high range.
 
      If all is good, true is returned.  If the table is invalid, errors are
-     logged to dmesg, the stack is dumped and false is returned.
+     logged to dmesg and false is returned.
+
+ (*) bool fs_validate_description(const struct fs_parameter_description *desc);
+
+     This performs some validation checks on a parameter description.  It
+     returns true if the description is good and false if it is not.  It will
+     log errors to dmesg if validation fails.
 
  (*) int fs_parse(struct fs_context *fc,
-		  const struct fs_param_parser *parser,
+		  const struct fs_parameter_description *desc,
 		  struct fs_parameter *param,
-		  struct fs_param_parse_result *result);
+		  struct fs_parse_result *result);
 
      This is the main interpreter of parameters.  It uses the parameter
-     description (parser) to look up the name of the parameter to use and to
-     convert that to a parameter ID (stored in result->key).
+     description to look up a parameter by key name and to convert that to an
+     option number (which it returns).
 
      If successful, and if the parameter type indicates the result is a
      boolean, integer or enum type, the value is converted by this function and
-     the result stored in result->{boolean,int_32,uint_32}.
+     the result stored in result->{boolean,int_32,uint_32,uint_64}.
 
      If a match isn't initially made, the key is prefixed with "no" and no
      value is present then an attempt will be made to look up the key with the
      prefix removed.  If this matches a parameter for which the type has flag
-     fs_param_neg_with_no set, then a match will be made and the value will be
-     set to false/0/NULL.
-
-     If the parameter is successfully matched and, optionally, parsed
-     correctly, 1 is returned.  If the parameter isn't matched and
-     parser->ignore_unknown is set, then 0 is returned.  Otherwise -EINVAL is
-     returned.
-
- (*) bool fs_validate_description(const struct fs_parameter_description *desc);
+     fs_param_neg_with_no set, then a match will be made and result->negated
+     will be set to true.
 
-     This is validates the parameter description.  It returns true if the
-     description is good and false if it is not.
+     If the parameter isn't matched, -ENOPARAM will be returned; if the
+     parameter is matched, but the value is erroneous, -EINVAL will be
+     returned; otherwise the parameter's option number will be returned.
 
  (*) int fs_lookup_param(struct fs_context *fc,
 			 struct fs_parameter *value,


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 03/68] vfs: Fix refcounting of filenames in fs_parser
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
  2019-03-27 23:40 ` [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x David Howells
  2019-03-27 23:40 ` [RFC PATCH 02/68] vfs: Update mount API docs David Howells
@ 2019-03-27 23:40 ` David Howells
  2019-03-27 23:41 ` [RFC PATCH 04/68] vfs: Provide sb->s_iflags settings in fs_context struct David Howells
                   ` (64 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:40 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Fix an overput in which filename_lookup() unconditionally drops a ref to
the filename it was given, but this isn't taken account of in the caller,
fs_lookup_param().

Addresses-Coverity-ID: 1443811 ("Use after free")
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/fs_parser.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/fs_parser.c b/fs/fs_parser.c
index 842e8f749db6..be86703ec371 100644
--- a/fs/fs_parser.c
+++ b/fs/fs_parser.c
@@ -268,6 +268,7 @@ int fs_lookup_param(struct fs_context *fc,
 		return invalf(fc, "%s: not usable as path", param->key);
 	}
 
+	f->refcnt++; /* filename_lookup() drops our ref. */
 	ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
 	if (ret < 0) {
 		errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 04/68] vfs: Provide sb->s_iflags settings in fs_context struct
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (2 preceding siblings ...)
  2019-03-27 23:40 ` [RFC PATCH 03/68] vfs: Fix refcounting of filenames in fs_parser David Howells
@ 2019-03-27 23:41 ` David Howells
  2019-03-27 23:41 ` [RFC PATCH 05/68] vfs: Provide a mount_pseudo-replacement for the new mount API David Howells
                   ` (63 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:41 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Provide a field in the fs_context struct through which bits in the
sb->s_iflags superblock field can be set.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/super.c                 |    1 +
 include/linux/fs_context.h |    1 +
 2 files changed, 2 insertions(+)

diff --git a/fs/super.c b/fs/super.c
index 583a0124bc39..6c3acd633cca 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -543,6 +543,7 @@ struct super_block *sget_fc(struct fs_context *fc,
 	}
 	fc->s_fs_info = NULL;
 	s->s_type = fc->fs_type;
+	s->s_iflags |= fc->s_iflags;
 	strlcpy(s->s_id, s->s_type->name, sizeof(s->s_id));
 	list_add_tail(&s->s_list, &super_blocks);
 	hlist_add_head(&s->s_instances, &s->s_type->fs_supers);
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index eaca452088fa..98b44ad89378 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -86,6 +86,7 @@ struct fs_context {
 	void			*s_fs_info;	/* Proposed s_fs_info */
 	unsigned int		sb_flags;	/* Proposed superblock flags (SB_*) */
 	unsigned int		sb_flags_mask;	/* Superblock flags that were changed */
+	unsigned int		s_iflags;	/* OR'd with sb->s_iflags */
 	unsigned int		lsm_flags;	/* Information flags from the fs to the LSM */
 	enum fs_context_purpose	purpose:8;
 	bool			need_free:1;	/* Need to call ops->free() */


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 05/68] vfs: Provide a mount_pseudo-replacement for the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (3 preceding siblings ...)
  2019-03-27 23:41 ` [RFC PATCH 04/68] vfs: Provide sb->s_iflags settings in fs_context struct David Howells
@ 2019-03-27 23:41 ` David Howells
  2019-03-27 23:41 ` [RFC PATCH 06/68] vfs: Convert aio to use " David Howells
                   ` (62 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:41 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Provide a function, vfs_init_pseudo_fs_context(), that provides a common
infrastructure for converting pseudo-filesystems that can never be
mountable.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/libfs.c                 |   93 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs_context.h |    7 +++
 2 files changed, 100 insertions(+)

diff --git a/fs/libfs.c b/fs/libfs.c
index 0fb590d79f30..ceaf69c885c2 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -16,6 +16,7 @@
 #include <linux/exportfs.h>
 #include <linux/writeback.h>
 #include <linux/buffer_head.h> /* sync_mapping_buffers */
+#include <linux/fs_context.h>
 
 #include <linux/uaccess.h>
 
@@ -233,6 +234,98 @@ static const struct super_operations simple_super_operations = {
 	.statfs		= simple_statfs,
 };
 
+struct pseudo_fs_context {
+	struct qstr d_name;
+	const struct super_operations *ops;
+	const struct xattr_handler **xattr;
+	const struct dentry_operations *dops;
+	unsigned long magic;
+};
+
+static int pseudo_fs_get_tree(struct fs_context *fc)
+{
+	struct pseudo_fs_context *ctx = fc->fs_private;
+	struct super_block *s;
+	struct dentry *dentry;
+	struct inode *root;
+
+	s = sget_userns(fc->fs_type, NULL, set_anon_super, SB_KERNMOUNT|SB_NOUSER,
+			&init_user_ns, NULL);
+	if (IS_ERR(s))
+		return PTR_ERR(s);
+
+	s->s_maxbytes = MAX_LFS_FILESIZE;
+	s->s_blocksize = PAGE_SIZE;
+	s->s_blocksize_bits = PAGE_SHIFT;
+	s->s_magic = ctx->magic;
+	s->s_op = ctx->ops ?: &simple_super_operations;
+	s->s_xattr = ctx->xattr;
+	s->s_time_gran = 1;
+	root = new_inode(s);
+	if (!root)
+		goto Enomem;
+	/*
+	 * since this is the first inode, make it number 1. New inodes created
+	 * after this must take care not to collide with it (by passing
+	 * max_reserved of 1 to iunique).
+	 */
+	root->i_ino = 1;
+	root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
+	root->i_atime = root->i_mtime = root->i_ctime = current_time(root);
+	dentry = __d_alloc(s, &ctx->d_name);
+	if (!dentry) {
+		iput(root);
+		goto Enomem;
+	}
+	d_instantiate(dentry, root);
+	s->s_root = dentry;
+	s->s_d_op = ctx->dops;
+	s->s_flags |= SB_ACTIVE;
+	fc->root = dget(s->s_root);
+	return 0;
+
+Enomem:
+	deactivate_locked_super(s);
+	return -ENOMEM;
+}
+
+static void pseudo_fs_free(struct fs_context *fc)
+{
+	kfree(fc->fs_private);
+}
+
+static const struct fs_context_operations pseudo_fs_context_ops = {
+	.free		= pseudo_fs_free,
+	.get_tree	= pseudo_fs_get_tree,
+};
+
+/*
+ * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that
+ * will never be mountable)
+ */
+int vfs_init_pseudo_fs_context(struct fs_context *fc,
+			       const char *name,
+			       const struct super_operations *ops,
+			       const struct xattr_handler **xattr,
+			       const struct dentry_operations *dops,
+			       unsigned long magic)
+{
+	struct pseudo_fs_context *ctx;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+	ctx->d_name = (struct qstr)QSTR_INIT(name, strlen(name));
+	ctx->ops = ops;
+	ctx->xattr = xattr;
+	ctx->dops = dops;
+	ctx->magic = magic;
+	fc->fs_private = ctx;
+	fc->ops = &pseudo_fs_context_ops;
+	return 0;
+}
+EXPORT_SYMBOL(vfs_init_pseudo_fs_context);
+
 /*
  * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that
  * will never be mountable)
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 98b44ad89378..a779022d06f5 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -121,6 +121,13 @@ extern int generic_parse_monolithic(struct fs_context *fc, void *data);
 extern int vfs_get_tree(struct fs_context *fc);
 extern void put_fs_context(struct fs_context *fc);
 
+extern int vfs_init_pseudo_fs_context(struct fs_context *fc,
+				      const char *name,
+				      const struct super_operations *ops,
+				      const struct xattr_handler **xattr,
+				      const struct dentry_operations *dops,
+				      unsigned long magic);
+
 /*
  * sget() wrapper to be called from the ->get_tree() op.
  */


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 06/68] vfs: Convert aio to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (4 preceding siblings ...)
  2019-03-27 23:41 ` [RFC PATCH 05/68] vfs: Provide a mount_pseudo-replacement for the new mount API David Howells
@ 2019-03-27 23:41 ` David Howells
  2019-03-27 23:41 ` [RFC PATCH 07/68] vfs: Convert anon_inodes " David Howells
                   ` (61 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:41 UTC (permalink / raw)
  To: viro; +Cc: Benjamin LaHaise, linux-aio, linux-fsdevel, linux-kernel, dhowells

Convert the aio filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Benjamin LaHaise <bcrl@kvack.org>
cc: linux-aio@kvack.org
---

 fs/aio.c |   15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/fs/aio.c b/fs/aio.c
index 38b741aef0bf..42bf0c83da66 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -42,6 +42,7 @@
 #include <linux/ramfs.h>
 #include <linux/percpu-refcount.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 
 #include <asm/kmap_types.h>
 #include <linux/uaccess.h>
@@ -250,15 +251,11 @@ static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages)
 	return file;
 }
 
-static struct dentry *aio_mount(struct file_system_type *fs_type,
-				int flags, const char *dev_name, void *data)
+static int aio_init_fs_context(struct fs_context *fc)
 {
-	struct dentry *root = mount_pseudo(fs_type, "aio:", NULL, NULL,
-					   AIO_RING_MAGIC);
-
-	if (!IS_ERR(root))
-		root->d_sb->s_iflags |= SB_I_NOEXEC;
-	return root;
+	fc->s_iflags |= SB_I_NOEXEC;
+	return vfs_init_pseudo_fs_context(fc, "aio:",
+					  NULL, NULL, NULL, AIO_RING_MAGIC);
 }
 
 /* aio_setup
@@ -269,7 +266,7 @@ static int __init aio_setup(void)
 {
 	static struct file_system_type aio_fs = {
 		.name		= "aio",
-		.mount		= aio_mount,
+		.init_fs_context = aio_init_fs_context,
 		.kill_sb	= kill_anon_super,
 	};
 	aio_mnt = kern_mount(&aio_fs);


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 07/68] vfs: Convert anon_inodes to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (5 preceding siblings ...)
  2019-03-27 23:41 ` [RFC PATCH 06/68] vfs: Convert aio to use " David Howells
@ 2019-03-27 23:41 ` David Howells
  2019-03-27 23:41 ` [RFC PATCH 08/68] vfs: Convert bdev " David Howells
                   ` (60 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:41 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Convert the anon_inodes filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/anon_inodes.c |   12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
index 91262c34b797..e05d01e5b28f 100644
--- a/fs/anon_inodes.c
+++ b/fs/anon_inodes.c
@@ -19,6 +19,7 @@
 #include <linux/kernel.h>
 #include <linux/magic.h>
 #include <linux/anon_inodes.h>
+#include <linux/fs_context.h>
 
 #include <linux/uaccess.h>
 
@@ -38,16 +39,17 @@ static const struct dentry_operations anon_inodefs_dentry_operations = {
 	.d_dname	= anon_inodefs_dname,
 };
 
-static struct dentry *anon_inodefs_mount(struct file_system_type *fs_type,
-				int flags, const char *dev_name, void *data)
+static int anon_inodefs_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type, "anon_inode:", NULL,
-			&anon_inodefs_dentry_operations, ANON_INODE_FS_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "anon_inode:",
+					  NULL, NULL,
+					  &anon_inodefs_dentry_operations,
+					  ANON_INODE_FS_MAGIC);
 }
 
 static struct file_system_type anon_inode_fs_type = {
 	.name		= "anon_inodefs",
-	.mount		= anon_inodefs_mount,
+	.init_fs_context = anon_inodefs_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 08/68] vfs: Convert bdev to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (6 preceding siblings ...)
  2019-03-27 23:41 ` [RFC PATCH 07/68] vfs: Convert anon_inodes " David Howells
@ 2019-03-27 23:41 ` David Howells
  2019-03-27 23:41 ` [RFC PATCH 09/68] vfs: Convert nsfs " David Howells
                   ` (59 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:41 UTC (permalink / raw)
  To: viro; +Cc: Jens Axboe, linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Convert the bdev filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jens Axboe <axboe@kernel.dk>
cc: linux-fsdevel@vger.kernel.org
---

 fs/block_dev.c |   14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/fs/block_dev.c b/fs/block_dev.c
index 78d3257435c0..f3151c9c9191 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -25,6 +25,7 @@
 #include <linux/writeback.h>
 #include <linux/mpage.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/uio.h>
 #include <linux/namei.h>
 #include <linux/log2.h>
@@ -844,19 +845,16 @@ static const struct super_operations bdev_sops = {
 	.evict_inode = bdev_evict_inode,
 };
 
-static struct dentry *bd_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int bd_init_fs_context(struct fs_context *fc)
 {
-	struct dentry *dent;
-	dent = mount_pseudo(fs_type, "bdev:", &bdev_sops, NULL, BDEVFS_MAGIC);
-	if (!IS_ERR(dent))
-		dent->d_sb->s_iflags |= SB_I_CGROUPWB;
-	return dent;
+	fc->s_iflags |= SB_I_CGROUPWB;
+	return vfs_init_pseudo_fs_context(fc, "bdev:",
+					  &bdev_sops, NULL, NULL, BDEVFS_MAGIC);
 }
 
 static struct file_system_type bd_type = {
 	.name		= "bdev",
-	.mount		= bd_mount,
+	.init_fs_context = bd_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 09/68] vfs: Convert nsfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (7 preceding siblings ...)
  2019-03-27 23:41 ` [RFC PATCH 08/68] vfs: Convert bdev " David Howells
@ 2019-03-27 23:41 ` David Howells
  2019-03-27 23:41 ` [RFC PATCH 10/68] vfs: Convert pipe " David Howells
                   ` (58 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:41 UTC (permalink / raw)
  To: viro
  Cc: Eric W. Biederman, linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Convert the nsfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Eric W. Biederman <ebiederm@xmission.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/nsfs.c |   13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/fs/nsfs.c b/fs/nsfs.c
index 60702d677bd4..1a86ab9abaf7 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/proc_ns.h>
@@ -262,15 +263,17 @@ static const struct super_operations nsfs_ops = {
 	.evict_inode = nsfs_evict,
 	.show_path = nsfs_show_path,
 };
-static struct dentry *nsfs_mount(struct file_system_type *fs_type,
-			int flags, const char *dev_name, void *data)
+
+static int nsfs_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type, "nsfs:", &nsfs_ops,
-			&ns_dentry_operations, NSFS_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "nsfs:",
+					  &nsfs_ops, NULL,
+					  &ns_dentry_operations, NSFS_MAGIC);
 }
+
 static struct file_system_type nsfs = {
 	.name = "nsfs",
-	.mount = nsfs_mount,
+	.init_fs_context = nsfs_init_fs_context,
 	.kill_sb = kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 10/68] vfs: Convert pipe to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (8 preceding siblings ...)
  2019-03-27 23:41 ` [RFC PATCH 09/68] vfs: Convert nsfs " David Howells
@ 2019-03-27 23:41 ` David Howells
  2019-03-27 23:41 ` [RFC PATCH 11/68] vfs: Convert zsmalloc " David Howells
                   ` (57 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:41 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Convert the pipe filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/pipe.c |   12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/fs/pipe.c b/fs/pipe.c
index 070aad543382..14f029d1897f 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -14,6 +14,7 @@
 #include <linux/fs.h>
 #include <linux/log2.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/magic.h>
 #include <linux/pipe_fs_i.h>
 #include <linux/uio.h>
@@ -1182,16 +1183,17 @@ static const struct super_operations pipefs_ops = {
  * any operations on the root directory. However, we need a non-trivial
  * d_name - pipe: will go nicely and kill the special-casing in procfs.
  */
-static struct dentry *pipefs_mount(struct file_system_type *fs_type,
-			 int flags, const char *dev_name, void *data)
+
+static int pipefs_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type, "pipe:", &pipefs_ops,
-			&pipefs_dentry_operations, PIPEFS_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "pipe:",
+					  &pipefs_ops, NULL,
+					  &pipefs_dentry_operations, PIPEFS_MAGIC);
 }
 
 static struct file_system_type pipe_fs_type = {
 	.name		= "pipefs",
-	.mount		= pipefs_mount,
+	.init_fs_context = pipefs_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 11/68] vfs: Convert zsmalloc to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (9 preceding siblings ...)
  2019-03-27 23:41 ` [RFC PATCH 10/68] vfs: Convert pipe " David Howells
@ 2019-03-27 23:41 ` David Howells
  2019-03-27 23:42 ` [RFC PATCH 12/68] vfs: Convert sockfs " David Howells
                   ` (56 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:41 UTC (permalink / raw)
  To: viro
  Cc: Minchan Kim, Nitin Gupta, Sergey Senozhatsky, linux-mm,
	linux-fsdevel, linux-kernel, dhowells

Convert the zsmalloc filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Minchan Kim <minchan@kernel.org>
cc: Nitin Gupta <ngupta@vflare.org>
cc: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
cc: linux-mm@kvack.org
---

 mm/zsmalloc.c |   19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 0787d33b80d8..02bfc7f70fab 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -52,6 +52,7 @@
 #include <linux/zsmalloc.h>
 #include <linux/zpool.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/migrate.h>
 #include <linux/pagemap.h>
 #include <linux/fs.h>
@@ -1814,19 +1815,21 @@ static void lock_zspage(struct zspage *zspage)
 	} while ((page = get_next_page(page)) != NULL);
 }
 
-static struct dentry *zs_mount(struct file_system_type *fs_type,
-				int flags, const char *dev_name, void *data)
-{
-	static const struct dentry_operations ops = {
-		.d_dname = simple_dname,
-	};
+static const struct dentry_operations zs_dentry_operations = {
+	.d_dname = simple_dname,
+};
 
-	return mount_pseudo(fs_type, "zsmalloc:", NULL, &ops, ZSMALLOC_MAGIC);
+
+static int zs_init_fs_context(struct fs_context *fc)
+{
+	return vfs_init_pseudo_fs_context(fc, "zsmalloc:",
+					  NULL, NULL,
+					  &zs_dentry_operations, ZSMALLOC_MAGIC);
 }
 
 static struct file_system_type zsmalloc_fs = {
 	.name		= "zsmalloc",
-	.mount		= zs_mount,
+	.init_fs_context = zs_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 12/68] vfs: Convert sockfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (10 preceding siblings ...)
  2019-03-27 23:41 ` [RFC PATCH 11/68] vfs: Convert zsmalloc " David Howells
@ 2019-03-27 23:42 ` David Howells
  2019-03-27 23:42 ` [RFC PATCH 13/68] vfs: Convert dax " David Howells
                   ` (55 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:42 UTC (permalink / raw)
  To: viro; +Cc: netdev, linux-fsdevel, linux-kernel, dhowells

Convert the sockfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: netdev@vger.kernel.org
---

 net/socket.c |   14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/net/socket.c b/net/socket.c
index 3c176a12fe48..40d0ed85d61a 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -79,6 +79,7 @@
 #include <linux/module.h>
 #include <linux/highmem.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/compat.h>
@@ -351,19 +352,20 @@ static const struct xattr_handler *sockfs_xattr_handlers[] = {
 	NULL
 };
 
-static struct dentry *sockfs_mount(struct file_system_type *fs_type,
-			 int flags, const char *dev_name, void *data)
+static int sockfs_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo_xattr(fs_type, "socket:", &sockfs_ops,
-				  sockfs_xattr_handlers,
-				  &sockfs_dentry_operations, SOCKFS_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "socket:",
+					  &sockfs_ops,
+					  sockfs_xattr_handlers,
+					  &sockfs_dentry_operations,
+					  SOCKFS_MAGIC);
 }
 
 static struct vfsmount *sock_mnt __read_mostly;
 
 static struct file_system_type sock_fs_type = {
 	.name =		"sockfs",
-	.mount =	sockfs_mount,
+	.init_fs_context = sockfs_init_fs_context,
 	.kill_sb =	kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 13/68] vfs: Convert dax to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (11 preceding siblings ...)
  2019-03-27 23:42 ` [RFC PATCH 12/68] vfs: Convert sockfs " David Howells
@ 2019-03-27 23:42 ` David Howells
  2019-03-27 23:42 ` [RFC PATCH 14/68] vfs: Convert drm " David Howells
                   ` (54 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:42 UTC (permalink / raw)
  To: viro
  Cc: Dan Williams, Vishal Verma, Keith Busch, Dave Jiang,
	linux-nvdimm, linux-fsdevel, linux-kernel, dhowells

Convert the dax filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Dan Williams <dan.j.williams@intel.com>
cc: Vishal Verma <vishal.l.verma@intel.com>
cc: Keith Busch <keith.busch@intel.com>
cc: Dave Jiang <dave.jiang@intel.com>
cc: linux-nvdimm@lists.01.org
---

 drivers/dax/super.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index 0a339b85133e..d8e503678a11 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -13,6 +13,7 @@
 #include <linux/pagemap.h>
 #include <linux/module.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/magic.h>
 #include <linux/genhd.h>
 #include <linux/pfn_t.h>
@@ -440,16 +441,16 @@ static const struct super_operations dax_sops = {
 	.drop_inode = generic_delete_inode,
 };
 
-static struct dentry *dax_mount(struct file_system_type *fs_type,
-		int flags, const char *dev_name, void *data)
+static int dax_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type, "dax:", &dax_sops, NULL, DAXFS_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "dax:",
+					  &dax_sops, NULL, NULL, DAXFS_MAGIC);
 }
 
 static struct file_system_type dax_fs_type = {
-	.name = "dax",
-	.mount = dax_mount,
-	.kill_sb = kill_anon_super,
+	.name		= "dax",
+	.init_fs_context = dax_init_fs_context,
+	.kill_sb	= kill_anon_super,
 };
 
 static int dax_test(struct inode *inode, void *data)


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 14/68] vfs: Convert drm to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (12 preceding siblings ...)
  2019-03-27 23:42 ` [RFC PATCH 13/68] vfs: Convert dax " David Howells
@ 2019-03-27 23:42 ` David Howells
  2019-03-28  7:47   ` Daniel Vetter
  2019-04-10  9:59   ` David Howells
  2019-03-27 23:42 ` [RFC PATCH 15/68] vfs: Convert ia64 perfmon " David Howells
                   ` (53 subsequent siblings)
  67 siblings, 2 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:42 UTC (permalink / raw)
  To: viro
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-fsdevel,
	linux-kernel, dhowells

Convert the drm filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: David Airlie <airlied@linux.ie>
cc: Daniel Vetter <daniel@ffwll.ch>
cc: dri-devel@lists.freedesktop.org
---

 drivers/gpu/drm/drm_drv.c |   14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 381581b01d48..9eead5a478de 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -31,6 +31,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/slab.h>
 #include <linux/srcu.h>
 
@@ -413,20 +414,17 @@ static const struct super_operations drm_fs_sops = {
 	.statfs		= simple_statfs,
 };
 
-static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags,
-				   const char *dev_name, void *data)
+static int drm_fs_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type,
-			    "drm:",
-			    &drm_fs_sops,
-			    &drm_fs_dops,
-			    0x010203ff);
+	return vfs_init_pseudo_fs_context(fc, "drm:",
+					  &drm_fs_sops, NULL,
+					  &drm_fs_dops, 0x010203ff);
 }
 
 static struct file_system_type drm_fs_type = {
 	.name		= "drm",
 	.owner		= THIS_MODULE,
-	.mount		= drm_fs_mount,
+	.init_fs_context = drm_fs_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 15/68] vfs: Convert ia64 perfmon to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (13 preceding siblings ...)
  2019-03-27 23:42 ` [RFC PATCH 14/68] vfs: Convert drm " David Howells
@ 2019-03-27 23:42 ` David Howells
  2019-03-27 23:42 ` [RFC PATCH 16/68] vfs: Convert cxl " David Howells
                   ` (52 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:42 UTC (permalink / raw)
  To: viro
  Cc: Tony Luck, Fenghua Yu, linux-ia64, linux-fsdevel, linux-kernel, dhowells

Convert the ia64 perfmon filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Tony Luck <tony.luck@intel.com>
cc: Fenghua Yu <fenghua.yu@intel.com>
cc: linux-ia64@vger.kernel.org
---

 arch/ia64/kernel/perfmon.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
index 7a969f4c3534..4b5770736ac5 100644
--- a/arch/ia64/kernel/perfmon.c
+++ b/arch/ia64/kernel/perfmon.c
@@ -38,6 +38,7 @@
 #include <linux/smp.h>
 #include <linux/pagemap.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/bitops.h>
 #include <linux/capability.h>
 #include <linux/rcupdate.h>
@@ -599,17 +600,16 @@ pfm_unprotect_ctx_ctxsw(pfm_context_t *x, unsigned long f)
 /* forward declaration */
 static const struct dentry_operations pfmfs_dentry_operations;
 
-static struct dentry *
-pfmfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data)
+static int pfmfs_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type, "pfm:", NULL, &pfmfs_dentry_operations,
-			PFMFS_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "pfm:", NULL, NULL,
+					  &pfmfs_dentry_operations, PFMFS_MAGIC);
 }
 
 static struct file_system_type pfm_fs_type = {
-	.name     = "pfmfs",
-	.mount    = pfmfs_mount,
-	.kill_sb  = kill_anon_super,
+	.name			= "pfmfs",
+	.init_fs_context	= pfmfs_init_fs_context,
+	.kill_sb		= kill_anon_super,
 };
 MODULE_ALIAS_FS("pfmfs");
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 16/68] vfs: Convert cxl to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (14 preceding siblings ...)
  2019-03-27 23:42 ` [RFC PATCH 15/68] vfs: Convert ia64 perfmon " David Howells
@ 2019-03-27 23:42 ` David Howells
  2019-03-27 23:42 ` [RFC PATCH 17/68] vfs: Convert ocxlflash " David Howells
                   ` (51 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:42 UTC (permalink / raw)
  To: viro
  Cc: Andrew Donnellan, Frederic Barrat, linuxppc-dev, linux-fsdevel,
	linux-kernel, dhowells

Convert the cxl filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>
Acked-by: Frederic Barrat <fbarrat@linux.ibm.com>
cc: linuxppc-dev@lists.ozlabs.org
---

 drivers/misc/cxl/api.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c
index 750470ef2049..395e9a88e6ba 100644
--- a/drivers/misc/cxl/api.c
+++ b/drivers/misc/cxl/api.c
@@ -13,6 +13,7 @@
 #include <misc/cxl.h>
 #include <linux/module.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/sched/mm.h>
 #include <linux/mmu_context.h>
 
@@ -41,17 +42,16 @@ static const struct dentry_operations cxl_fs_dops = {
 	.d_dname	= simple_dname,
 };
 
-static struct dentry *cxl_fs_mount(struct file_system_type *fs_type, int flags,
-				const char *dev_name, void *data)
+static int cxl_fs_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type, "cxl:", NULL, &cxl_fs_dops,
-			CXL_PSEUDO_FS_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "cxl:", NULL, NULL,
+					  &cxl_fs_dops, CXL_PSEUDO_FS_MAGIC);
 }
 
 static struct file_system_type cxl_fs_type = {
 	.name		= "cxl",
 	.owner		= THIS_MODULE,
-	.mount		= cxl_fs_mount,
+	.init_fs_context = cxl_fs_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 17/68] vfs: Convert ocxlflash to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (15 preceding siblings ...)
  2019-03-27 23:42 ` [RFC PATCH 16/68] vfs: Convert cxl " David Howells
@ 2019-03-27 23:42 ` David Howells
  2019-03-27 23:42 ` [RFC PATCH 18/68] vfs: Convert virtio_balloon " David Howells
                   ` (50 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:42 UTC (permalink / raw)
  To: viro
  Cc: Matthew R. Ochs, Manoj N. Kumar, Uma Krishnan,
	James E.J. Bottomley, Martin K. Petersen, linux-scsi,
	linux-fsdevel, linux-kernel, dhowells

Convert the ocxlflash filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>
cc: "Manoj N. Kumar" <manoj@linux.ibm.com>
cc: Uma Krishnan <ukrishn@linux.ibm.com>
cc: "James E.J. Bottomley" <jejb@linux.ibm.com>
cc: "Martin K. Petersen" <martin.petersen@oracle.com>
cc: linux-scsi@vger.kernel.org
---

 drivers/scsi/cxlflash/ocxl_hw.c |   21 ++++++---------------
 1 file changed, 6 insertions(+), 15 deletions(-)

diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c
index 37b8dc60f5f6..a8d2cc6390c7 100644
--- a/drivers/scsi/cxlflash/ocxl_hw.c
+++ b/drivers/scsi/cxlflash/ocxl_hw.c
@@ -16,6 +16,7 @@
 #include <linux/idr.h>
 #include <linux/module.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/poll.h>
 #include <linux/sched/signal.h>
 
@@ -39,27 +40,17 @@ static const struct dentry_operations ocxlflash_fs_dops = {
 	.d_dname	= simple_dname,
 };
 
-/*
- * ocxlflash_fs_mount() - mount the pseudo-filesystem
- * @fs_type:	File system type.
- * @flags:	Flags for the filesystem.
- * @dev_name:	Device name associated with the filesystem.
- * @data:	Data pointer.
- *
- * Return: pointer to the directory entry structure
- */
-static struct dentry *ocxlflash_fs_mount(struct file_system_type *fs_type,
-					 int flags, const char *dev_name,
-					 void *data)
+static int ocxlflash_fs_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type, "ocxlflash:", NULL, &ocxlflash_fs_dops,
-			    OCXLFLASH_FS_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "ocxlflash:",
+					  NULL, NULL,
+					  &ocxlflash_fs_dops, OCXLFLASH_FS_MAGIC);
 }
 
 static struct file_system_type ocxlflash_fs_type = {
 	.name		= "ocxlflash",
 	.owner		= THIS_MODULE,
-	.mount		= ocxlflash_fs_mount,
+	.init_fs_context = ocxlflash_fs_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 18/68] vfs: Convert virtio_balloon to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (16 preceding siblings ...)
  2019-03-27 23:42 ` [RFC PATCH 17/68] vfs: Convert ocxlflash " David Howells
@ 2019-03-27 23:42 ` David Howells
  2019-03-27 23:42 ` [RFC PATCH 19/68] vfs: Convert btrfs_test " David Howells
                   ` (49 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:42 UTC (permalink / raw)
  To: viro
  Cc: Michael S. Tsirkin, Jason Wang, virtualization, linux-fsdevel,
	linux-kernel, dhowells

Convert the virtio_balloon filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: "Michael S. Tsirkin" <mst@redhat.com>
cc: Jason Wang <jasowang@redhat.com>
cc: virtualization@lists.linux-foundation.org
---

 drivers/virtio/virtio_balloon.c |   19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index f19061b585a4..89d67c8aa719 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -757,21 +757,22 @@ static int virtballoon_migratepage(struct balloon_dev_info *vb_dev_info,
 
 	return MIGRATEPAGE_SUCCESS;
 }
+#include <linux/fs_context.h>
 
-static struct dentry *balloon_mount(struct file_system_type *fs_type,
-		int flags, const char *dev_name, void *data)
-{
-	static const struct dentry_operations ops = {
-		.d_dname = simple_dname,
-	};
+static const struct dentry_operations balloon_dops = {
+	.d_dname = simple_dname,
+};
 
-	return mount_pseudo(fs_type, "balloon-kvm:", NULL, &ops,
-				BALLOON_KVM_MAGIC);
+static int balloon_init_fs_context(struct fs_context *fc)
+{
+	return vfs_init_pseudo_fs_context(fc, "balloon-kvm:",
+					  NULL, NULL,
+					  &balloon_dops, BALLOON_KVM_MAGIC);
 }
 
 static struct file_system_type balloon_fs = {
 	.name           = "balloon-kvm",
-	.mount          = balloon_mount,
+	.init_fs_context = balloon_init_fs_context,
 	.kill_sb        = kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 19/68] vfs: Convert btrfs_test to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (17 preceding siblings ...)
  2019-03-27 23:42 ` [RFC PATCH 18/68] vfs: Convert virtio_balloon " David Howells
@ 2019-03-27 23:42 ` David Howells
  2019-03-27 23:43 ` [RFC PATCH 20/68] vfs: Kill off mount_pseudo() and mount_pseudo_xattr() David Howells
                   ` (48 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:42 UTC (permalink / raw)
  To: viro
  Cc: David Sterba, Chris Mason, Josef Bacik, linux-btrfs,
	linux-fsdevel, linux-kernel, dhowells

Convert the btrfs_test filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: David Sterba <dsterba@suse.com>
cc: Chris Mason <clm@fb.com>
cc: Josef Bacik <josef@toxicpanda.com>
cc: linux-btrfs@vger.kernel.org
---

 fs/btrfs/tests/btrfs-tests.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c
index 8a59597f1883..dabd43a37414 100644
--- a/fs/btrfs/tests/btrfs-tests.c
+++ b/fs/btrfs/tests/btrfs-tests.c
@@ -5,6 +5,7 @@
 
 #include <linux/fs.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/magic.h>
 #include "btrfs-tests.h"
 #include "../ctree.h"
@@ -22,17 +23,17 @@ static const struct super_operations btrfs_test_super_ops = {
 	.destroy_inode	= btrfs_test_destroy_inode,
 };
 
-static struct dentry *btrfs_test_mount(struct file_system_type *fs_type,
-				       int flags, const char *dev_name,
-				       void *data)
+
+static int btrfs_test_init_fs_context(struct fs_context *fc)
 {
-	return mount_pseudo(fs_type, "btrfs_test:", &btrfs_test_super_ops,
-			    NULL, BTRFS_TEST_MAGIC);
+	return vfs_init_pseudo_fs_context(fc, "btrfs_test:",
+					  &btrfs_test_super_ops, NULL,
+					  NULL, BTRFS_TEST_MAGIC);
 }
 
 static struct file_system_type test_type = {
 	.name		= "btrfs_test_fs",
-	.mount		= btrfs_test_mount,
+	.init_fs_context = btrfs_test_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 20/68] vfs: Kill off mount_pseudo() and mount_pseudo_xattr()
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (18 preceding siblings ...)
  2019-03-27 23:42 ` [RFC PATCH 19/68] vfs: Convert btrfs_test " David Howells
@ 2019-03-27 23:43 ` David Howells
  2019-03-27 23:43 ` [RFC PATCH 21/68] vfs: Use sget_fc() for pseudo-filesystems David Howells
                   ` (47 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:43 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Kill off mount_pseudo() and mount_pseudo_xattr() as they're replaced with
vfs_init_pseudo_fs_context().

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/libfs.c         |   53 ----------------------------------------------------
 include/linux/fs.h |   13 -------------
 2 files changed, 66 deletions(-)

diff --git a/fs/libfs.c b/fs/libfs.c
index ceaf69c885c2..9a15590b65da 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -326,59 +326,6 @@ int vfs_init_pseudo_fs_context(struct fs_context *fc,
 }
 EXPORT_SYMBOL(vfs_init_pseudo_fs_context);
 
-/*
- * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that
- * will never be mountable)
- */
-struct dentry *mount_pseudo_xattr(struct file_system_type *fs_type, char *name,
-	const struct super_operations *ops, const struct xattr_handler **xattr,
-	const struct dentry_operations *dops, unsigned long magic)
-{
-	struct super_block *s;
-	struct dentry *dentry;
-	struct inode *root;
-	struct qstr d_name = QSTR_INIT(name, strlen(name));
-
-	s = sget_userns(fs_type, NULL, set_anon_super, SB_KERNMOUNT|SB_NOUSER,
-			&init_user_ns, NULL);
-	if (IS_ERR(s))
-		return ERR_CAST(s);
-
-	s->s_maxbytes = MAX_LFS_FILESIZE;
-	s->s_blocksize = PAGE_SIZE;
-	s->s_blocksize_bits = PAGE_SHIFT;
-	s->s_magic = magic;
-	s->s_op = ops ? ops : &simple_super_operations;
-	s->s_xattr = xattr;
-	s->s_time_gran = 1;
-	root = new_inode(s);
-	if (!root)
-		goto Enomem;
-	/*
-	 * since this is the first inode, make it number 1. New inodes created
-	 * after this must take care not to collide with it (by passing
-	 * max_reserved of 1 to iunique).
-	 */
-	root->i_ino = 1;
-	root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
-	root->i_atime = root->i_mtime = root->i_ctime = current_time(root);
-	dentry = __d_alloc(s, &d_name);
-	if (!dentry) {
-		iput(root);
-		goto Enomem;
-	}
-	d_instantiate(dentry, root);
-	s->s_root = dentry;
-	s->s_d_op = dops;
-	s->s_flags |= SB_ACTIVE;
-	return dget(s->s_root);
-
-Enomem:
-	deactivate_locked_super(s);
-	return ERR_PTR(-ENOMEM);
-}
-EXPORT_SYMBOL(mount_pseudo_xattr);
-
 int simple_open(struct inode *inode, struct file *file)
 {
 	if (inode->i_private)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 8b42df09b04c..b738e8e5b096 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2245,19 +2245,6 @@ struct super_block *sget(struct file_system_type *type,
 			int (*test)(struct super_block *,void *),
 			int (*set)(struct super_block *,void *),
 			int flags, void *data);
-extern struct dentry *mount_pseudo_xattr(struct file_system_type *, char *,
-					 const struct super_operations *ops,
-					 const struct xattr_handler **xattr,
-					 const struct dentry_operations *dops,
-					 unsigned long);
-
-static inline struct dentry *
-mount_pseudo(struct file_system_type *fs_type, char *name,
-	     const struct super_operations *ops,
-	     const struct dentry_operations *dops, unsigned long magic)
-{
-	return mount_pseudo_xattr(fs_type, name, ops, NULL, dops, magic);
-}
 
 /* Alas, no aliases. Too much hassle with bringing module.h everywhere */
 #define fops_get(fops) \


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 21/68] vfs: Use sget_fc() for pseudo-filesystems
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (19 preceding siblings ...)
  2019-03-27 23:43 ` [RFC PATCH 20/68] vfs: Kill off mount_pseudo() and mount_pseudo_xattr() David Howells
@ 2019-03-27 23:43 ` David Howells
  2019-03-27 23:43 ` [RFC PATCH 22/68] vfs: Convert binderfs to use the new mount API David Howells
                   ` (46 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:43 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/libfs.c |   25 +++++++++++--------------
 1 file changed, 11 insertions(+), 14 deletions(-)

diff --git a/fs/libfs.c b/fs/libfs.c
index 9a15590b65da..f6710316de00 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -242,18 +242,12 @@ struct pseudo_fs_context {
 	unsigned long magic;
 };
 
-static int pseudo_fs_get_tree(struct fs_context *fc)
+static int pseudo_fs_fill_super(struct super_block *s, struct fs_context *fc)
 {
 	struct pseudo_fs_context *ctx = fc->fs_private;
-	struct super_block *s;
 	struct dentry *dentry;
 	struct inode *root;
 
-	s = sget_userns(fc->fs_type, NULL, set_anon_super, SB_KERNMOUNT|SB_NOUSER,
-			&init_user_ns, NULL);
-	if (IS_ERR(s))
-		return PTR_ERR(s);
-
 	s->s_maxbytes = MAX_LFS_FILESIZE;
 	s->s_blocksize = PAGE_SIZE;
 	s->s_blocksize_bits = PAGE_SHIFT;
@@ -263,7 +257,8 @@ static int pseudo_fs_get_tree(struct fs_context *fc)
 	s->s_time_gran = 1;
 	root = new_inode(s);
 	if (!root)
-		goto Enomem;
+		return -ENOMEM;
+
 	/*
 	 * since this is the first inode, make it number 1. New inodes created
 	 * after this must take care not to collide with it (by passing
@@ -275,18 +270,17 @@ static int pseudo_fs_get_tree(struct fs_context *fc)
 	dentry = __d_alloc(s, &ctx->d_name);
 	if (!dentry) {
 		iput(root);
-		goto Enomem;
+		return -ENOMEM;
 	}
 	d_instantiate(dentry, root);
 	s->s_root = dentry;
 	s->s_d_op = ctx->dops;
-	s->s_flags |= SB_ACTIVE;
-	fc->root = dget(s->s_root);
 	return 0;
+}
 
-Enomem:
-	deactivate_locked_super(s);
-	return -ENOMEM;
+static int pseudo_fs_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_independent_super, pseudo_fs_fill_super);
 }
 
 static void pseudo_fs_free(struct fs_context *fc)
@@ -320,6 +314,9 @@ int vfs_init_pseudo_fs_context(struct fs_context *fc,
 	ctx->xattr = xattr;
 	ctx->dops = dops;
 	ctx->magic = magic;
+
+	fc->sb_flags = SB_KERNMOUNT | SB_NOUSER;
+	fc->global = true;
 	fc->fs_private = ctx;
 	fc->ops = &pseudo_fs_context_ops;
 	return 0;


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 22/68] vfs: Convert binderfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (20 preceding siblings ...)
  2019-03-27 23:43 ` [RFC PATCH 21/68] vfs: Use sget_fc() for pseudo-filesystems David Howells
@ 2019-03-27 23:43 ` David Howells
  2019-03-28 14:45   ` Dan Carpenter
  2019-04-10 10:01   ` David Howells
  2019-03-27 23:43 ` [RFC PATCH 23/68] vfs: Convert nfsctl " David Howells
                   ` (45 subsequent siblings)
  67 siblings, 2 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:43 UTC (permalink / raw)
  To: viro
  Cc: Christian Brauner, Greg Kroah-Hartman, Arve Hjønnevåg,
	Todd Kjos, Martijn Coenen, Joel Fernandes, devel, linux-fsdevel,
	linux-kernel, dhowells

Convert the binderfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Christian Brauner <christian@brauner.io>
cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
cc: "Arve Hjønnevåg" <arve@android.com>
cc: Todd Kjos <tkjos@android.com>
cc: Martijn Coenen <maco@android.com>
cc: Joel Fernandes <joel@joelfernandes.org>
cc: devel@driverdev.osuosl.org
---

 drivers/android/binderfs.c |  173 ++++++++++++++++++++++++--------------------
 1 file changed, 96 insertions(+), 77 deletions(-)

diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
index e773f45d19d9..0a16ecc9594f 100644
--- a/drivers/android/binderfs.c
+++ b/drivers/android/binderfs.c
@@ -18,7 +18,8 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/mount.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/radix-tree.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
@@ -48,22 +49,18 @@ static dev_t binderfs_dev;
 static DEFINE_MUTEX(binderfs_minors_mutex);
 static DEFINE_IDA(binderfs_minors);
 
-/**
- * binderfs_mount_opts - mount options for binderfs
- * @max: maximum number of allocatable binderfs binder devices
- */
-struct binderfs_mount_opts {
-	int max;
-};
-
 enum {
 	Opt_max,
-	Opt_err
 };
 
-static const match_table_t tokens = {
-	{ Opt_max, "max=%d" },
-	{ Opt_err, NULL     }
+static const struct fs_parameter_spec binderfs_param_specs[] = {
+	fsparam_s32   ("max",	Opt_max),
+	{}
+};
+
+static const struct fs_parameter_description binderfs_fs_parameters = {
+	.name		= "binderfs",
+	.specs		= binderfs_param_specs,
 };
 
 /**
@@ -75,7 +72,7 @@ static const match_table_t tokens = {
  *                  created.
  * @root_gid:       gid that needs to be used when a new binder device is
  *                  created.
- * @mount_opts:     The mount options in use.
+ * @max:	    Maximum number of allocatable binderfs binder devices.
  * @device_count:   The current number of allocated binder devices.
  */
 struct binderfs_info {
@@ -83,7 +80,7 @@ struct binderfs_info {
 	struct dentry *control_dentry;
 	kuid_t root_uid;
 	kgid_t root_gid;
-	struct binderfs_mount_opts mount_opts;
+	int max;
 	int device_count;
 };
 
@@ -138,7 +135,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
 
 	/* Reserve new minor number for the new device. */
 	mutex_lock(&binderfs_minors_mutex);
-	if (++info->device_count <= info->mount_opts.max)
+	if (++info->device_count <= info->max)
 		minor = ida_alloc_max(&binderfs_minors,
 				      use_reserve ? BINDERFS_MAX_MINOR :
 						    BINDERFS_MAX_MINOR_CAPPED,
@@ -285,46 +282,36 @@ static void binderfs_evict_inode(struct inode *inode)
 }
 
 /**
- * binderfs_parse_mount_opts - parse binderfs mount options
- * @data: options to set (can be NULL in which case defaults are used)
+ * binderfs_parse_param - parse a binderfs mount option
+ * @fc: The context to be configured
+ * @param: The parameter to apply
  */
-static int binderfs_parse_mount_opts(char *data,
-				     struct binderfs_mount_opts *opts)
+static int binderfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	char *p;
-	opts->max = BINDERFS_MAX_MINOR;
-
-	while ((p = strsep(&data, ",")) != NULL) {
-		substring_t args[MAX_OPT_ARGS];
-		int token;
-		int max_devices;
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_max:
-			if (match_int(&args[0], &max_devices) ||
-			    (max_devices < 0 ||
-			     (max_devices > BINDERFS_MAX_MINOR)))
-				return -EINVAL;
-
-			opts->max = max_devices;
-			break;
-		default:
-			pr_err("Invalid mount options\n");
-			return -EINVAL;
-		}
+	struct fs_parse_result result;
+	struct binderfs_info *info = fc->s_fs_info;
+	int opt;
+
+	opt = fs_parse(fc, &binderfs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_max:
+		info->max = result.int_32;
+		break;
 	}
 
 	return 0;
 }
 
-static int binderfs_remount(struct super_block *sb, int *flags, char *data)
+static int binderfs_reconfigure(struct fs_context *fc)
 {
-	struct binderfs_info *info = sb->s_fs_info;
-	return binderfs_parse_mount_opts(data, &info->mount_opts);
+	struct binderfs_info *info = fc->root->d_sb->s_fs_info;
+	struct binderfs_info *cfg = fc->s_fs_info;
+
+	info->max = cfg->max;
+	return 0;
 }
 
 static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root)
@@ -332,15 +319,14 @@ static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root)
 	struct binderfs_info *info;
 
 	info = root->d_sb->s_fs_info;
-	if (info->mount_opts.max <= BINDERFS_MAX_MINOR)
-		seq_printf(seq, ",max=%d", info->mount_opts.max);
+	if (info->max <= BINDERFS_MAX_MINOR)
+		seq_printf(seq, ",max=%d", info->max);
 
 	return 0;
 }
 
 static const struct super_operations binderfs_super_ops = {
 	.evict_inode    = binderfs_evict_inode,
-	.remount_fs	= binderfs_remount,
 	.show_options	= binderfs_show_mount_opts,
 	.statfs         = simple_statfs,
 };
@@ -462,10 +448,8 @@ static const struct inode_operations binderfs_dir_inode_operations = {
 	.unlink = binderfs_unlink,
 };
 
-static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
+static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
-	int ret;
-	struct binderfs_info *info;
 	struct inode *inode = NULL;
 
 	sb->s_blocksize = PAGE_SIZE;
@@ -488,24 +472,6 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
 	sb->s_op = &binderfs_super_ops;
 	sb->s_time_gran = 1;
 
-	sb->s_fs_info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL);
-	if (!sb->s_fs_info)
-		return -ENOMEM;
-	info = sb->s_fs_info;
-
-	info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns);
-
-	ret = binderfs_parse_mount_opts(data, &info->mount_opts);
-	if (ret)
-		return ret;
-
-	info->root_gid = make_kgid(sb->s_user_ns, 0);
-	if (!gid_valid(info->root_gid))
-		info->root_gid = GLOBAL_ROOT_GID;
-	info->root_uid = make_kuid(sb->s_user_ns, 0);
-	if (!uid_valid(info->root_uid))
-		info->root_uid = GLOBAL_ROOT_UID;
-
 	inode = new_inode(sb);
 	if (!inode)
 		return -ENOMEM;
@@ -524,11 +490,63 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
 	return binderfs_binder_ctl_create(sb);
 }
 
-static struct dentry *binderfs_mount(struct file_system_type *fs_type,
-				     int flags, const char *dev_name,
-				     void *data)
+static int binderfs_get_tree(struct fs_context *fc)
+{
+	if (!ns_capable(fc->user_ns, CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return vfs_get_super(fc, vfs_get_independent_super, binderfs_fill_super);
+}
+
+static void binderfs_free_fc(struct fs_context *fc)
+{
+	struct binderfs_info *info = fc->s_fs_info;
+
+	if (info) {
+		struct ipc_namespace *ipc_ns = fc->s_fs_info;
+		put_ipc_ns(ipc_ns);
+		kfree(info);
+	}
+}
+
+static const struct fs_context_operations binderfs_context_ops = {
+	.free		= binderfs_free_fc,
+	.parse_param	= binderfs_parse_param,
+	.get_tree	= binderfs_get_tree,
+	.reconfigure	= binderfs_reconfigure,
+};
+
+static int binderfs_init_fs_context(struct fs_context *fc)
 {
-	return mount_nodev(fs_type, flags, data, binderfs_fill_super);
+	struct binderfs_info *info;
+	struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
+	struct user_namespace *user_ns = ipc_ns->user_ns;
+
+	info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	switch (fc->purpose) {
+	case FS_CONTEXT_FOR_MOUNT:
+		put_user_ns(fc->user_ns);
+		fc->user_ns = get_user_ns(user_ns);
+		info->ipc_ns = get_ipc_ns(ipc_ns);
+
+		info->root_gid = make_kgid(user_ns, 0);
+		if (!gid_valid(info->root_gid))
+			info->root_gid = GLOBAL_ROOT_GID;
+		info->root_uid = make_kuid(user_ns, 0);
+		if (!uid_valid(info->root_uid))
+			info->root_uid = GLOBAL_ROOT_UID;
+		break;
+
+	default:
+		break;
+	}
+
+	fc->s_fs_info = info;
+	fc->ops = &binderfs_context_ops;
+	return 0;
 }
 
 static void binderfs_kill_super(struct super_block *sb)
@@ -545,7 +563,8 @@ static void binderfs_kill_super(struct super_block *sb)
 
 static struct file_system_type binder_fs_type = {
 	.name		= "binder",
-	.mount		= binderfs_mount,
+	.init_fs_context = binderfs_init_fs_context,
+	.parameters	= &binderfs_fs_parameters,
 	.kill_sb	= binderfs_kill_super,
 	.fs_flags	= FS_USERNS_MOUNT,
 };


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 23/68] vfs: Convert nfsctl to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (21 preceding siblings ...)
  2019-03-27 23:43 ` [RFC PATCH 22/68] vfs: Convert binderfs to use the new mount API David Howells
@ 2019-03-27 23:43 ` David Howells
  2019-03-27 23:43 ` [RFC PATCH 24/68] vfs: Convert rpc_pipefs " David Howells
                   ` (44 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:43 UTC (permalink / raw)
  To: viro
  Cc: J. Bruce Fields, Jeff Layton, linux-nfs, linux-fsdevel,
	linux-kernel, dhowells

Convert the nfsctl filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: "J. Bruce Fields" <bfields@fieldses.org>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-nfs@vger.kernel.org
---

 fs/nfsd/nfsctl.c |   33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index f2feb2d11bae..d4eb4f34a94c 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -7,6 +7,7 @@
 #include <linux/slab.h>
 #include <linux/namei.h>
 #include <linux/ctype.h>
+#include <linux/fs_context.h>
 
 #include <linux/sunrpc/svcsock.h>
 #include <linux/lockd/lockd.h>
@@ -1146,7 +1147,7 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
  *	populating the filesystem.
  */
 
-static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
+static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static const struct tree_descr nfsd_files[] = {
 		[NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO},
@@ -1176,15 +1177,33 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
 #endif
 		/* last one */ {""}
 	};
-	get_net(sb->s_fs_info);
+
 	return simple_fill_super(sb, 0x6e667364, nfsd_files);
 }
 
-static struct dentry *nfsd_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int nfsd_fs_get_tree(struct fs_context *fc)
+{
+	fc->s_fs_info = get_net(fc->net_ns);
+	return vfs_get_super(fc, vfs_get_keyed_super, nfsd_fill_super);
+}
+
+static void nfsd_fs_free_fc(struct fs_context *fc)
+{
+	if (fc->s_fs_info)
+		put_net(fc->s_fs_info);
+}
+
+static const struct fs_context_operations nfsd_fs_context_ops = {
+	.free		= nfsd_fs_free_fc,
+	.get_tree	= nfsd_fs_get_tree,
+};
+
+static int nfsd_init_fs_context(struct fs_context *fc)
 {
-	struct net *net = current->nsproxy->net_ns;
-	return mount_ns(fs_type, flags, data, net, net->user_ns, nfsd_fill_super);
+	put_user_ns(fc->user_ns);
+	fc->user_ns = get_user_ns(fc->net_ns->user_ns);
+	fc->ops = &nfsd_fs_context_ops;
+	return 0;
 }
 
 static void nfsd_umount(struct super_block *sb)
@@ -1198,7 +1217,7 @@ static void nfsd_umount(struct super_block *sb)
 static struct file_system_type nfsd_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "nfsd",
-	.mount		= nfsd_mount,
+	.init_fs_context = nfsd_init_fs_context,
 	.kill_sb	= nfsd_umount,
 };
 MODULE_ALIAS_FS("nfsd");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 24/68] vfs: Convert rpc_pipefs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (22 preceding siblings ...)
  2019-03-27 23:43 ` [RFC PATCH 23/68] vfs: Convert nfsctl " David Howells
@ 2019-03-27 23:43 ` David Howells
  2019-03-27 23:43 ` [RFC PATCH 25/68] vfs: Kill mount_ns() David Howells
                   ` (43 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:43 UTC (permalink / raw)
  To: viro
  Cc: Trond Myklebust, Anna Schumaker, J. Bruce Fields, Jeff Layton,
	linux-nfs, linux-fsdevel, linux-kernel, dhowells

Convert the rpc_pipefs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Trond Myklebust <trond.myklebust@hammerspace.com>
cc: Anna Schumaker <anna.schumaker@netapp.com>
cc: "J. Bruce Fields" <bfields@fieldses.org>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-nfs@vger.kernel.org
---

 net/sunrpc/rpc_pipe.c |   34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 69663681bf9d..2d697861d49a 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -13,6 +13,7 @@
 #include <linux/string.h>
 #include <linux/pagemap.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/namei.h>
 #include <linux/fsnotify.h>
 #include <linux/kernel.h>
@@ -1354,11 +1355,11 @@ rpc_gssd_dummy_depopulate(struct dentry *pipe_dentry)
 }
 
 static int
-rpc_fill_super(struct super_block *sb, void *data, int silent)
+rpc_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct inode *inode;
 	struct dentry *root, *gssd_dentry;
-	struct net *net = get_net(sb->s_fs_info);
+	struct net *net = sb->s_fs_info;
 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
 	int err;
 
@@ -1415,12 +1416,29 @@ gssd_running(struct net *net)
 }
 EXPORT_SYMBOL_GPL(gssd_running);
 
-static struct dentry *
-rpc_mount(struct file_system_type *fs_type,
-		int flags, const char *dev_name, void *data)
+static int rpc_fs_get_tree(struct fs_context *fc)
+{
+	fc->s_fs_info = get_net(fc->net_ns);
+	return vfs_get_super(fc, vfs_get_keyed_super, rpc_fill_super);
+}
+
+static void rpc_fs_free_fc(struct fs_context *fc)
 {
-	struct net *net = current->nsproxy->net_ns;
-	return mount_ns(fs_type, flags, data, net, net->user_ns, rpc_fill_super);
+	if (fc->s_fs_info)
+		put_net(fc->s_fs_info);
+}
+
+static const struct fs_context_operations rpc_fs_context_ops = {
+	.free		= rpc_fs_free_fc,
+	.get_tree	= rpc_fs_get_tree,
+};
+
+static int rpc_init_fs_context(struct fs_context *fc)
+{
+	put_user_ns(fc->user_ns);
+	fc->user_ns = get_user_ns(fc->net_ns->user_ns);
+	fc->ops = &rpc_fs_context_ops;
+	return 0;
 }
 
 static void rpc_kill_sb(struct super_block *sb)
@@ -1448,7 +1466,7 @@ static void rpc_kill_sb(struct super_block *sb)
 static struct file_system_type rpc_pipe_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "rpc_pipefs",
-	.mount		= rpc_mount,
+	.init_fs_context = rpc_init_fs_context,
 	.kill_sb	= rpc_kill_sb,
 };
 MODULE_ALIAS_FS("rpc_pipefs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 25/68] vfs: Kill mount_ns()
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (23 preceding siblings ...)
  2019-03-27 23:43 ` [RFC PATCH 24/68] vfs: Convert rpc_pipefs " David Howells
@ 2019-03-27 23:43 ` David Howells
  2019-03-27 23:43 ` [RFC PATCH 26/68] vfs: Kill sget_userns() David Howells
                   ` (42 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:43 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Kill mount_ns() as it has been replaced by vfs_get_super() in the new mount
API.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/super.c         |   44 --------------------------------------------
 include/linux/fs.h |    3 ---
 2 files changed, 47 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index 6c3acd633cca..3b5275298f7c 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1148,50 +1148,6 @@ void kill_litter_super(struct super_block *sb)
 }
 EXPORT_SYMBOL(kill_litter_super);
 
-static int ns_test_super(struct super_block *sb, void *data)
-{
-	return sb->s_fs_info == data;
-}
-
-static int ns_set_super(struct super_block *sb, void *data)
-{
-	sb->s_fs_info = data;
-	return set_anon_super(sb, NULL);
-}
-
-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))
-{
-	struct super_block *sb;
-
-	/* Don't allow mounting unless the caller has CAP_SYS_ADMIN
-	 * over the namespace.
-	 */
-	if (!(flags & SB_KERNMOUNT) && !ns_capable(user_ns, CAP_SYS_ADMIN))
-		return ERR_PTR(-EPERM);
-
-	sb = sget_userns(fs_type, ns_test_super, ns_set_super, flags,
-			 user_ns, ns);
-	if (IS_ERR(sb))
-		return ERR_CAST(sb);
-
-	if (!sb->s_root) {
-		int err;
-		err = fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
-		if (err) {
-			deactivate_locked_super(sb);
-			return ERR_PTR(err);
-		}
-
-		sb->s_flags |= SB_ACTIVE;
-	}
-
-	return dget(sb->s_root);
-}
-
-EXPORT_SYMBOL(mount_ns);
-
 int set_anon_super_fc(struct super_block *sb, struct fs_context *fc)
 {
 	return set_anon_super(sb, NULL);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b738e8e5b096..20c15794c2ac 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2194,9 +2194,6 @@ struct file_system_type {
 
 #define MODULE_ALIAS_FS(NAME) MODULE_ALIAS("fs-" NAME)
 
-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));
 #ifdef CONFIG_BLOCK
 extern struct dentry *mount_bdev(struct file_system_type *fs_type,
 	int flags, const char *dev_name, void *data,


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 26/68] vfs: Kill sget_userns()
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (24 preceding siblings ...)
  2019-03-27 23:43 ` [RFC PATCH 25/68] vfs: Kill mount_ns() David Howells
@ 2019-03-27 23:43 ` David Howells
  2019-03-27 23:43 ` [RFC PATCH 27/68] vfs: Convert binfmt_misc to use the new mount API David Howells
                   ` (41 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:43 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Kill sget_userns(), folding it into sget() as that's the only remaining
user.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-fsdevel@vger.kernel.org
---

 fs/super.c         |   61 +++++++++++++++++-----------------------------------
 include/linux/fs.h |    5 ----
 2 files changed, 20 insertions(+), 46 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index 3b5275298f7c..f27ee08fb26f 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -566,24 +566,35 @@ struct super_block *sget_fc(struct fs_context *fc,
 EXPORT_SYMBOL(sget_fc);
 
 /**
- *	sget_userns -	find or create a superblock
- *	@type:	filesystem type superblock should belong to
- *	@test:	comparison callback
- *	@set:	setup callback
- *	@flags:	mount flags
- *	@user_ns: User namespace for the super_block
- *	@data:	argument to each of them
+ *	sget	-	find or create a superblock
+ *	@type:	  filesystem type superblock should belong to
+ *	@test:	  comparison callback
+ *	@set:	  setup callback
+ *	@flags:	  mount flags
+ *	@data:	  argument to each of them
  */
-struct super_block *sget_userns(struct file_system_type *type,
+struct super_block *sget(struct file_system_type *type,
 			int (*test)(struct super_block *,void *),
 			int (*set)(struct super_block *,void *),
-			int flags, struct user_namespace *user_ns,
+			int flags,
 			void *data)
 {
+	struct user_namespace *user_ns = current_user_ns();
 	struct super_block *s = NULL;
 	struct super_block *old;
 	int err;
 
+	/* We don't yet pass the user namespace of the parent
+	 * mount through to here so always use &init_user_ns
+	 * until that changes.
+	 */
+	if (flags & SB_SUBMOUNT)
+		user_ns = &init_user_ns;
+
+	/* Ensure the requestor has permissions over the target filesystem */
+	if (!(flags & (SB_KERNMOUNT|SB_SUBMOUNT)) && !ns_capable(user_ns, CAP_SYS_ADMIN))
+		return ERR_PTR(-EPERM);
+
 	if (!(flags & (SB_KERNMOUNT|SB_SUBMOUNT)) &&
 	    !(type->fs_flags & FS_USERNS_MOUNT) &&
 	    !capable(CAP_SYS_ADMIN))
@@ -629,38 +640,6 @@ struct super_block *sget_userns(struct file_system_type *type,
 	return s;
 }
 
-EXPORT_SYMBOL(sget_userns);
-
-/**
- *	sget	-	find or create a superblock
- *	@type:	  filesystem type superblock should belong to
- *	@test:	  comparison callback
- *	@set:	  setup callback
- *	@flags:	  mount flags
- *	@data:	  argument to each of them
- */
-struct super_block *sget(struct file_system_type *type,
-			int (*test)(struct super_block *,void *),
-			int (*set)(struct super_block *,void *),
-			int flags,
-			void *data)
-{
-	struct user_namespace *user_ns = current_user_ns();
-
-	/* We don't yet pass the user namespace of the parent
-	 * mount through to here so always use &init_user_ns
-	 * until that changes.
-	 */
-	if (flags & SB_SUBMOUNT)
-		user_ns = &init_user_ns;
-
-	/* Ensure the requestor has permissions over the target filesystem */
-	if (!(flags & (SB_KERNMOUNT|SB_SUBMOUNT)) && !ns_capable(user_ns, CAP_SYS_ADMIN))
-		return ERR_PTR(-EPERM);
-
-	return sget_userns(type, test, set, flags, user_ns, data);
-}
-
 EXPORT_SYMBOL(sget);
 
 void drop_super(struct super_block *sb)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 20c15794c2ac..646f4ccaeee9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2233,11 +2233,6 @@ void free_anon_bdev(dev_t);
 struct super_block *sget_fc(struct fs_context *fc,
 			    int (*test)(struct super_block *, struct fs_context *),
 			    int (*set)(struct super_block *, struct fs_context *));
-struct super_block *sget_userns(struct file_system_type *type,
-			int (*test)(struct super_block *,void *),
-			int (*set)(struct super_block *,void *),
-			int flags, struct user_namespace *user_ns,
-			void *data);
 struct super_block *sget(struct file_system_type *type,
 			int (*test)(struct super_block *,void *),
 			int (*set)(struct super_block *,void *),


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 27/68] vfs: Convert binfmt_misc to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (25 preceding siblings ...)
  2019-03-27 23:43 ` [RFC PATCH 26/68] vfs: Kill sget_userns() David Howells
@ 2019-03-27 23:43 ` David Howells
  2019-03-27 23:44 ` [RFC PATCH 28/68] vfs: Convert configfs " David Howells
                   ` (40 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:43 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Convert the binfmt_misc filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Alexander Viro <viro@zeniv.linux.org.uk>
cc: linux-fsdevel@vger.kernel.org
---

 fs/binfmt_misc.c |   20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index aa4a7a23ff99..0c32e59bfd20 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -22,6 +22,7 @@
 #include <linux/pagemap.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/syscalls.h>
 #include <linux/fs.h>
 #include <linux/uaccess.h>
@@ -820,7 +821,7 @@ static const struct super_operations s_ops = {
 	.evict_inode	= bm_evict_inode,
 };
 
-static int bm_fill_super(struct super_block *sb, void *data, int silent)
+static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	int err;
 	static const struct tree_descr bm_files[] = {
@@ -835,10 +836,19 @@ static int bm_fill_super(struct super_block *sb, void *data, int silent)
 	return err;
 }
 
-static struct dentry *bm_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int bm_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, bm_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, bm_fill_super);
+}
+
+static const struct fs_context_operations bm_context_ops = {
+	.get_tree	= bm_get_tree,
+};
+
+static int bm_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &bm_context_ops;
+	return 0;
 }
 
 static struct linux_binfmt misc_format = {
@@ -849,7 +859,7 @@ static struct linux_binfmt misc_format = {
 static struct file_system_type bm_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "binfmt_misc",
-	.mount		= bm_mount,
+	.init_fs_context = bm_init_fs_context,
 	.kill_sb	= kill_litter_super,
 };
 MODULE_ALIAS_FS("binfmt_misc");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 28/68] vfs: Convert configfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (26 preceding siblings ...)
  2019-03-27 23:43 ` [RFC PATCH 27/68] vfs: Convert binfmt_misc to use the new mount API David Howells
@ 2019-03-27 23:44 ` David Howells
  2019-03-27 23:44 ` [RFC PATCH 29/68] vfs: Convert efivarfs " David Howells
                   ` (39 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:44 UTC (permalink / raw)
  To: viro
  Cc: Joel Becker, Christoph Hellwig, linux-fsdevel, linux-kernel, dhowells

Convert the configfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Joel Becker <jlbec@evilplan.org>
cc: Christoph Hellwig <hch@lst.de>
---

 fs/configfs/mount.c |   20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index cfd91320e869..af4db09f46b4 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -27,6 +27,7 @@
 #include <linux/fs.h>
 #include <linux/module.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
 #include <linux/pagemap.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -66,7 +67,7 @@ static struct configfs_dirent configfs_root = {
 	.s_iattr	= NULL,
 };
 
-static int configfs_fill_super(struct super_block *sb, void *data, int silent)
+static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct inode *inode;
 	struct dentry *root;
@@ -102,16 +103,25 @@ static int configfs_fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
-static struct dentry *configfs_do_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int configfs_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, configfs_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, configfs_fill_super);
+}
+
+static const struct fs_context_operations configfs_context_ops = {
+	.get_tree	= configfs_get_tree,
+};
+
+static int configfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &configfs_context_ops;
+	return 0;
 }
 
 static struct file_system_type configfs_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "configfs",
-	.mount		= configfs_do_mount,
+	.init_fs_context = configfs_init_fs_context,
 	.kill_sb	= kill_litter_super,
 };
 MODULE_ALIAS_FS("configfs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 29/68] vfs: Convert efivarfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (27 preceding siblings ...)
  2019-03-27 23:44 ` [RFC PATCH 28/68] vfs: Convert configfs " David Howells
@ 2019-03-27 23:44 ` David Howells
  2019-03-27 23:44 ` [RFC PATCH 30/68] vfs: Convert fusectl " David Howells
                   ` (38 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:44 UTC (permalink / raw)
  To: viro
  Cc: Matthew Garrett, Jeremy Kerr, Ard Biesheuvel, linux-efi,
	linux-fsdevel, linux-kernel, dhowells

Convert the efivarfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Matthew Garrett <matthew.garrett@nebula.com>
cc: Jeremy Kerr <jk@ozlabs.org>
cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
cc: linux-efi@vger.kernel.org
---

 fs/efivarfs/super.c |   20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index 5b68e4294faa..1ea25c7a329a 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -10,6 +10,7 @@
 #include <linux/ctype.h>
 #include <linux/efi.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/module.h>
 #include <linux/pagemap.h>
 #include <linux/ucs2_string.h>
@@ -191,7 +192,7 @@ static int efivarfs_destroy(struct efivar_entry *entry, void *data)
 	return 0;
 }
 
-static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
+static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct inode *inode = NULL;
 	struct dentry *root;
@@ -226,10 +227,19 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 	return err;
 }
 
-static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
-				    int flags, const char *dev_name, void *data)
+static int efivarfs_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, efivarfs_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, efivarfs_fill_super);
+}
+
+static const struct fs_context_operations efivarfs_context_ops = {
+	.get_tree	= efivarfs_get_tree,
+};
+
+static int efivarfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &efivarfs_context_ops;
+	return 0;
 }
 
 static void efivarfs_kill_sb(struct super_block *sb)
@@ -244,7 +254,7 @@ static void efivarfs_kill_sb(struct super_block *sb)
 static struct file_system_type efivarfs_type = {
 	.owner   = THIS_MODULE,
 	.name    = "efivarfs",
-	.mount   = efivarfs_mount,
+	.init_fs_context = efivarfs_init_fs_context,
 	.kill_sb = efivarfs_kill_sb,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 30/68] vfs: Convert fusectl to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (28 preceding siblings ...)
  2019-03-27 23:44 ` [RFC PATCH 29/68] vfs: Convert efivarfs " David Howells
@ 2019-03-27 23:44 ` David Howells
  2019-04-24 13:49   ` Miklos Szeredi
  2019-04-24 15:16   ` David Howells
  2019-03-27 23:44 ` [RFC PATCH 31/68] vfs: Convert qib_fs/ipathfs " David Howells
                   ` (37 subsequent siblings)
  67 siblings, 2 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:44 UTC (permalink / raw)
  To: viro; +Cc: Miklos Szeredi, linux-fsdevel, linux-fsdevel, linux-kernel, dhowells

Convert the fusectl filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Miklos Szeredi <miklos@szeredi.hu>
cc: linux-fsdevel@vger.kernel.org
---

 fs/fuse/control.c |   20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/fs/fuse/control.c b/fs/fuse/control.c
index fe80bea4ad89..14ce1e47f980 100644
--- a/fs/fuse/control.c
+++ b/fs/fuse/control.c
@@ -10,6 +10,7 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/fs_context.h>
 
 #define FUSE_CTL_SUPER_MAGIC 0x65735543
 
@@ -317,7 +318,7 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc)
 	drop_nlink(d_inode(fuse_control_sb->s_root));
 }
 
-static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent)
+static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fctx)
 {
 	static const struct tree_descr empty_descr = {""};
 	struct fuse_conn *fc;
@@ -343,10 +344,19 @@ static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
-static struct dentry *fuse_ctl_mount(struct file_system_type *fs_type,
-			int flags, const char *dev_name, void *raw_data)
+static int fuse_ctl_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, raw_data, fuse_ctl_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, fuse_ctl_fill_super);
+}
+
+static const struct fs_context_operations fuse_ctl_context_ops = {
+	.get_tree	= fuse_ctl_get_tree,
+};
+
+static int fuse_ctl_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &fuse_ctl_context_ops;
+	return 0;
 }
 
 static void fuse_ctl_kill_sb(struct super_block *sb)
@@ -365,7 +375,7 @@ static void fuse_ctl_kill_sb(struct super_block *sb)
 static struct file_system_type fuse_ctl_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "fusectl",
-	.mount		= fuse_ctl_mount,
+	.init_fs_context = fuse_ctl_init_fs_context,
 	.kill_sb	= fuse_ctl_kill_sb,
 };
 MODULE_ALIAS_FS("fusectl");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 31/68] vfs: Convert qib_fs/ipathfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (29 preceding siblings ...)
  2019-03-27 23:44 ` [RFC PATCH 30/68] vfs: Convert fusectl " David Howells
@ 2019-03-27 23:44 ` David Howells
  2019-04-09 17:14   ` Dennis Dalessandro
  2019-03-27 23:44 ` [RFC PATCH 32/68] vfs: Convert ibmasmfs " David Howells
                   ` (36 subsequent siblings)
  67 siblings, 1 reply; 87+ messages in thread
From: David Howells @ 2019-03-27 23:44 UTC (permalink / raw)
  To: viro
  Cc: Dennis Dalessandro, Mike Marciniszyn, linux-rdma, linux-fsdevel,
	linux-kernel, dhowells

Convert the qib_fs/ipathfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

[Q] Can qib_remove() race with qibfs_kill_super()?  Should qib_super
    accesses be serialised with some sort of lock?

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Dennis Dalessandro <dennis.dalessandro@intel.com>
cc: Mike Marciniszyn <mike.marciniszyn@intel.com>
cc: linux-rdma@vger.kernel.org
---

 drivers/infiniband/hw/qib/qib_fs.c |   26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/drivers/infiniband/hw/qib/qib_fs.c b/drivers/infiniband/hw/qib/qib_fs.c
index 1d940a2885c9..fc4f62a248a1 100644
--- a/drivers/infiniband/hw/qib/qib_fs.c
+++ b/drivers/infiniband/hw/qib/qib_fs.c
@@ -34,6 +34,7 @@
 
 #include <linux/module.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/mount.h>
 #include <linux/pagemap.h>
 #include <linux/init.h>
@@ -506,7 +507,7 @@ static int remove_device_files(struct super_block *sb,
  * after device init.  The direct add_cntr_files() call handles adding
  * them from the init code, when the fs is already mounted.
  */
-static int qibfs_fill_super(struct super_block *sb, void *data, int silent)
+static int qibfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct qib_devdata *dd, *tmp;
 	unsigned long flags;
@@ -540,17 +541,26 @@ static int qibfs_fill_super(struct super_block *sb, void *data, int silent)
 	return ret;
 }
 
-static struct dentry *qibfs_mount(struct file_system_type *fs_type, int flags,
-			const char *dev_name, void *data)
+static int qibfs_get_tree(struct fs_context *fc)
 {
-	struct dentry *ret;
+	int ret;
 
-	ret = mount_single(fs_type, flags, data, qibfs_fill_super);
-	if (!IS_ERR(ret))
-		qib_super = ret->d_sb;
+	ret = vfs_get_super(fc, vfs_get_single_super, qibfs_fill_super);
+	if (ret == 0)
+		qib_super = fc->root->d_sb;
 	return ret;
 }
 
+static const struct fs_context_operations qibfs_context_ops = {
+	.get_tree	= qibfs_get_tree,
+};
+
+static int qibfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &qibfs_context_ops;
+	return 0;
+}
+
 static void qibfs_kill_super(struct super_block *s)
 {
 	kill_litter_super(s);
@@ -589,7 +599,7 @@ int qibfs_remove(struct qib_devdata *dd)
 static struct file_system_type qibfs_fs_type = {
 	.owner =        THIS_MODULE,
 	.name =         "ipathfs",
-	.mount =        qibfs_mount,
+	.init_fs_context = qibfs_init_fs_context,
 	.kill_sb =      qibfs_kill_super,
 };
 MODULE_ALIAS_FS("ipathfs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 32/68] vfs: Convert ibmasmfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (30 preceding siblings ...)
  2019-03-27 23:44 ` [RFC PATCH 31/68] vfs: Convert qib_fs/ipathfs " David Howells
@ 2019-03-27 23:44 ` David Howells
  2019-03-27 23:44 ` [RFC PATCH 33/68] vfs: Convert oprofilefs " David Howells
                   ` (35 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:44 UTC (permalink / raw)
  To: viro
  Cc: Arnd Bergmann, Greg Kroah-Hartman, linux-fsdevel, linux-kernel, dhowells

Convert the ibmasmfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Arnd Bergmann <arnd@arndb.de>
cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---

 drivers/misc/ibmasm/ibmasmfs.c |   21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c
index fa840666bdd1..e615f335db99 100644
--- a/drivers/misc/ibmasm/ibmasmfs.c
+++ b/drivers/misc/ibmasm/ibmasmfs.c
@@ -74,6 +74,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
@@ -88,13 +89,21 @@ static LIST_HEAD(service_processors);
 
 static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
 static void ibmasmfs_create_files (struct super_block *sb);
-static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent);
+static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc);
 
+static int ibmasmfs_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_single_super, ibmasmfs_fill_super);
+}
 
-static struct dentry *ibmasmfs_mount(struct file_system_type *fst,
-			int flags, const char *name, void *data)
+static const struct fs_context_operations ibmasmfs_context_ops = {
+	.get_tree	= ibmasmfs_get_tree,
+};
+
+static int ibmasmfs_init_fs_context(struct fs_context *fc)
 {
-	return mount_single(fst, flags, data, ibmasmfs_fill_super);
+	fc->ops = &ibmasmfs_context_ops;
+	return 0;
 }
 
 static const struct super_operations ibmasmfs_s_ops = {
@@ -107,12 +116,12 @@ static const struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
 static struct file_system_type ibmasmfs_type = {
 	.owner          = THIS_MODULE,
 	.name           = "ibmasmfs",
-	.mount          = ibmasmfs_mount,
+	.init_fs_context = ibmasmfs_init_fs_context,
 	.kill_sb        = kill_litter_super,
 };
 MODULE_ALIAS_FS("ibmasmfs");
 
-static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent)
+static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct inode *root;
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 33/68] vfs: Convert oprofilefs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (31 preceding siblings ...)
  2019-03-27 23:44 ` [RFC PATCH 32/68] vfs: Convert ibmasmfs " David Howells
@ 2019-03-27 23:44 ` David Howells
  2019-03-27 23:44 ` [RFC PATCH 34/68] vfs: Convert gadgetfs " David Howells
                   ` (34 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:44 UTC (permalink / raw)
  To: viro; +Cc: Robert Richter, oprofile-list, linux-fsdevel, linux-kernel, dhowells

Convert the oprofilefs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Robert Richter <rric@kernel.org>
cc: oprofile-list@lists.sf.net
---

 drivers/oprofile/oprofilefs.c |   20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c
index 4ea08979312c..d83fcd39ee55 100644
--- a/drivers/oprofile/oprofilefs.c
+++ b/drivers/oprofile/oprofilefs.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/oprofile.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/pagemap.h>
 #include <linux/uaccess.h>
 
@@ -238,7 +239,7 @@ struct dentry *oprofilefs_mkdir(struct dentry *parent, char const *name)
 }
 
 
-static int oprofilefs_fill_super(struct super_block *sb, void *data, int silent)
+static int oprofilefs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct inode *root_inode;
 
@@ -263,18 +264,25 @@ static int oprofilefs_fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
-
-static struct dentry *oprofilefs_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int oprofilefs_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, oprofilefs_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, oprofilefs_fill_super);
 }
 
+static const struct fs_context_operations oprofilefs_context_ops = {
+	.get_tree	= oprofilefs_get_tree,
+};
+
+static int oprofilefs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &oprofilefs_context_ops;
+	return 0;
+}
 
 static struct file_system_type oprofilefs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "oprofilefs",
-	.mount		= oprofilefs_mount,
+	.init_fs_context = oprofilefs_init_fs_context,
 	.kill_sb	= kill_litter_super,
 };
 MODULE_ALIAS_FS("oprofilefs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 34/68] vfs: Convert gadgetfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (32 preceding siblings ...)
  2019-03-27 23:44 ` [RFC PATCH 33/68] vfs: Convert oprofilefs " David Howells
@ 2019-03-27 23:44 ` David Howells
  2019-03-27 23:44 ` [RFC PATCH 35/68] vfs: Convert xenfs " David Howells
                   ` (33 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:44 UTC (permalink / raw)
  To: viro; +Cc: Felipe Balbi, linux-usb, linux-fsdevel, linux-kernel, dhowells

Convert the gadgetfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Felipe Balbi <felipe.balbi@linux.intel.com>
cc: linux-usb@vger.kernel.org
---

 drivers/usb/gadget/legacy/inode.c |   21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 249277d0e53f..441da16ffc39 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/pagemap.h>
 #include <linux/uts.h>
 #include <linux/wait.h>
@@ -1990,7 +1991,7 @@ static const struct super_operations gadget_fs_operations = {
 };
 
 static int
-gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
+gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
 {
 	struct inode	*inode;
 	struct dev_data	*dev;
@@ -2044,11 +2045,19 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
 }
 
 /* "mount -t gadgetfs path /dev/gadget" ends up here */
-static struct dentry *
-gadgetfs_mount (struct file_system_type *t, int flags,
-		const char *path, void *opts)
+static int gadgetfs_get_tree(struct fs_context *fc)
 {
-	return mount_single (t, flags, opts, gadgetfs_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, gadgetfs_fill_super);
+}
+
+static const struct fs_context_operations gadgetfs_context_ops = {
+	.get_tree	= gadgetfs_get_tree,
+};
+
+static int gadgetfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &gadgetfs_context_ops;
+	return 0;
 }
 
 static void
@@ -2068,7 +2077,7 @@ gadgetfs_kill_sb (struct super_block *sb)
 static struct file_system_type gadgetfs_type = {
 	.owner		= THIS_MODULE,
 	.name		= shortname,
-	.mount		= gadgetfs_mount,
+	.init_fs_context = gadgetfs_init_fs_context,
 	.kill_sb	= gadgetfs_kill_sb,
 };
 MODULE_ALIAS_FS("gadgetfs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 35/68] vfs: Convert xenfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (33 preceding siblings ...)
  2019-03-27 23:44 ` [RFC PATCH 34/68] vfs: Convert gadgetfs " David Howells
@ 2019-03-27 23:44 ` David Howells
  2019-04-02  7:38   ` Juergen Gross
  2019-03-27 23:45 ` [RFC PATCH 36/68] vfs: Convert openpromfs " David Howells
                   ` (32 subsequent siblings)
  67 siblings, 1 reply; 87+ messages in thread
From: David Howells @ 2019-03-27 23:44 UTC (permalink / raw)
  To: viro
  Cc: Boris Ostrovsky, Juergen Gross, Stefano Stabellini, xen-devel,
	linux-fsdevel, linux-kernel, dhowells

Convert the xenfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
cc: Juergen Gross <jgross@suse.com>
cc: Stefano Stabellini <sstabellini@kernel.org>
cc: xen-devel@lists.xenproject.org
---

 drivers/xen/xenfs/super.c |   21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c
index 71ddfb4cf61c..2e16214e9c7f 100644
--- a/drivers/xen/xenfs/super.c
+++ b/drivers/xen/xenfs/super.c
@@ -13,6 +13,7 @@
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/magic.h>
 
 #include <xen/xen.h>
@@ -42,7 +43,7 @@ static const struct file_operations capabilities_file_ops = {
 	.llseek = default_llseek,
 };
 
-static int xenfs_fill_super(struct super_block *sb, void *data, int silent)
+static int xenfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static const struct tree_descr xenfs_files[] = {
 		[2] = { "xenbus", &xen_xenbus_fops, S_IRUSR|S_IWUSR },
@@ -67,17 +68,25 @@ static int xenfs_fill_super(struct super_block *sb, void *data, int silent)
 			xen_initial_domain() ? xenfs_init_files : xenfs_files);
 }
 
-static struct dentry *xenfs_mount(struct file_system_type *fs_type,
-				  int flags, const char *dev_name,
-				  void *data)
+static int xenfs_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, xenfs_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, xenfs_fill_super);
+}
+
+static const struct fs_context_operations xenfs_context_ops = {
+	.get_tree	= xenfs_get_tree,
+};
+
+static int xenfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &xenfs_context_ops;
+	return 0;
 }
 
 static struct file_system_type xenfs_type = {
 	.owner =	THIS_MODULE,
 	.name =		"xenfs",
-	.mount =	xenfs_mount,
+	.init_fs_context = xenfs_init_fs_context,
 	.kill_sb =	kill_litter_super,
 };
 MODULE_ALIAS_FS("xenfs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 36/68] vfs: Convert openpromfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (34 preceding siblings ...)
  2019-03-27 23:44 ` [RFC PATCH 35/68] vfs: Convert xenfs " David Howells
@ 2019-03-27 23:45 ` David Howells
  2019-03-27 23:45 ` [RFC PATCH 37/68] vfs: Convert apparmorfs " David Howells
                   ` (31 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:45 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Convert the openpromfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

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

 fs/openpromfs/inode.c |   20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c
index 1b2d0d2fe2ee..7ec384df0109 100644
--- a/fs/openpromfs/inode.c
+++ b/fs/openpromfs/inode.c
@@ -8,6 +8,7 @@
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/seq_file.h>
@@ -380,7 +381,7 @@ static const struct super_operations openprom_sops = {
 	.remount_fs	= openprom_remount,
 };
 
-static int openprom_fill_super(struct super_block *s, void *data, int silent)
+static int openprom_fill_super(struct super_block *s, struct fs_context *fc)
 {
 	struct inode *root_inode;
 	struct op_inode_info *oi;
@@ -414,16 +415,25 @@ static int openprom_fill_super(struct super_block *s, void *data, int silent)
 	return ret;
 }
 
-static struct dentry *openprom_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int openpromfs_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, openprom_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, openprom_fill_super);
+}
+
+static const struct fs_context_operations openpromfs_context_ops = {
+	.get_tree	= openpromfs_get_tree,
+};
+
+static int openpromfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &openpromfs_context_ops;
+	return 0;
 }
 
 static struct file_system_type openprom_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "openpromfs",
-	.mount		= openprom_mount,
+	.init_fs_context = openpromfs_init_fs_context,
 	.kill_sb	= kill_anon_super,
 };
 MODULE_ALIAS_FS("openpromfs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 37/68] vfs: Convert apparmorfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (35 preceding siblings ...)
  2019-03-27 23:45 ` [RFC PATCH 36/68] vfs: Convert openpromfs " David Howells
@ 2019-03-27 23:45 ` David Howells
  2019-03-27 23:45 ` [RFC PATCH 38/68] vfs: Convert securityfs " David Howells
                   ` (30 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:45 UTC (permalink / raw)
  To: viro
  Cc: John Johansen, apparmor, linux-security-module, linux-fsdevel,
	linux-kernel, dhowells

Convert the apparmorfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: John Johansen <john.johansen@canonical.com>
cc: apparmor@lists.ubuntu.com
cc: linux-security-module@vger.kernel.org
---

 security/apparmor/apparmorfs.c |   20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index fefee040bf79..75f7da0e5ec1 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -23,6 +23,7 @@
 #include <linux/capability.h>
 #include <linux/rcupdate.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/poll.h>
 #include <uapi/linux/major.h>
 #include <uapi/linux/magic.h>
@@ -137,7 +138,7 @@ static const struct super_operations aafs_super_ops = {
 	.show_path = aafs_show_path,
 };
 
-static int fill_super(struct super_block *sb, void *data, int silent)
+static int apparmorfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static struct tree_descr files[] = { {""} };
 	int error;
@@ -150,16 +151,25 @@ static int fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
-static struct dentry *aafs_mount(struct file_system_type *fs_type,
-				 int flags, const char *dev_name, void *data)
+static int apparmorfs_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, apparmorfs_fill_super);
+}
+
+static const struct fs_context_operations apparmorfs_context_ops = {
+	.get_tree	= apparmorfs_get_tree,
+};
+
+static int apparmorfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &apparmorfs_context_ops;
+	return 0;
 }
 
 static struct file_system_type aafs_ops = {
 	.owner = THIS_MODULE,
 	.name = AAFS_NAME,
-	.mount = aafs_mount,
+	.init_fs_context = apparmorfs_init_fs_context,
 	.kill_sb = kill_anon_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 38/68] vfs: Convert securityfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (36 preceding siblings ...)
  2019-03-27 23:45 ` [RFC PATCH 37/68] vfs: Convert apparmorfs " David Howells
@ 2019-03-27 23:45 ` David Howells
  2019-03-27 23:45 ` [RFC PATCH 39/68] vfs: Convert selinuxfs " David Howells
                   ` (29 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:45 UTC (permalink / raw)
  To: viro; +Cc: linux-security-module, linux-fsdevel, linux-kernel, dhowells

Convert the securityfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-security-module@vger.kernel.org
---

 security/inode.c |   21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index b7772a9b315e..4fecbc8b072a 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -16,6 +16,7 @@
 #include <linux/sysfs.h>
 #include <linux/kobject.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/mount.h>
 #include <linux/pagemap.h>
 #include <linux/init.h>
@@ -40,7 +41,7 @@ static const struct super_operations securityfs_super_operations = {
 	.evict_inode	= securityfs_evict_inode,
 };
 
-static int fill_super(struct super_block *sb, void *data, int silent)
+static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static const struct tree_descr files[] = {{""}};
 	int error;
@@ -54,17 +55,25 @@ static int fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
-static struct dentry *get_sb(struct file_system_type *fs_type,
-		  int flags, const char *dev_name,
-		  void *data)
+static int securityfs_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, securityfs_fill_super);
+}
+
+static const struct fs_context_operations securityfs_context_ops = {
+	.get_tree	= securityfs_get_tree,
+};
+
+static int securityfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &securityfs_context_ops;
+	return 0;
 }
 
 static struct file_system_type fs_type = {
 	.owner =	THIS_MODULE,
 	.name =		"securityfs",
-	.mount =	get_sb,
+	.init_fs_context = securityfs_init_fs_context,
 	.kill_sb =	kill_litter_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 39/68] vfs: Convert selinuxfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (37 preceding siblings ...)
  2019-03-27 23:45 ` [RFC PATCH 38/68] vfs: Convert securityfs " David Howells
@ 2019-03-27 23:45 ` David Howells
  2019-03-27 23:45 ` [RFC PATCH 40/68] vfs: Convert smackfs " David Howells
                   ` (28 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:45 UTC (permalink / raw)
  To: viro
  Cc: Paul Moore, Stephen Smalley, Eric Paris, selinux,
	linux-security-module, linux-fsdevel, linux-kernel, dhowells

Convert the selinuxfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Paul Moore <paul@paul-moore.com>
cc: Stephen Smalley <sds@tycho.nsa.gov>
cc: Eric Paris <eparis@parisplace.org>
cc: selinux@vger.kernel.org
cc: linux-security-module@vger.kernel.org
---

 security/selinux/selinuxfs.c |   20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 145ee62f205a..ccfe86dfa784 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -19,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/mount.h>
 #include <linux/mutex.h>
 #include <linux/init.h>
@@ -1893,7 +1894,7 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
 
 #define NULL_FILE_NAME "null"
 
-static int sel_fill_super(struct super_block *sb, void *data, int silent)
+static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct selinux_fs_info *fsi;
 	int ret;
@@ -2009,10 +2010,19 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
 	return ret;
 }
 
-static struct dentry *sel_mount(struct file_system_type *fs_type,
-		      int flags, const char *dev_name, void *data)
+static int sel_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, sel_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, sel_fill_super);
+}
+
+static const struct fs_context_operations sel_context_ops = {
+	.get_tree	= sel_get_tree,
+};
+
+static int sel_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &sel_context_ops;
+	return 0;
 }
 
 static void sel_kill_sb(struct super_block *sb)
@@ -2023,7 +2033,7 @@ static void sel_kill_sb(struct super_block *sb)
 
 static struct file_system_type sel_fs_type = {
 	.name		= "selinuxfs",
-	.mount		= sel_mount,
+	.init_fs_context = sel_init_fs_context,
 	.kill_sb	= sel_kill_sb,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 40/68] vfs: Convert smackfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (38 preceding siblings ...)
  2019-03-27 23:45 ` [RFC PATCH 39/68] vfs: Convert selinuxfs " David Howells
@ 2019-03-27 23:45 ` David Howells
  2019-03-27 23:45 ` [RFC PATCH 41/68] vfs: Convert ramfs, shmem, tmpfs, devtmpfs, rootfs " David Howells
                   ` (27 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:45 UTC (permalink / raw)
  To: viro
  Cc: Casey Schaufler, linux-security-module, linux-fsdevel,
	linux-kernel, dhowells

Convert the smackfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Casey Schaufler <casey@schaufler-ca.com>
cc: linux-security-module@vger.kernel.org
---

 security/smack/smackfs.c |   34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index faf2ea3968b3..7e3a81d05136 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -27,6 +27,7 @@
 #include <linux/ctype.h>
 #include <linux/audit.h>
 #include <linux/magic.h>
+#include <linux/fs_context.h>
 #include "smack.h"
 
 #define BEBITS	(sizeof(__be32) * 8)
@@ -2843,14 +2844,13 @@ static const struct file_operations smk_ptrace_ops = {
 /**
  * smk_fill_super - fill the smackfs superblock
  * @sb: the empty superblock
- * @data: unused
- * @silent: unused
+ * @fc: unused
  *
  * Fill in the well known entries for the smack filesystem
  *
  * Returns 0 on success, an error code on failure
  */
-static int smk_fill_super(struct super_block *sb, void *data, int silent)
+static int smk_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	int rc;
 
@@ -2920,25 +2920,35 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
 }
 
 /**
- * smk_mount - get the smackfs superblock
- * @fs_type: passed along without comment
- * @flags: passed along without comment
- * @dev_name: passed along without comment
- * @data: passed along without comment
+ * smk_get_tree - get the smackfs superblock
+ * @fc: The mount context, including any options
  *
  * Just passes everything along.
  *
  * Returns what the lower level code does.
  */
-static struct dentry *smk_mount(struct file_system_type *fs_type,
-		      int flags, const char *dev_name, void *data)
+static int smk_get_tree(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, smk_fill_super);
+	return vfs_get_super(fc, vfs_get_single_super, smk_fill_super);
+}
+
+static const struct fs_context_operations smk_context_ops = {
+	.get_tree	= smk_get_tree,
+};
+
+/**
+ * smk_init_fs_context - Initialise a filesystem context for smackfs
+ * @fc: The blank mount context
+ */
+static int smk_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &smk_context_ops;
+	return 0;
 }
 
 static struct file_system_type smk_fs_type = {
 	.name		= "smackfs",
-	.mount		= smk_mount,
+	.init_fs_context = smk_init_fs_context,
 	.kill_sb	= kill_litter_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 41/68] vfs: Convert ramfs, shmem, tmpfs, devtmpfs, rootfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (39 preceding siblings ...)
  2019-03-27 23:45 ` [RFC PATCH 40/68] vfs: Convert smackfs " David Howells
@ 2019-03-27 23:45 ` David Howells
  2019-03-27 23:45 ` [RFC PATCH 42/68] vfs: Create fs_context-aware mount_bdev() replacement David Howells
                   ` (26 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:45 UTC (permalink / raw)
  To: viro; +Cc: Hugh Dickins, linux-mm, linux-fsdevel, linux-kernel, dhowells

Convert the ramfs, shmem, tmpfs, devtmpfs and rootfs filesystems to the new
internal mount API as the old one will be obsoleted and removed.  This
allows greater flexibility in communication of mount parameters between
userspace, the VFS and the filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Note that tmpfs is slightly tricky as it can contain embedded commas, so it
can't be trivially split up using strsep() to break on commas in
generic_parse_monolithic().  Instead, tmpfs has to supply its own generic
parser.

However, if tmpfs changes, then devtmpfs and rootfs, which are wrappers
around tmpfs or ramfs, must change too - and thus so must ramfs, so these
had to be converted also.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Hugh Dickins <hughd@google.com>
cc: linux-mm@kvack.org
---

 drivers/base/devtmpfs.c  |   16 +-
 fs/ramfs/inode.c         |  104 +++++++-----
 include/linux/ramfs.h    |    6 -
 include/linux/shmem_fs.h |    4 
 init/do_mounts.c         |   12 +
 mm/shmem.c               |  396 ++++++++++++++++++++++++++++++----------------
 6 files changed, 341 insertions(+), 197 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 0dbc43068eeb..1f50c844c2ab 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -56,19 +56,15 @@ static int __init mount_param(char *str)
 }
 __setup("devtmpfs.mount=", mount_param);
 
-static struct dentry *dev_mount(struct file_system_type *fs_type, int flags,
-		      const char *dev_name, void *data)
-{
+static struct file_system_type dev_fs_type = {
+	.name = "devtmpfs",
 #ifdef CONFIG_TMPFS
-	return mount_single(fs_type, flags, data, shmem_fill_super);
+	.init_fs_context = shmem_init_fs_context,
+	.parameters	= &shmem_fs_parameters,
 #else
-	return mount_single(fs_type, flags, data, ramfs_fill_super);
+	.init_fs_context = ramfs_init_fs_context,
+	.parameters	= &ramfs_fs_parameters,
 #endif
-}
-
-static struct file_system_type dev_fs_type = {
-	.name = "devtmpfs",
-	.mount = dev_mount,
 	.kill_sb = kill_litter_super,
 };
 
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
index 11201b2d06b9..01170dd6c95f 100644
--- a/fs/ramfs/inode.c
+++ b/fs/ramfs/inode.c
@@ -36,6 +36,8 @@
 #include <linux/magic.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include "internal.h"
 
 struct ramfs_mount_opts {
@@ -175,62 +177,52 @@ static const struct super_operations ramfs_ops = {
 	.show_options	= ramfs_show_options,
 };
 
-enum {
+enum ramfs_param {
 	Opt_mode,
-	Opt_err
 };
 
-static const match_table_t tokens = {
-	{Opt_mode, "mode=%o"},
-	{Opt_err, NULL}
+static const struct fs_parameter_spec ramfs_param_specs[] = {
+	fsparam_u32oct("mode",	Opt_mode),
+	{}
 };
 
-static int ramfs_parse_options(char *data, struct ramfs_mount_opts *opts)
+const struct fs_parameter_description ramfs_fs_parameters = {
+	.name		= "ramfs",
+	.specs		= ramfs_param_specs,
+};
+
+static int ramfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	substring_t args[MAX_OPT_ARGS];
-	int option;
-	int token;
-	char *p;
-
-	opts->mode = RAMFS_DEFAULT_MODE;
-
-	while ((p = strsep(&data, ",")) != NULL) {
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_mode:
-			if (match_octal(&args[0], &option))
-				return -EINVAL;
-			opts->mode = option & S_IALLUGO;
-			break;
+	struct fs_parse_result result;
+	struct ramfs_fs_info *fsi = fc->s_fs_info;
+	int opt;
+
+	opt = fs_parse(fc, &ramfs_fs_parameters, param, &result);
+	if (opt < 0) {
 		/*
 		 * We might like to report bad mount options here;
 		 * but traditionally ramfs has ignored all mount options,
 		 * and as it is used as a !CONFIG_SHMEM simple substitute
 		 * for tmpfs, better continue to ignore other mount options.
 		 */
-		}
+		if (opt == -ENOPARAM)
+			opt = 0;
+		return opt;
+	}
+
+	switch (opt) {
+	case Opt_mode:
+		fsi->mount_opts.mode = result.uint_32 & S_IALLUGO;
+		break;
 	}
 
 	return 0;
 }
 
-int ramfs_fill_super(struct super_block *sb, void *data, int silent)
+static int ramfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
-	struct ramfs_fs_info *fsi;
+	struct ramfs_fs_info *fsi = sb->s_fs_info;
 	struct inode *inode;
-	int err;
-
-	fsi = kzalloc(sizeof(struct ramfs_fs_info), GFP_KERNEL);
-	sb->s_fs_info = fsi;
-	if (!fsi)
-		return -ENOMEM;
-
-	err = ramfs_parse_options(data, &fsi->mount_opts);
-	if (err)
-		return err;
 
 	sb->s_maxbytes		= MAX_LFS_FILESIZE;
 	sb->s_blocksize		= PAGE_SIZE;
@@ -247,10 +239,39 @@ int ramfs_fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
-struct dentry *ramfs_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int ramfs_get_tree(struct fs_context *fc)
 {
-	return mount_nodev(fs_type, flags, data, ramfs_fill_super);
+	enum vfs_get_super_keying keying = vfs_get_independent_super;
+
+	if (strcmp(fc->fs_type->name, "devtmpfs") == 0)
+		keying = vfs_get_single_super;
+
+	return vfs_get_super(fc, keying, ramfs_fill_super);
+}
+
+static void ramfs_free_fc(struct fs_context *fc)
+{
+	kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations ramfs_context_ops = {
+	.free		= ramfs_free_fc,
+	.parse_param	= ramfs_parse_param,
+	.get_tree	= ramfs_get_tree,
+};
+
+int ramfs_init_fs_context(struct fs_context *fc)
+{
+	struct ramfs_fs_info *fsi;
+
+	fsi = kzalloc(sizeof(*fsi), GFP_KERNEL);
+	if (!fsi)
+		return -ENOMEM;
+
+	fsi->mount_opts.mode = RAMFS_DEFAULT_MODE;
+	fc->s_fs_info = fsi;
+	fc->ops = &ramfs_context_ops;
+	return 0;
 }
 
 static void ramfs_kill_sb(struct super_block *sb)
@@ -261,7 +282,8 @@ static void ramfs_kill_sb(struct super_block *sb)
 
 static struct file_system_type ramfs_fs_type = {
 	.name		= "ramfs",
-	.mount		= ramfs_mount,
+	.init_fs_context = ramfs_init_fs_context,
+	.parameters	= &ramfs_fs_parameters,
 	.kill_sb	= ramfs_kill_sb,
 	.fs_flags	= FS_USERNS_MOUNT,
 };
diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h
index 5ef7d54caac2..94b407424cb7 100644
--- a/include/linux/ramfs.h
+++ b/include/linux/ramfs.h
@@ -4,8 +4,7 @@
 
 struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir,
 	 umode_t mode, dev_t dev);
-extern struct dentry *ramfs_mount(struct file_system_type *fs_type,
-	 int flags, const char *dev_name, void *data);
+extern int ramfs_init_fs_context(struct fs_context *fc);
 
 #ifdef CONFIG_MMU
 static inline int
@@ -17,10 +16,9 @@ ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
 extern int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize);
 #endif
 
+extern const struct fs_parameter_description ramfs_fs_parameters;
 extern const struct file_operations ramfs_file_operations;
 extern const struct vm_operations_struct generic_file_vm_ops;
 extern int __init init_ramfs_fs(void);
 
-int ramfs_fill_super(struct super_block *sb, void *data, int silent);
-
 #endif
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index f3fb1edb3526..3f73c00081e8 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -48,8 +48,10 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
 /*
  * Functions in mm/shmem.c called directly from elsewhere:
  */
+extern const struct fs_parameter_description shmem_fs_parameters;
+
 extern int shmem_init(void);
-extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
+extern int shmem_init_fs_context(struct fs_context *fc);
 extern struct file *shmem_file_setup(const char *name,
 					loff_t size, unsigned long flags);
 extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
diff --git a/init/do_mounts.c b/init/do_mounts.c
index f8c230c77035..8242abd47e3f 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -626,24 +626,22 @@ void __init prepare_namespace(void)
 }
 
 static bool is_tmpfs;
-static struct dentry *rootfs_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int rootfs_init_fs_context(struct fs_context *fc)
 {
 	static unsigned long once;
-	void *fill = ramfs_fill_super;
 
 	if (test_and_set_bit(0, &once))
-		return ERR_PTR(-ENODEV);
+		return -ENODEV;
 
 	if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs)
-		fill = shmem_fill_super;
+		return shmem_init_fs_context(fc);
 
-	return mount_nodev(fs_type, flags, data, fill);
+	return ramfs_init_fs_context(fc);
 }
 
 static struct file_system_type rootfs_fs_type = {
 	.name		= "rootfs",
-	.mount		= rootfs_mount,
+	.init_fs_context = rootfs_init_fs_context,
 	.kill_sb	= kill_litter_super,
 };
 
diff --git a/mm/shmem.c b/mm/shmem.c
index b3db3779a30a..5f45a710ee04 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -37,6 +37,8 @@
 #include <linux/khugepaged.h>
 #include <linux/hugetlb.h>
 #include <linux/frontswap.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 
 #include <asm/tlbflush.h> /* for arch/microblaze update_mmu_cache() */
 
@@ -85,6 +87,17 @@ static struct vfsmount *shm_mnt;
 
 #include "internal.h"
 
+struct shmem_fs_context {
+	unsigned long	changes;
+	unsigned long	max_blocks;	/* How many blocks are allowed */
+	unsigned long	max_inodes;	/* How many inodes are allowed */
+	kuid_t		uid;
+	kgid_t		gid;
+	int		huge;
+	umode_t		mode;
+	struct mempolicy *mpol;		/* default memory policy for mappings */
+};
+
 #define BLOCKS_PER_PAGE  (PAGE_SIZE/512)
 #define VM_ACCT(size)    (PAGE_ALIGN(size) >> PAGE_SHIFT)
 
@@ -3351,16 +3364,13 @@ static const struct export_operations shmem_export_ops = {
 	.fh_to_dentry	= shmem_fh_to_dentry,
 };
 
-static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo,
-			       bool remount)
+static int shmem_parse_monolithic(struct fs_context *fc, void *data)
 {
-	char *this_char, *value, *rest;
-	struct mempolicy *mpol = NULL;
-	uid_t uid;
-	gid_t gid;
+	char *options = data, *key;
+	int ret = 0;
 
 	while (options != NULL) {
-		this_char = options;
+		key = options;
 		for (;;) {
 			/*
 			 * NUL-terminate this option: unfortunately,
@@ -3376,139 +3386,219 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo,
 				break;
 			}
 		}
-		if (!*this_char)
-			continue;
-		if ((value = strchr(this_char,'=')) != NULL) {
-			*value++ = 0;
-		} else {
-			pr_err("tmpfs: No value for mount option '%s'\n",
-			       this_char);
-			goto error;
-		}
 
-		if (!strcmp(this_char,"size")) {
-			unsigned long long size;
-			size = memparse(value,&rest);
-			if (*rest == '%') {
-				size <<= PAGE_SHIFT;
-				size *= totalram_pages();
-				do_div(size, 100);
-				rest++;
+		if (*key) {
+			size_t v_len = 0;
+			char *value = strchr(key, '=');
+
+			if (value) {
+				if (value == key)
+					continue;
+				*value++ = 0;
+				v_len = strlen(value);
 			}
-			if (*rest)
-				goto bad_val;
-			sbinfo->max_blocks =
-				DIV_ROUND_UP(size, PAGE_SIZE);
-		} else if (!strcmp(this_char,"nr_blocks")) {
-			sbinfo->max_blocks = memparse(value, &rest);
-			if (*rest)
-				goto bad_val;
-		} else if (!strcmp(this_char,"nr_inodes")) {
-			sbinfo->max_inodes = memparse(value, &rest);
-			if (*rest)
-				goto bad_val;
-		} else if (!strcmp(this_char,"mode")) {
-			if (remount)
-				continue;
-			sbinfo->mode = simple_strtoul(value, &rest, 8) & 07777;
-			if (*rest)
-				goto bad_val;
-		} else if (!strcmp(this_char,"uid")) {
-			if (remount)
-				continue;
-			uid = simple_strtoul(value, &rest, 0);
-			if (*rest)
-				goto bad_val;
-			sbinfo->uid = make_kuid(current_user_ns(), uid);
-			if (!uid_valid(sbinfo->uid))
-				goto bad_val;
-		} else if (!strcmp(this_char,"gid")) {
-			if (remount)
-				continue;
-			gid = simple_strtoul(value, &rest, 0);
-			if (*rest)
-				goto bad_val;
-			sbinfo->gid = make_kgid(current_user_ns(), gid);
-			if (!gid_valid(sbinfo->gid))
-				goto bad_val;
+			ret = vfs_parse_fs_string(fc, key, value, v_len);
+			if (ret < 0)
+				break;
+		}
+	}
+
+	return ret;
+}
+
+enum shmem_param {
+	Opt_gid,
+	Opt_huge,
+	Opt_mode,
+	Opt_mpol,
+	Opt_nr_blocks,
+	Opt_nr_inodes,
+	Opt_size,
+	Opt_uid,
+};
+
+static const struct fs_parameter_spec shmem_param_specs[] = {
+	fsparam_u32   ("gid",		Opt_gid),
+	fsparam_enum  ("huge",		Opt_huge),
+	fsparam_u32oct("mode",		Opt_mode),
+	fsparam_string("mpol",		Opt_mpol),
+	fsparam_string("nr_blocks",	Opt_nr_blocks),
+	fsparam_string("nr_inodes",	Opt_nr_inodes),
+	fsparam_string("size",		Opt_size),
+	fsparam_u32   ("uid",		Opt_uid),
+	{}
+};
+
+static const struct fs_parameter_enum shmem_param_enums[] = {
+	{ Opt_huge,	"never",	SHMEM_HUGE_NEVER },
+	{ Opt_huge,	"always",	SHMEM_HUGE_ALWAYS },
+	{ Opt_huge,	"within_size",	SHMEM_HUGE_WITHIN_SIZE },
+	{ Opt_huge,	"advise",	SHMEM_HUGE_ADVISE },
+	{ Opt_huge,	"deny",		SHMEM_HUGE_DENY },
+	{ Opt_huge,	"force",	SHMEM_HUGE_FORCE },
+	{}
+};
+
+const struct fs_parameter_description shmem_fs_parameters = {
+	.name		= "shmem",
+	.specs		= shmem_param_specs,
+	.enums		= shmem_param_enums,
+};
+
+static void shmem_apply_options(struct shmem_sb_info *sbinfo,
+				struct fs_context *fc,
+				unsigned long inodes_in_use)
+{
+	struct shmem_fs_context *ctx = fc->fs_private;
+	struct mempolicy *old = NULL;
+
+	if (test_bit(Opt_nr_blocks, &ctx->changes))
+		sbinfo->max_blocks = ctx->max_blocks;
+	if (test_bit(Opt_nr_inodes, &ctx->changes)) {
+		sbinfo->max_inodes = ctx->max_inodes;
+		sbinfo->free_inodes = ctx->max_inodes - inodes_in_use;
+	}
+	if (test_bit(Opt_huge, &ctx->changes))
+		sbinfo->huge = ctx->huge;
+	if (test_bit(Opt_mpol, &ctx->changes)) {
+		old = sbinfo->mpol;
+		sbinfo->mpol = ctx->mpol;
+	}
+
+	if (fc->purpose != FS_CONTEXT_FOR_RECONFIGURE) {
+		if (test_bit(Opt_uid, &ctx->changes))
+			sbinfo->uid = ctx->uid;
+		if (test_bit(Opt_gid, &ctx->changes))
+			sbinfo->gid = ctx->gid;
+		if (test_bit(Opt_mode, &ctx->changes))
+			sbinfo->mode = ctx->mode;
+	}
+
+	mpol_put(old);
+}
+
+static int shmem_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct shmem_fs_context *ctx = fc->fs_private;
+	struct fs_parse_result result;
+	unsigned long long size;
+	struct mempolicy *mpol;
+	char *rest;
+	int opt;
+
+	opt = fs_parse(fc, &shmem_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_size:
+		rest = param->string;
+		size = memparse(param->string, &rest);
+		if (*rest == '%') {
+			size <<= PAGE_SHIFT;
+			size *= totalram_pages();
+			do_div(size, 100);
+			rest++;
+		}
+		if (*rest)
+			return invalf(fc, "shmem: Invalid size");
+		ctx->max_blocks = DIV_ROUND_UP(size, PAGE_SIZE);
+		break;
+
+	case Opt_nr_blocks:
+		rest = param->string;
+		ctx->max_blocks = memparse(param->string, &rest);
+		if (*rest)
+			return invalf(fc, "shmem: Invalid nr_blocks");
+		break;
+	case Opt_nr_inodes:
+		rest = param->string;
+		ctx->max_inodes = memparse(param->string, &rest);
+		if (*rest)
+			return invalf(fc, "shmem: Invalid nr_inodes");
+		break;
+	case Opt_mode:
+		ctx->mode = result.uint_32 & 07777;
+		break;
+	case Opt_uid:
+		ctx->uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(ctx->uid))
+			return invalf(fc, "shmem: Invalid uid");
+		break;
+
+	case Opt_gid:
+		ctx->gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(ctx->gid))
+			return invalf(fc, "shmem: Invalid gid");
+		break;
+
+	case Opt_huge:
 #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
-		} else if (!strcmp(this_char, "huge")) {
-			int huge;
-			huge = shmem_parse_huge(value);
-			if (huge < 0)
-				goto bad_val;
-			if (!has_transparent_hugepage() &&
-					huge != SHMEM_HUGE_NEVER)
-				goto bad_val;
-			sbinfo->huge = huge;
+		if (!has_transparent_hugepage() &&
+		    result.uint_32 != SHMEM_HUGE_NEVER)
+			return invalf(fc, "shmem: Huge pages disabled");
+
+		ctx->huge = result.uint_32;
+		break;
+#else
+		return invalf(fc, "shmem: huge= option disabled");
 #endif
+
+	case Opt_mpol:
 #ifdef CONFIG_NUMA
-		} else if (!strcmp(this_char,"mpol")) {
-			mpol_put(mpol);
-			mpol = NULL;
-			if (mpol_parse_str(value, &mpol))
-				goto bad_val;
+		if (mpol_parse_str(param->string, &mpol))
+			return invalf(fc, "shmem: Invalid mpol=");
+		mpol_put(ctx->mpol);
+		ctx->mpol = mpol;
 #endif
-		} else {
-			pr_err("tmpfs: Bad mount option %s\n", this_char);
-			goto error;
-		}
+		break;
 	}
-	sbinfo->mpol = mpol;
-	return 0;
-
-bad_val:
-	pr_err("tmpfs: Bad value '%s' for mount option '%s'\n",
-	       value, this_char);
-error:
-	mpol_put(mpol);
-	return 1;
 
+	__set_bit(opt, &ctx->changes);
+	return 0;
 }
 
-static int shmem_remount_fs(struct super_block *sb, int *flags, char *data)
+/*
+ * Reconfigure a shmem filesystem.
+ *
+ * Note that we disallow change from limited->unlimited blocks/inodes while any
+ * are in use; but we must separately disallow unlimited->limited, because in
+ * that case we have no record of how much is already in use.
+ */
+static int shmem_reconfigure(struct fs_context *fc)
 {
+	struct shmem_fs_context *ctx = fc->fs_private;
+	struct super_block *sb = fc->root->d_sb;
 	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
-	struct shmem_sb_info config = *sbinfo;
-	unsigned long inodes;
-	int error = -EINVAL;
-
-	config.mpol = NULL;
-	if (shmem_parse_options(data, &config, true))
-		return error;
+	unsigned long inodes_in_use;
 
 	spin_lock(&sbinfo->stat_lock);
-	inodes = sbinfo->max_inodes - sbinfo->free_inodes;
-	if (percpu_counter_compare(&sbinfo->used_blocks, config.max_blocks) > 0)
-		goto out;
-	if (config.max_inodes < inodes)
-		goto out;
-	/*
-	 * Those tests disallow limited->unlimited while any are in use;
-	 * but we must separately disallow unlimited->limited, because
-	 * in that case we have no record of how much is already in use.
-	 */
-	if (config.max_blocks && !sbinfo->max_blocks)
-		goto out;
-	if (config.max_inodes && !sbinfo->max_inodes)
-		goto out;
-
-	error = 0;
-	sbinfo->huge = config.huge;
-	sbinfo->max_blocks  = config.max_blocks;
-	sbinfo->max_inodes  = config.max_inodes;
-	sbinfo->free_inodes = config.max_inodes - inodes;
+	if (test_bit(Opt_nr_blocks, &ctx->changes)) {
+		if (ctx->max_blocks && !sbinfo->max_blocks) {
+			spin_unlock(&sbinfo->stat_lock);
+			return invalf(fc, "shmem: Can't retroactively limit nr_blocks");
+		}
+		if (percpu_counter_compare(&sbinfo->used_blocks, ctx->max_blocks) > 0) {
+			spin_unlock(&sbinfo->stat_lock);
+			return invalf(fc, "shmem: Too few blocks for current use");
+		}
+	}
 
-	/*
-	 * Preserve previous mempolicy unless mpol remount option was specified.
-	 */
-	if (config.mpol) {
-		mpol_put(sbinfo->mpol);
-		sbinfo->mpol = config.mpol;	/* transfers initial ref */
+	inodes_in_use = sbinfo->max_inodes - sbinfo->free_inodes;
+	if (test_bit(Opt_nr_inodes, &ctx->changes)) {
+		if (ctx->max_inodes && !sbinfo->max_inodes) {
+			spin_unlock(&sbinfo->stat_lock);
+			return invalf(fc, "shmem: Can't retroactively limit nr_inodes");
+		}
+		if (ctx->max_inodes < inodes_in_use) {
+			spin_unlock(&sbinfo->stat_lock);
+			return invalf(fc, "shmem: Too few inodes for current use");
+		}
 	}
-out:
+
+	shmem_apply_options(sbinfo, fc, inodes_in_use);
 	spin_unlock(&sbinfo->stat_lock);
-	return error;
+	return 0;
 }
 
 static int shmem_show_options(struct seq_file *seq, struct dentry *root)
@@ -3549,7 +3639,7 @@ static void shmem_put_super(struct super_block *sb)
 	sb->s_fs_info = NULL;
 }
 
-int shmem_fill_super(struct super_block *sb, void *data, int silent)
+static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct inode *inode;
 	struct shmem_sb_info *sbinfo;
@@ -3575,10 +3665,7 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
 	if (!(sb->s_flags & SB_KERNMOUNT)) {
 		sbinfo->max_blocks = shmem_default_max_blocks();
 		sbinfo->max_inodes = shmem_default_max_inodes();
-		if (shmem_parse_options(data, sbinfo, false)) {
-			err = -EINVAL;
-			goto failed;
-		}
+		shmem_apply_options(sbinfo, fc, 0);
 	} else {
 		sb->s_flags |= SB_NOUSER;
 	}
@@ -3624,6 +3711,36 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
 	return err;
 }
 
+static int shmem_get_tree(struct fs_context *fc)
+{
+	enum vfs_get_super_keying keying = vfs_get_independent_super;
+
+	if (strcmp(fc->fs_type->name, "devtmpfs") == 0)
+		keying = vfs_get_single_super;
+
+	return vfs_get_super(fc, keying, shmem_fill_super);
+}
+
+static void shmem_free_fc(struct fs_context *fc)
+{
+	struct shmem_fs_context *ctx = fc->fs_private;
+
+	if (ctx) {
+		mpol_put(ctx->mpol);
+		kfree(ctx);
+	}
+}
+
+static const struct fs_context_operations shmem_fs_context_ops = {
+	.free			= shmem_free_fc,
+	.get_tree		= shmem_get_tree,
+#ifdef CONFIG_TMPFS
+	.parse_monolithic	= shmem_parse_monolithic,
+	.parse_param		= shmem_parse_param,
+	.reconfigure		= shmem_reconfigure,
+#endif
+};
+
 static struct kmem_cache *shmem_inode_cachep;
 
 static struct inode *shmem_alloc_inode(struct super_block *sb)
@@ -3741,7 +3858,6 @@ static const struct super_operations shmem_ops = {
 	.destroy_inode	= shmem_destroy_inode,
 #ifdef CONFIG_TMPFS
 	.statfs		= shmem_statfs,
-	.remount_fs	= shmem_remount_fs,
 	.show_options	= shmem_show_options,
 #endif
 	.evict_inode	= shmem_evict_inode,
@@ -3762,16 +3878,26 @@ static const struct vm_operations_struct shmem_vm_ops = {
 #endif
 };
 
-static struct dentry *shmem_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+int shmem_init_fs_context(struct fs_context *fc)
 {
-	return mount_nodev(fs_type, flags, data, shmem_fill_super);
+	struct shmem_fs_context *ctx;
+
+	ctx = kzalloc(sizeof(struct shmem_fs_context), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	fc->fs_private = ctx;
+	fc->ops = &shmem_fs_context_ops;
+	return 0;
 }
 
 static struct file_system_type shmem_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "tmpfs",
-	.mount		= shmem_mount,
+	.init_fs_context = shmem_init_fs_context,
+#ifdef CONFIG_TMPFS
+	.parameters	= &shmem_fs_parameters,
+#endif
 	.kill_sb	= kill_litter_super,
 	.fs_flags	= FS_USERNS_MOUNT,
 };
@@ -3916,7 +4042,8 @@ bool shmem_huge_enabled(struct vm_area_struct *vma)
 
 static struct file_system_type shmem_fs_type = {
 	.name		= "tmpfs",
-	.mount		= ramfs_mount,
+	.init_fs_context = ramfs_init_fs_context,
+	.parameters	= &ramfs_fs_parameters,
 	.kill_sb	= kill_litter_super,
 	.fs_flags	= FS_USERNS_MOUNT,
 };
@@ -4117,3 +4244,4 @@ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
 #endif
 }
 EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);
+


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 42/68] vfs: Create fs_context-aware mount_bdev() replacement
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (40 preceding siblings ...)
  2019-03-27 23:45 ` [RFC PATCH 41/68] vfs: Convert ramfs, shmem, tmpfs, devtmpfs, rootfs " David Howells
@ 2019-03-27 23:45 ` David Howells
  2019-03-27 23:45 ` [RFC PATCH 43/68] vfs: Make fs_parse() handle fs_param_is_fd-type params better David Howells
                   ` (25 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:45 UTC (permalink / raw)
  To: viro; +Cc: Jens Axboe, linux-block, linux-fsdevel, linux-kernel, dhowells

Create a function, vfs_get_block_super(), that is fs_context-aware and a
replacement for mount_bdev().  It caches the block device pointer and file
open mode in the fs_context struct so that this information can be passed
into sget_fc()'s test and set functions.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jens Axboe <axboe@kernel.dk>
cc: linux-block@vger.kernel.org
---

 fs/fs_context.c            |    2 +
 fs/super.c                 |  111 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs_context.h |    9 ++++
 3 files changed, 121 insertions(+), 1 deletion(-)

diff --git a/fs/fs_context.c b/fs/fs_context.c
index 87e3546b9a52..fc8fda4b9fe9 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -425,6 +425,8 @@ void put_fs_context(struct fs_context *fc)
 
 	if (fc->need_free && fc->ops && fc->ops->free)
 		fc->ops->free(fc);
+	if (fc->dev_destructor)
+		fc->dev_destructor(fc);
 
 	security_free_mnt_opts(&fc->security);
 	put_net(fc->net_ns);
diff --git a/fs/super.c b/fs/super.c
index f27ee08fb26f..e9e678d2c37c 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1211,6 +1211,110 @@ int vfs_get_super(struct fs_context *fc,
 EXPORT_SYMBOL(vfs_get_super);
 
 #ifdef CONFIG_BLOCK
+static void fc_bdev_destructor(struct fs_context *fc)
+{
+	if (fc->bdev) {
+		blkdev_put(fc->bdev, fc->bdev_mode);
+		fc->bdev = NULL;
+	}
+}
+
+static int set_bdev_super_fc(struct super_block *s, struct fs_context *fc)
+{
+	s->s_mode = fc->bdev_mode;
+	s->s_bdev = fc->bdev;
+	s->s_dev = s->s_bdev->bd_dev;
+	s->s_bdi = bdi_get(s->s_bdev->bd_bdi);
+	fc->bdev = NULL;
+	return 0;
+}
+
+static int test_bdev_super_fc(struct super_block *s, struct fs_context *fc)
+{
+	return s->s_bdev == fc->bdev;
+}
+
+/**
+ * vfs_get_block_super - Get a superblock based on a single block device
+ * @fc: The filesystem context holding the parameters
+ * @keying: How to distinguish superblocks
+ * @fill_super: Helper to initialise a new superblock
+ */
+int vfs_get_block_super(struct fs_context *fc,
+			int (*fill_super)(struct super_block *,
+					  struct fs_context *))
+{
+	struct block_device *bdev;
+	struct super_block *s;
+	int error = 0;
+
+	fc->bdev_mode = FMODE_READ | FMODE_EXCL;
+	if (!(fc->sb_flags & SB_RDONLY))
+		fc->bdev_mode |= FMODE_WRITE;
+
+	if (!fc->source)
+		return invalf(fc, "No source specified");
+
+	bdev = blkdev_get_by_path(fc->source, fc->bdev_mode, fc->fs_type);
+	if (IS_ERR(bdev)) {
+		errorf(fc, "%s: Can't open blockdev", fc->source);
+		return PTR_ERR(bdev);
+	}
+
+	fc->dev_destructor = fc_bdev_destructor;
+	fc->bdev = bdev;
+
+	/* Once the superblock is inserted into the list by sget_fc(), s_umount
+	 * will protect the lockfs code from trying to start a snapshot while
+	 * we are mounting
+	 */
+	mutex_lock(&bdev->bd_fsfreeze_mutex);
+	if (bdev->bd_fsfreeze_count > 0) {
+		mutex_unlock(&bdev->bd_fsfreeze_mutex);
+		warnf(fc, "%pg: Can't mount, blockdev is frozen", bdev);
+		return -EBUSY;
+	}
+
+	fc->sb_flags |= SB_NOSEC;
+	s = sget_fc(fc, test_bdev_super_fc, set_bdev_super_fc);
+	mutex_unlock(&bdev->bd_fsfreeze_mutex);
+	if (IS_ERR(s))
+		return PTR_ERR(s);
+
+	if (s->s_root) {
+		/* Don't summarily change the RO/RW state. */
+		if ((fc->sb_flags ^ s->s_flags) & SB_RDONLY) {
+			warnf(fc, "%pg: Can't mount, would change RO state", bdev);
+			error = -EBUSY;
+			goto error_sb;
+		}
+
+		/* Leave fc->bdev to fc_bdev_destructor() to clean up to avoid
+		 * locking conflicts.
+		 */
+	} else {
+		snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
+		sb_set_blocksize(s, block_size(bdev));
+		error = fill_super(s, fc);
+		if (error)
+			goto error_sb;
+
+		s->s_flags |= SB_ACTIVE;
+		bdev->bd_super = s;
+	}
+
+	BUG_ON(fc->root);
+	fc->root = dget(s->s_root);
+	return 0;
+
+error_sb:
+	deactivate_locked_super(s);
+	/* Leave fc->bdev to fc_bdev_destructor() to clean up */
+	return error;
+}
+EXPORT_SYMBOL(vfs_get_block_super);
+
+
 static int set_bdev_super(struct super_block *s, void *data)
 {
 	s->s_bdev = data;
@@ -1415,8 +1519,13 @@ int vfs_get_tree(struct fs_context *fc)
 	 * on the superblock.
 	 */
 	error = fc->ops->get_tree(fc);
-	if (error < 0)
+	if (error < 0) {
+		if (fc->dev_destructor) {
+			fc->dev_destructor(fc);
+			fc->dev_destructor = NULL;
+		}
 		return error;
+	}
 
 	if (!fc->root) {
 		pr_err("Filesystem %s get_tree() didn't set fc->root\n",
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index a779022d06f5..9af788a3ef8e 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -76,6 +76,9 @@ struct fs_context {
 	const struct fs_context_operations *ops;
 	struct file_system_type	*fs_type;
 	void			*fs_private;	/* The filesystem's context */
+	union {
+		struct block_device *bdev;	/* The backing blockdev (if applicable) */
+	};
 	struct dentry		*root;		/* The root and superblock */
 	struct user_namespace	*user_ns;	/* The user namespace for this mount */
 	struct net		*net_ns;	/* The network namespace for this mount */
@@ -84,6 +87,7 @@ struct fs_context {
 	const char		*subtype;	/* The subtype to set on the superblock */
 	void			*security;	/* Linux S&M options */
 	void			*s_fs_info;	/* Proposed s_fs_info */
+	fmode_t			bdev_mode;	/* File open mode for bdev */
 	unsigned int		sb_flags;	/* Proposed superblock flags (SB_*) */
 	unsigned int		sb_flags_mask;	/* Superblock flags that were changed */
 	unsigned int		s_iflags;	/* OR'd with sb->s_iflags */
@@ -91,6 +95,7 @@ struct fs_context {
 	enum fs_context_purpose	purpose:8;
 	bool			need_free:1;	/* Need to call ops->free() */
 	bool			global:1;	/* Goes into &init_user_ns */
+	void (*dev_destructor)(struct fs_context *fc); /* For block or mtd */
 };
 
 struct fs_context_operations {
@@ -141,6 +146,10 @@ extern int vfs_get_super(struct fs_context *fc,
 			 int (*fill_super)(struct super_block *sb,
 					   struct fs_context *fc));
 
+extern int vfs_get_block_super(struct fs_context *fc,
+			       int (*fill_super)(struct super_block *sb,
+						 struct fs_context *fc));
+
 extern const struct file_operations fscontext_fops;
 
 #ifdef CONFIG_PRINTK


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 43/68] vfs: Make fs_parse() handle fs_param_is_fd-type params better
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (41 preceding siblings ...)
  2019-03-27 23:45 ` [RFC PATCH 42/68] vfs: Create fs_context-aware mount_bdev() replacement David Howells
@ 2019-03-27 23:45 ` David Howells
  2019-03-27 23:46 ` [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API David Howells
                   ` (24 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:45 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Make fs_parse() handle fs_param_is_fd-type parameters that are passed a
string by converting it to an integer (in addition to handling direct fd
specification).

Also range check the integer.

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

 fs/fs_parser.c |   15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/fs/fs_parser.c b/fs/fs_parser.c
index be86703ec371..bd59e1725c50 100644
--- a/fs/fs_parser.c
+++ b/fs/fs_parser.c
@@ -208,9 +208,20 @@ int fs_parse(struct fs_context *fc,
 		goto okay;
 
 	case fs_param_is_fd: {
-		if (param->type != fs_value_is_file)
+		switch (param->type) {
+		case fs_value_is_string:
+			ret = kstrtouint(param->string, 0, &result->uint_32);
+			break;
+		case fs_value_is_file:
+			result->uint_32 = param->dirfd;
+			ret = 0;
+		default:
 			goto bad_value;
-		goto okay;
+		}
+
+		if (result->uint_32 > INT_MAX)
+			goto bad_value;
+		goto maybe_okay;
 	}
 
 	case fs_param_is_blockdev:


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (42 preceding siblings ...)
  2019-03-27 23:45 ` [RFC PATCH 43/68] vfs: Make fs_parse() handle fs_param_is_fd-type params better David Howells
@ 2019-03-27 23:46 ` David Howells
  2019-04-24 13:53   ` Miklos Szeredi
  2019-04-24 15:22   ` David Howells
  2019-03-27 23:46 ` [RFC PATCH 45/68] vfs: Move the subtype parameter into fuse David Howells
                   ` (23 subsequent siblings)
  67 siblings, 2 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:46 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Convert the fuse filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.
---

 fs/fuse/inode.c |  274 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 149 insertions(+), 125 deletions(-)

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index ec5d9953dfb6..bfcde29e4a1e 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -15,7 +15,8 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/statfs.h>
 #include <linux/random.h>
 #include <linux/sched.h>
@@ -59,7 +60,12 @@ MODULE_PARM_DESC(max_user_congthresh,
 /** Congestion starts at 75% of maximum */
 #define FUSE_DEFAULT_CONGESTION_THRESHOLD (FUSE_DEFAULT_MAX_BACKGROUND * 3 / 4)
 
-struct fuse_mount_data {
+#ifdef CONFIG_BLOCK
+static struct file_system_type fuseblk_fs_type;
+#endif
+
+struct fuse_fs_context {
+	bool		is_bdev;
 	int fd;
 	unsigned rootmode;
 	kuid_t user_id;
@@ -448,6 +454,7 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
 }
 
 enum {
+	OPT_SOURCE,
 	OPT_FD,
 	OPT_ROOTMODE,
 	OPT_USER_ID,
@@ -459,111 +466,97 @@ enum {
 	OPT_ERR
 };
 
-static const match_table_t tokens = {
-	{OPT_FD,			"fd=%u"},
-	{OPT_ROOTMODE,			"rootmode=%o"},
-	{OPT_USER_ID,			"user_id=%u"},
-	{OPT_GROUP_ID,			"group_id=%u"},
-	{OPT_DEFAULT_PERMISSIONS,	"default_permissions"},
-	{OPT_ALLOW_OTHER,		"allow_other"},
-	{OPT_MAX_READ,			"max_read=%u"},
-	{OPT_BLKSIZE,			"blksize=%u"},
-	{OPT_ERR,			NULL}
+static const struct fs_parameter_spec fuse_param_specs[] = {
+	fsparam_string	("source",		OPT_SOURCE),
+	fsparam_fd	("fd",			OPT_FD),
+	fsparam_u32oct	("rootmode",		OPT_ROOTMODE),
+	fsparam_u32	("user_id",		OPT_USER_ID),
+	fsparam_u32	("group_id",		OPT_GROUP_ID),
+	fsparam_flag	("default_permissions",	OPT_DEFAULT_PERMISSIONS),
+	fsparam_flag	("allow_other",		OPT_ALLOW_OTHER),
+	fsparam_u32	("max_read",		OPT_MAX_READ),
+	fsparam_u32	("blksize",		OPT_BLKSIZE),
+	{}
+};
+
+static const struct fs_parameter_description fuse_fs_parameters = {
+	.name		= "fuse",
+	.specs		= fuse_param_specs,
 };
 
-static int fuse_match_uint(substring_t *s, unsigned int *res)
+static int fuse_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	int err = -ENOMEM;
-	char *buf = match_strdup(s);
-	if (buf) {
-		err = kstrtouint(buf, 10, res);
-		kfree(buf);
+	struct fs_parse_result result;
+	struct fuse_fs_context *ctx = fc->fs_private;
+	int opt;
+
+	opt = fs_parse(fc, &fuse_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case OPT_SOURCE:
+		kfree(fc->source);
+		fc->source = param->string;
+		param->string = NULL;
+		break;
+
+	case OPT_FD:
+		ctx->fd = result.uint_32;
+		ctx->fd_present = 1;
+		break;
+
+	case OPT_ROOTMODE:
+		if (!fuse_valid_type(result.uint_32))
+			return invalf(fc, "fuse: Invalid rootmode");
+		ctx->rootmode = result.uint_32;
+		ctx->rootmode_present = 1;
+		break;
+
+	case OPT_USER_ID:
+		ctx->user_id = make_kuid(fc->user_ns, result.uint_32);
+		if (!uid_valid(ctx->user_id))
+			return invalf(fc, "fuse: Invalid user_id");
+		ctx->user_id_present = 1;
+		break;
+
+	case OPT_GROUP_ID:
+		ctx->group_id = make_kgid(fc->user_ns, result.uint_32);
+		if (!gid_valid(ctx->group_id))
+			return invalf(fc, "fuse: Invalid group_id");
+		ctx->group_id_present = 1;
+		break;
+
+	case OPT_DEFAULT_PERMISSIONS:
+		ctx->default_permissions = 1;
+		break;
+
+	case OPT_ALLOW_OTHER:
+		ctx->allow_other = 1;
+		break;
+
+	case OPT_MAX_READ:
+		ctx->max_read = result.uint_32;
+		break;
+
+	case OPT_BLKSIZE:
+		if (!ctx->is_bdev)
+			return invalf(fc, "fuse: blksize only supported for fuseblk");
+		ctx->blksize = result.uint_32;
+		break;
+
+	default:
+		return -EINVAL;
 	}
-	return err;
+
+	return 0;
 }
 
-static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev,
-			  struct user_namespace *user_ns)
+static void fuse_free_fc(struct fs_context *fc)
 {
-	char *p;
-	memset(d, 0, sizeof(struct fuse_mount_data));
-	d->max_read = ~0;
-	d->blksize = FUSE_DEFAULT_BLKSIZE;
-
-	while ((p = strsep(&opt, ",")) != NULL) {
-		int token;
-		int value;
-		unsigned uv;
-		substring_t args[MAX_OPT_ARGS];
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case OPT_FD:
-			if (match_int(&args[0], &value))
-				return 0;
-			d->fd = value;
-			d->fd_present = 1;
-			break;
-
-		case OPT_ROOTMODE:
-			if (match_octal(&args[0], &value))
-				return 0;
-			if (!fuse_valid_type(value))
-				return 0;
-			d->rootmode = value;
-			d->rootmode_present = 1;
-			break;
-
-		case OPT_USER_ID:
-			if (fuse_match_uint(&args[0], &uv))
-				return 0;
-			d->user_id = make_kuid(user_ns, uv);
-			if (!uid_valid(d->user_id))
-				return 0;
-			d->user_id_present = 1;
-			break;
-
-		case OPT_GROUP_ID:
-			if (fuse_match_uint(&args[0], &uv))
-				return 0;
-			d->group_id = make_kgid(user_ns, uv);
-			if (!gid_valid(d->group_id))
-				return 0;
-			d->group_id_present = 1;
-			break;
-
-		case OPT_DEFAULT_PERMISSIONS:
-			d->default_permissions = 1;
-			break;
-
-		case OPT_ALLOW_OTHER:
-			d->allow_other = 1;
-			break;
-
-		case OPT_MAX_READ:
-			if (match_int(&args[0], &value))
-				return 0;
-			d->max_read = value;
-			break;
-
-		case OPT_BLKSIZE:
-			if (!is_bdev || match_int(&args[0], &value))
-				return 0;
-			d->blksize = value;
-			break;
-
-		default:
-			return 0;
-		}
-	}
+	struct fuse_fs_context *ctx = fc->fs_private;
 
-	if (!d->fd_present || !d->rootmode_present ||
-	    !d->user_id_present || !d->group_id_present)
-		return 0;
-
-	return 1;
+	kfree(ctx);
 }
 
 static int fuse_show_options(struct seq_file *m, struct dentry *root)
@@ -1078,12 +1071,12 @@ void fuse_dev_free(struct fuse_dev *fud)
 }
 EXPORT_SYMBOL_GPL(fuse_dev_free);
 
-static int fuse_fill_super(struct super_block *sb, void *data, int silent)
+static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
 {
+	struct fuse_fs_context *ctx = fsc->fs_private;
 	struct fuse_dev *fud;
 	struct fuse_conn *fc;
 	struct inode *root;
-	struct fuse_mount_data d;
 	struct file *file;
 	struct dentry *root_dentry;
 	struct fuse_req *init_req;
@@ -1096,13 +1089,10 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 
 	sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
 
-	if (!parse_fuse_opt(data, &d, is_bdev, sb->s_user_ns))
-		goto err;
-
 	if (is_bdev) {
 #ifdef CONFIG_BLOCK
 		err = -EINVAL;
-		if (!sb_set_blocksize(sb, d.blksize))
+		if (!sb_set_blocksize(sb, ctx->blksize))
 			goto err;
 #endif
 	} else {
@@ -1119,7 +1109,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 	if (sb->s_user_ns != &init_user_ns)
 		sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
 
-	file = fget(d.fd);
+	file = fget(ctx->fd);
 	err = -EINVAL;
 	if (!file)
 		goto err;
@@ -1162,17 +1152,17 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 		fc->dont_mask = 1;
 	sb->s_flags |= SB_POSIXACL;
 
-	fc->default_permissions = d.default_permissions;
-	fc->allow_other = d.allow_other;
-	fc->user_id = d.user_id;
-	fc->group_id = d.group_id;
-	fc->max_read = max_t(unsigned, 4096, d.max_read);
+	fc->default_permissions = ctx->default_permissions;
+	fc->allow_other = ctx->allow_other;
+	fc->user_id = ctx->user_id;
+	fc->group_id = ctx->group_id;
+	fc->max_read = max_t(unsigned, 4096, ctx->max_read);
 
 	/* Used by get_root_inode() */
 	sb->s_fs_info = fc;
 
 	err = -ENOMEM;
-	root = fuse_get_root_inode(sb, d.rootmode);
+	root = fuse_get_root_inode(sb, ctx->rootmode);
 	sb->s_d_op = &fuse_root_dentry_operations;
 	root_dentry = d_make_root(root);
 	if (!root_dentry)
@@ -1232,11 +1222,50 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 	return err;
 }
 
-static struct dentry *fuse_mount(struct file_system_type *fs_type,
-		       int flags, const char *dev_name,
-		       void *raw_data)
+static int fuse_get_tree(struct fs_context *fc)
+{
+	struct fuse_fs_context *ctx = fc->fs_private;
+
+	if (!ctx->fd_present || !ctx->rootmode_present ||
+	    !ctx->user_id_present || !ctx->group_id_present)
+		return invalf(fc, "fuse: Missing mount parameter(s)");
+
+#ifdef CONFIG_BLOCK
+	if (ctx->is_bdev)
+		return vfs_get_block_super(fc, fuse_fill_super);
+#endif
+
+	return vfs_get_super(fc, vfs_get_independent_super, fuse_fill_super);
+}
+
+static const struct fs_context_operations fuse_context_ops = {
+	.free		= fuse_free_fc,
+	.parse_param	= fuse_parse_param,
+	.get_tree	= fuse_get_tree,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+static int fuse_init_fs_context(struct fs_context *fc)
 {
-	return mount_nodev(fs_type, flags, raw_data, fuse_fill_super);
+	struct fuse_fs_context *ctx;
+
+	ctx = kzalloc(sizeof(struct fuse_fs_context), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->max_read = ~0;
+	ctx->blksize = FUSE_DEFAULT_BLKSIZE;
+
+#ifdef CONFIG_BLOCK
+	if (fc->fs_type == &fuseblk_fs_type)
+		ctx->is_bdev = true;
+#endif
+
+	fc->fs_private = ctx;
+	fc->ops = &fuse_context_ops;
+	return 0;
 }
 
 static void fuse_sb_destroy(struct super_block *sb)
@@ -1265,19 +1294,13 @@ static struct file_system_type fuse_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "fuse",
 	.fs_flags	= FS_HAS_SUBTYPE | FS_USERNS_MOUNT,
-	.mount		= fuse_mount,
+	.init_fs_context = fuse_init_fs_context,
+	.parameters	= &fuse_fs_parameters,
 	.kill_sb	= fuse_kill_sb_anon,
 };
 MODULE_ALIAS_FS("fuse");
 
 #ifdef CONFIG_BLOCK
-static struct dentry *fuse_mount_blk(struct file_system_type *fs_type,
-			   int flags, const char *dev_name,
-			   void *raw_data)
-{
-	return mount_bdev(fs_type, flags, dev_name, raw_data, fuse_fill_super);
-}
-
 static void fuse_kill_sb_blk(struct super_block *sb)
 {
 	fuse_sb_destroy(sb);
@@ -1287,7 +1310,8 @@ static void fuse_kill_sb_blk(struct super_block *sb)
 static struct file_system_type fuseblk_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "fuseblk",
-	.mount		= fuse_mount_blk,
+	.init_fs_context = fuse_init_fs_context,
+	.parameters	= &fuse_fs_parameters,
 	.kill_sb	= fuse_kill_sb_blk,
 	.fs_flags	= FS_REQUIRES_DEV | FS_HAS_SUBTYPE,
 };


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 45/68] vfs: Move the subtype parameter into fuse
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (43 preceding siblings ...)
  2019-03-27 23:46 ` [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API David Howells
@ 2019-03-27 23:46 ` David Howells
  2019-03-27 23:46 ` [RFC PATCH 46/68] mtd: Provide fs_context-aware mount_mtd() replacement David Howells
                   ` (22 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:46 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Move as much as possible of the mount subtype apparatus into the fuse
driver.  The bits that are left involve determining whether it's permitted
to split the filesystem type string passed in to mount(2).  Consequently,
this means that we cannot get rid of the FS_HAS_SUBTYPE flag unless we
define that a type string with a dot in in always indicates a subtype
specification.

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

 fs/fs_context.c            |   12 ------------
 fs/fuse/inode.c            |   21 +++++++++++++++++++--
 fs/super.c                 |    5 -----
 include/linux/fs_context.h |    1 -
 4 files changed, 19 insertions(+), 20 deletions(-)

diff --git a/fs/fs_context.c b/fs/fs_context.c
index fc8fda4b9fe9..f9ad2554c2b8 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -432,7 +432,6 @@ void put_fs_context(struct fs_context *fc)
 	put_net(fc->net_ns);
 	put_user_ns(fc->user_ns);
 	put_cred(fc->cred);
-	kfree(fc->subtype);
 	put_filesystem(fc->fs_type);
 	kfree(fc->source);
 	kfree(fc);
@@ -498,17 +497,6 @@ static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 	}
 
-	if ((fc->fs_type->fs_flags & FS_HAS_SUBTYPE) &&
-	    strcmp(param->key, "subtype") == 0) {
-		if (param->type != fs_value_is_string)
-			return invalf(fc, "VFS: Legacy: Non-string subtype");
-		if (fc->subtype)
-			return invalf(fc, "VFS: Legacy: Multiple subtype");
-		fc->subtype = param->string;
-		param->string = NULL;
-		return 0;
-	}
-
 	if (ctx->param_type == LEGACY_FS_MONOLITHIC_PARAMS)
 		return invalf(fc, "VFS: Legacy: Can't mix monolithic and individual options");
 
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index bfcde29e4a1e..a655c1ef1424 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -65,6 +65,7 @@ static struct file_system_type fuseblk_fs_type;
 #endif
 
 struct fuse_fs_context {
+	const char	*subtype;
 	bool		is_bdev;
 	int fd;
 	unsigned rootmode;
@@ -455,6 +456,7 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 enum {
 	OPT_SOURCE,
+	OPT_SUBTYPE,
 	OPT_FD,
 	OPT_ROOTMODE,
 	OPT_USER_ID,
@@ -468,6 +470,7 @@ enum {
 
 static const struct fs_parameter_spec fuse_param_specs[] = {
 	fsparam_string	("source",		OPT_SOURCE),
+	fsparam_string	("subtype",		OPT_SUBTYPE),
 	fsparam_fd	("fd",			OPT_FD),
 	fsparam_u32oct	("rootmode",		OPT_ROOTMODE),
 	fsparam_u32	("user_id",		OPT_USER_ID),
@@ -496,11 +499,19 @@ static int fuse_parse_param(struct fs_context *fc, struct fs_parameter *param)
 
 	switch (opt) {
 	case OPT_SOURCE:
-		kfree(fc->source);
+		if (fc->source)
+			return invalf(fc, "fuse: Multiple sources specified");
 		fc->source = param->string;
 		param->string = NULL;
 		break;
 
+	case OPT_SUBTYPE:
+		if (ctx->subtype)
+			return invalf(fc, "fuse: Multiple subtypes specified");
+		ctx->subtype = param->string;
+		param->string = NULL;
+		return 0;
+
 	case OPT_FD:
 		ctx->fd = result.uint_32;
 		ctx->fd_present = 1;
@@ -556,7 +567,10 @@ static void fuse_free_fc(struct fs_context *fc)
 {
 	struct fuse_fs_context *ctx = fc->fs_private;
 
-	kfree(ctx);
+	if (ctx) {
+		kfree(ctx->subtype);
+		kfree(ctx);
+	}
 }
 
 static int fuse_show_options(struct seq_file *m, struct dentry *root)
@@ -1099,6 +1113,9 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
 		sb->s_blocksize = PAGE_SIZE;
 		sb->s_blocksize_bits = PAGE_SHIFT;
 	}
+
+	sb->s_subtype = ctx->subtype;
+	ctx->subtype = NULL;
 	sb->s_magic = FUSE_SUPER_MAGIC;
 	sb->s_op = &fuse_super_operations;
 	sb->s_xattr = fuse_xattr_handlers;
diff --git a/fs/super.c b/fs/super.c
index e9e678d2c37c..6cb57ad31e6f 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1539,11 +1539,6 @@ int vfs_get_tree(struct fs_context *fc)
 	sb = fc->root->d_sb;
 	WARN_ON(!sb->s_bdi);
 
-	if (fc->subtype && !sb->s_subtype) {
-		sb->s_subtype = fc->subtype;
-		fc->subtype = NULL;
-	}
-
 	/*
 	 * Write barrier is for super_cache_count(). We place it before setting
 	 * SB_BORN as the data dependency between the two functions is the
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 9af788a3ef8e..70fdebf1dc85 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -84,7 +84,6 @@ struct fs_context {
 	struct net		*net_ns;	/* The network namespace for this mount */
 	const struct cred	*cred;		/* The mounter's credentials */
 	const char		*source;	/* The source name (eg. dev path) */
-	const char		*subtype;	/* The subtype to set on the superblock */
 	void			*security;	/* Linux S&M options */
 	void			*s_fs_info;	/* Proposed s_fs_info */
 	fmode_t			bdev_mode;	/* File open mode for bdev */


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 46/68] mtd: Provide fs_context-aware mount_mtd() replacement
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (44 preceding siblings ...)
  2019-03-27 23:46 ` [RFC PATCH 45/68] vfs: Move the subtype parameter into fuse David Howells
@ 2019-03-27 23:46 ` David Howells
  2019-03-27 23:46 ` [RFC PATCH 47/68] vfs: Convert romfs to use the new mount API David Howells
                   ` (21 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:46 UTC (permalink / raw)
  To: viro
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, linux-mtd, linux-fsdevel, linux-kernel,
	dhowells

Provide a function, vfs_get_mtd_super(), to replace mount_mtd(), using an
fs_context struct to hold the parameters.  The mtd device pointer is cached
in the struct, along with a destructor pointer as the mtd core may be in a
module and, as such, unreachable by the core code that would release the
fs_context.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: David Woodhouse <dwmw2@infradead.org>
cc: Brian Norris <computersforpeace@gmail.com>
cc: Boris Brezillon <bbrezillon@kernel.org>
cc: Marek Vasut <marek.vasut@gmail.com>
cc: Richard Weinberger <richard@nod.at>
cc: linux-mtd@lists.infradead.org
---

 drivers/mtd/mtdcore.h      |    1 
 drivers/mtd/mtdsuper.c     |  192 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs_context.h |    3 -
 include/linux/mtd/super.h  |    3 +
 4 files changed, 196 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
index b31c868019ad..b5eefeabf310 100644
--- a/drivers/mtd/mtdcore.h
+++ b/drivers/mtd/mtdcore.h
@@ -5,6 +5,7 @@
  */
 
 extern struct mutex mtd_table_mutex;
+extern struct backing_dev_info *mtd_bdi;
 
 struct mtd_info *__mtd_next_device(int i);
 int __must_check add_mtd_device(struct mtd_info *mtd);
diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c
index d58a61c09304..cf165f7c8632 100644
--- a/drivers/mtd/mtdsuper.c
+++ b/drivers/mtd/mtdsuper.c
@@ -19,6 +19,196 @@
 #include <linux/slab.h>
 #include <linux/major.h>
 #include <linux/backing-dev.h>
+#include <linux/fs_context.h>
+#include "mtdcore.h"
+
+static void mtd_fc_destructor(struct fs_context *fc)
+{
+	if (fc->mtd)
+		put_mtd_device(fc->mtd);
+}
+
+/*
+ * compare superblocks to see if they're equivalent
+ * - they are if the underlying MTD device is the same
+ */
+static int mtd_test_super(struct super_block *sb, struct fs_context *fc)
+{
+	struct mtd_info *mtd = fc->mtd;
+
+	if (sb->s_mtd == mtd) {
+		pr_debug("MTDSB: Match on device %d (\"%s\")\n",
+			 mtd->index, mtd->name);
+		return 1;
+	}
+
+	pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
+		 sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
+	return 0;
+}
+
+/*
+ * mark the superblock by the MTD device it is using
+ * - set the device number to be the correct MTD block device for pesuperstence
+ *   of NFS exports
+ */
+static int mtd_set_super(struct super_block *sb, struct fs_context *fc)
+{
+	struct mtd_info *mtd = fc->mtd;
+
+	sb->s_mtd = mtd;
+	sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
+	sb->s_bdi = bdi_get(mtd_bdi);
+	fc->mtd = NULL;
+	return 0;
+}
+
+/*
+ * get a superblock on an MTD-backed filesystem
+ */
+static int mtd_get_sb(struct fs_context *fc,
+		      int (*fill_super)(struct super_block *,
+					struct fs_context *))
+{
+	struct super_block *sb;
+	struct mtd_info *mtd = fc->mtd;
+	int ret;
+
+	sb = sget_fc(fc, mtd_test_super, mtd_set_super);
+	if (IS_ERR(sb))
+		return PTR_ERR(sb);
+
+	if (sb->s_root) {
+		/* new mountpoint for an already mounted superblock */
+		pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n",
+			 mtd->index, mtd->name);
+		put_mtd_device(mtd);
+	} else {
+		/* fresh new superblock */
+		pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
+			 mtd->index, mtd->name);
+
+		ret = fill_super(sb, fc);
+		if (ret < 0)
+			goto error_sb;
+
+		sb->s_flags |= SB_ACTIVE;
+	}
+
+	BUG_ON(fc->root);
+	fc->root = dget(sb->s_root);
+	return 0;
+
+error_sb:
+	deactivate_locked_super(sb);
+	return ret;
+}
+
+/*
+ * get a superblock on an MTD-backed filesystem by MTD device number
+ */
+static int mtd_get_sb_by_nr(struct fs_context *fc, int mtdnr,
+			    int (*fill_super)(struct super_block *,
+					      struct fs_context *))
+{
+	struct mtd_info *mtd;
+
+	mtd = get_mtd_device(NULL, mtdnr);
+	if (IS_ERR(mtd)) {
+		errorf(fc, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
+		return PTR_ERR(mtd);
+	}
+
+	fc->mtd = mtd;
+	fc->dev_destructor = mtd_fc_destructor;
+	return mtd_get_sb(fc, fill_super);
+}
+
+/**
+ * vfs_get_mtd_super - Get a superblock based on a single MTD device
+ * @fc: The filesystem context holding the parameters
+ * @fill_super: Helper to initialise a new superblock
+ */
+int vfs_get_mtd_super(struct fs_context *fc,
+		      int (*fill_super)(struct super_block *sb,
+					struct fs_context *fc))
+{
+#ifdef CONFIG_BLOCK
+	struct block_device *bdev;
+	int ret, major;
+#endif
+	int mtdnr;
+
+	if (!fc->source)
+		return invalf(fc, "No source specified");
+
+	pr_debug("MTDSB: dev_name \"%s\"\n", fc->source);
+
+	/* the preferred way of mounting in future; especially when
+	 * CONFIG_BLOCK=n - we specify the underlying MTD device by number or
+	 * by name, so that we don't require block device support to be present
+	 * in the kernel.
+	 */
+	if (fc->source[0] == 'm' &&
+	    fc->source[1] == 't' &&
+	    fc->source[2] == 'd') {
+		if (fc->source[3] == ':') {
+			struct mtd_info *mtd;
+
+			/* mount by MTD device name */
+			pr_debug("MTDSB: mtd:%%s, name \"%s\"\n",
+				 fc->source + 4);
+
+			mtd = get_mtd_device_nm(fc->source + 4);
+			if (!IS_ERR(mtd)) {
+				fc->mtd = mtd;
+				fc->dev_destructor = mtd_fc_destructor;
+				return mtd_get_sb(fc, fill_super);
+			}
+
+			errorf(fc, "MTD: MTD device with name \"%s\" not found",
+			       fc->source + 4);
+
+		} else if (isdigit(fc->source[3])) {
+			/* mount by MTD device number name */
+			char *endptr;
+
+			mtdnr = simple_strtoul(fc->source + 3, &endptr, 0);
+			if (!*endptr) {
+				/* It was a valid number */
+				pr_debug("MTDSB: mtd%%d, mtdnr %d\n", mtdnr);
+				return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
+			}
+		}
+	}
+
+#ifdef CONFIG_BLOCK
+	/* try the old way - the hack where we allowed users to mount
+	 * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
+	 */
+	bdev = lookup_bdev(fc->source);
+	if (IS_ERR(bdev)) {
+		ret = PTR_ERR(bdev);
+		errorf(fc, "MTD: Couldn't look up '%s': %d", fc->source, ret);
+		return ret;
+	}
+	pr_debug("MTDSB: lookup_bdev() returned 0\n");
+
+	major = MAJOR(bdev->bd_dev);
+	mtdnr = MINOR(bdev->bd_dev);
+	bdput(bdev);
+
+	if (major == MTD_BLOCK_MAJOR)
+		return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
+
+#endif /* CONFIG_BLOCK */
+
+	if (!(fc->sb_flags & SB_SILENT))
+		errorf(fc, "MTD: Attempt to mount non-MTD device \"%s\"",
+		       fc->source);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(vfs_get_mtd_super);
 
 /*
  * compare superblocks to see if they're equivalent
@@ -39,8 +229,6 @@ static int get_sb_mtd_compare(struct super_block *sb, void *_mtd)
 	return 0;
 }
 
-extern struct backing_dev_info *mtd_bdi;
-
 /*
  * mark the superblock by the MTD device it is using
  * - set the device number to be the correct MTD block device for pesuperstence
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 70fdebf1dc85..7c7eefb35cc3 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -77,7 +77,8 @@ struct fs_context {
 	struct file_system_type	*fs_type;
 	void			*fs_private;	/* The filesystem's context */
 	union {
-		struct block_device *bdev;	/* The backing blockdev (if applicable) */
+		struct block_device	*bdev;	/* The backing blockdev (if applicable) */
+		struct mtd_info		*mtd;	/* The backing mtd (if applicable) */
 	};
 	struct dentry		*root;		/* The root and superblock */
 	struct user_namespace	*user_ns;	/* The user namespace for this mount */
diff --git a/include/linux/mtd/super.h b/include/linux/mtd/super.h
index f456230f9330..7ebd69e5c640 100644
--- a/include/linux/mtd/super.h
+++ b/include/linux/mtd/super.h
@@ -18,6 +18,9 @@
 #include <linux/fs.h>
 #include <linux/mount.h>
 
+extern int vfs_get_mtd_super(struct fs_context *fc,
+			     int (*fill_super)(struct super_block *sb,
+					       struct fs_context *fc));
 extern struct dentry *mount_mtd(struct file_system_type *fs_type, int flags,
 		      const char *dev_name, void *data,
 		      int (*fill_super)(struct super_block *, void *, int));


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 47/68] vfs: Convert romfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (45 preceding siblings ...)
  2019-03-27 23:46 ` [RFC PATCH 46/68] mtd: Provide fs_context-aware mount_mtd() replacement David Howells
@ 2019-03-27 23:46 ` David Howells
  2019-03-27 23:46 ` [RFC PATCH 48/68] vfs: Convert cramfs " David Howells
                   ` (20 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:46 UTC (permalink / raw)
  To: viro; +Cc: linux-mtd, linux-block, linux-fsdevel, linux-kernel, dhowells

Convert the romfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-mtd@lists.infradead.org
cc: linux-block@vger.kernel.org
---

 fs/romfs/super.c |   46 ++++++++++++++++++++++++++++------------------
 1 file changed, 28 insertions(+), 18 deletions(-)

diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index 6ccb51993a76..627c2b64ff03 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -65,7 +65,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
 #include <linux/mount.h>
 #include <linux/namei.h>
 #include <linux/statfs.h>
@@ -430,10 +430,10 @@ static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 /*
  * remounting must involve read-only
  */
-static int romfs_remount(struct super_block *sb, int *flags, char *data)
+static int romfs_reconfigure(struct fs_context *fc)
 {
-	sync_filesystem(sb);
-	*flags |= SB_RDONLY;
+	sync_filesystem(fc->root->d_sb);
+	fc->sb_flags |= SB_RDONLY;
 	return 0;
 }
 
@@ -441,7 +441,6 @@ static const struct super_operations romfs_super_ops = {
 	.alloc_inode	= romfs_alloc_inode,
 	.destroy_inode	= romfs_destroy_inode,
 	.statfs		= romfs_statfs,
-	.remount_fs	= romfs_remount,
 };
 
 /*
@@ -464,7 +463,7 @@ static __u32 romfs_checksum(const void *data, int size)
 /*
  * fill in the superblock
  */
-static int romfs_fill_super(struct super_block *sb, void *data, int silent)
+static int romfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct romfs_super_block *rsb;
 	struct inode *root;
@@ -511,8 +510,8 @@ static int romfs_fill_super(struct super_block *sb, void *data, int silent)
 
 	if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 ||
 	    img_size < ROMFH_SIZE) {
-		if (!silent)
-			pr_warn("VFS: Can't find a romfs filesystem on dev %s.\n",
+		if (!(fc->sb_flags & SB_SILENT))
+			errorf(fc, "VFS: Can't find a romfs filesystem on dev %s.\n",
 			       sb->s_id);
 		goto error_rsb_inval;
 	}
@@ -525,7 +524,7 @@ static int romfs_fill_super(struct super_block *sb, void *data, int silent)
 	storage = sb->s_mtd ? "MTD" : "the block layer";
 
 	len = strnlen(rsb->name, ROMFS_MAXFN);
-	if (!silent)
+	if (!(fc->sb_flags & SB_SILENT))
 		pr_notice("Mounting image '%*.*s' through %s\n",
 			  (unsigned) len, (unsigned) len, rsb->name, storage);
 
@@ -555,23 +554,34 @@ static int romfs_fill_super(struct super_block *sb, void *data, int silent)
 /*
  * get a superblock for mounting
  */
-static struct dentry *romfs_mount(struct file_system_type *fs_type,
-			int flags, const char *dev_name,
-			void *data)
+static int romfs_get_tree(struct fs_context *fc)
 {
-	struct dentry *ret = ERR_PTR(-EINVAL);
+	int ret = -EINVAL;
 
 #ifdef CONFIG_ROMFS_ON_MTD
-	ret = mount_mtd(fs_type, flags, dev_name, data, romfs_fill_super);
+	ret = vfs_get_mtd_super(fc, romfs_fill_super);
 #endif
 #ifdef CONFIG_ROMFS_ON_BLOCK
-	if (ret == ERR_PTR(-EINVAL))
-		ret = mount_bdev(fs_type, flags, dev_name, data,
-				  romfs_fill_super);
+	if (ret == -EINVAL)
+		ret = vfs_get_block_super(fc, romfs_fill_super);
 #endif
 	return ret;
 }
 
+static const struct fs_context_operations romfs_context_ops = {
+	.get_tree	= romfs_get_tree,
+	.reconfigure	= romfs_reconfigure,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+static int romfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &romfs_context_ops;
+	return 0;
+}
+
 /*
  * destroy a romfs superblock in the appropriate manner
  */
@@ -594,7 +604,7 @@ static void romfs_kill_sb(struct super_block *sb)
 static struct file_system_type romfs_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "romfs",
-	.mount		= romfs_mount,
+	.init_fs_context = romfs_init_fs_context,
 	.kill_sb	= romfs_kill_sb,
 	.fs_flags	= FS_REQUIRES_DEV,
 };


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 48/68] vfs: Convert cramfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (46 preceding siblings ...)
  2019-03-27 23:46 ` [RFC PATCH 47/68] vfs: Convert romfs to use the new mount API David Howells
@ 2019-03-27 23:46 ` David Howells
  2019-04-01 15:25   ` Nicolas Pitre
  2019-03-27 23:46 ` [RFC PATCH 49/68] vfs: Convert jffs2 " David Howells
                   ` (19 subsequent siblings)
  67 siblings, 1 reply; 87+ messages in thread
From: David Howells @ 2019-03-27 23:46 UTC (permalink / raw)
  To: viro
  Cc: Nicolas Pitre, linux-mtd, linux-block, linux-fsdevel,
	linux-kernel, dhowells

Convert the cramfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Nicolas Pitre <nico@linaro.org>
cc: linux-mtd@lists.infradead.org
cc: linux-block@vger.kernel.org
---

 fs/cramfs/inode.c |   69 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 39 insertions(+), 30 deletions(-)

diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 9352487bd0fc..2ee89a353d64 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -24,6 +24,7 @@
 #include <linux/blkdev.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/super.h>
+#include <linux/fs_context.h>
 #include <linux/slab.h>
 #include <linux/vfs.h>
 #include <linux/mutex.h>
@@ -506,18 +507,19 @@ static void cramfs_kill_sb(struct super_block *sb)
 	kfree(sbi);
 }
 
-static int cramfs_remount(struct super_block *sb, int *flags, char *data)
+static int cramfs_reconfigure(struct fs_context *fc)
 {
-	sync_filesystem(sb);
-	*flags |= SB_RDONLY;
+	sync_filesystem(fc->root->d_sb);
+	fc->sb_flags |= SB_RDONLY;
 	return 0;
 }
 
-static int cramfs_read_super(struct super_block *sb,
-			     struct cramfs_super *super, int silent)
+static int cramfs_read_super(struct super_block *sb, struct fs_context *fc,
+			     struct cramfs_super *super)
 {
 	struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
 	unsigned long root_offset;
+	bool silent = fc->sb_flags & SB_SILENT;
 
 	/* We don't know the real size yet */
 	sbi->size = PAGE_SIZE;
@@ -532,7 +534,7 @@ static int cramfs_read_super(struct super_block *sb,
 		/* check for wrong endianness */
 		if (super->magic == CRAMFS_MAGIC_WEND) {
 			if (!silent)
-				pr_err("wrong endianness\n");
+				errorf(fc, "cramfs: wrong endianness");
 			return -EINVAL;
 		}
 
@@ -544,22 +546,22 @@ static int cramfs_read_super(struct super_block *sb,
 		mutex_unlock(&read_mutex);
 		if (super->magic != CRAMFS_MAGIC) {
 			if (super->magic == CRAMFS_MAGIC_WEND && !silent)
-				pr_err("wrong endianness\n");
+				errorf(fc, "cramfs: wrong endianness");
 			else if (!silent)
-				pr_err("wrong magic\n");
+				errorf(fc, "cramfs: wrong magic");
 			return -EINVAL;
 		}
 	}
 
 	/* get feature flags first */
 	if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) {
-		pr_err("unsupported filesystem features\n");
+		errorf(fc, "cramfs: unsupported filesystem features");
 		return -EINVAL;
 	}
 
 	/* Check that the root inode is in a sane state */
 	if (!S_ISDIR(super->root.mode)) {
-		pr_err("root is not a directory\n");
+		errorf(fc, "cramfs: root is not a directory");
 		return -EINVAL;
 	}
 	/* correct strange, hard-coded permissions of mkcramfs */
@@ -578,12 +580,12 @@ static int cramfs_read_super(struct super_block *sb,
 	sbi->magic = super->magic;
 	sbi->flags = super->flags;
 	if (root_offset == 0)
-		pr_info("empty filesystem");
+		infof(fc, "cramfs: empty filesystem");
 	else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
 		 ((root_offset != sizeof(struct cramfs_super)) &&
 		  (root_offset != 512 + sizeof(struct cramfs_super))))
 	{
-		pr_err("bad root offset %lu\n", root_offset);
+		errorf(fc, "cramfs: bad root offset %lu", root_offset);
 		return -EINVAL;
 	}
 
@@ -607,8 +609,7 @@ static int cramfs_finalize_super(struct super_block *sb,
 	return 0;
 }
 
-static int cramfs_blkdev_fill_super(struct super_block *sb, void *data,
-				    int silent)
+static int cramfs_blkdev_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct cramfs_sb_info *sbi;
 	struct cramfs_super super;
@@ -623,14 +624,13 @@ static int cramfs_blkdev_fill_super(struct super_block *sb, void *data,
 	for (i = 0; i < READ_BUFFERS; i++)
 		buffer_blocknr[i] = -1;
 
-	err = cramfs_read_super(sb, &super, silent);
+	err = cramfs_read_super(sb, fc, &super);
 	if (err)
 		return err;
 	return cramfs_finalize_super(sb, &super.root);
 }
 
-static int cramfs_mtd_fill_super(struct super_block *sb, void *data,
-				 int silent)
+static int cramfs_mtd_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct cramfs_sb_info *sbi;
 	struct cramfs_super super;
@@ -652,7 +652,7 @@ static int cramfs_mtd_fill_super(struct super_block *sb, void *data,
 
 	pr_info("checking physical address %pap for linear cramfs image\n",
 		&sbi->linear_phys_addr);
-	err = cramfs_read_super(sb, &super, silent);
+	err = cramfs_read_super(sb, fc, &super);
 	if (err)
 		return err;
 
@@ -947,32 +947,41 @@ static const struct inode_operations cramfs_dir_inode_operations = {
 };
 
 static const struct super_operations cramfs_ops = {
-	.remount_fs	= cramfs_remount,
 	.statfs		= cramfs_statfs,
 };
 
-static struct dentry *cramfs_mount(struct file_system_type *fs_type, int flags,
-				   const char *dev_name, void *data)
+static int cramfs_get_tree(struct fs_context *fc)
 {
-	struct dentry *ret = ERR_PTR(-ENOPROTOOPT);
+	int ret = -ENOPROTOOPT;
 
 	if (IS_ENABLED(CONFIG_CRAMFS_MTD)) {
-		ret = mount_mtd(fs_type, flags, dev_name, data,
-				cramfs_mtd_fill_super);
-		if (!IS_ERR(ret))
+		ret = vfs_get_mtd_super(fc, cramfs_mtd_fill_super);
+		if (ret < 0)
 			return ret;
 	}
-	if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) {
-		ret = mount_bdev(fs_type, flags, dev_name, data,
-				 cramfs_blkdev_fill_super);
-	}
+	if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV))
+		ret = vfs_get_block_super(fc, cramfs_blkdev_fill_super);
 	return ret;
 }
 
+static const struct fs_context_operations cramfs_context_ops = {
+	.get_tree	= cramfs_get_tree,
+	.reconfigure	= cramfs_reconfigure,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+static int cramfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &cramfs_context_ops;
+	return 0;
+}
+
 static struct file_system_type cramfs_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "cramfs",
-	.mount		= cramfs_mount,
+	.init_fs_context = cramfs_init_fs_context,
 	.kill_sb	= cramfs_kill_sb,
 	.fs_flags	= FS_REQUIRES_DEV,
 };


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 49/68] vfs: Convert jffs2 to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (47 preceding siblings ...)
  2019-03-27 23:46 ` [RFC PATCH 48/68] vfs: Convert cramfs " David Howells
@ 2019-03-27 23:46 ` David Howells
  2019-03-27 23:46 ` [RFC PATCH 50/68] mtd: Kill mount_mtd() David Howells
                   ` (18 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:46 UTC (permalink / raw)
  To: viro; +Cc: David Woodhouse, linux-mtd, linux-fsdevel, linux-kernel, dhowells

Convert the jffs2 filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: David Woodhouse <dwmw2@infradead.org>
cc: linux-mtd@lists.infradead.org
---

 fs/jffs2/fs.c       |   21 +++---
 fs/jffs2/os-linux.h |    4 +
 fs/jffs2/super.c    |  172 +++++++++++++++++++++++++--------------------------
 3 files changed, 96 insertions(+), 101 deletions(-)

diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index eab04eca95a3..d79734bd7650 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -17,6 +17,7 @@
 #include <linux/sched.h>
 #include <linux/cred.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/list.h>
 #include <linux/mtd/mtd.h>
 #include <linux/pagemap.h>
@@ -184,7 +185,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
 	if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) {
 		truncate_setsize(inode, iattr->ia_size);
 		inode->i_blocks = (inode->i_size + 511) >> 9;
-	}	
+	}
 
 	return 0;
 }
@@ -390,7 +391,7 @@ void jffs2_dirty_inode(struct inode *inode, int flags)
 	jffs2_do_setattr(inode, &iattr);
 }
 
-int jffs2_do_remount_fs(struct super_block *sb, int *flags, char *data)
+int jffs2_do_remount_fs(struct super_block *sb, struct fs_context *fc)
 {
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 
@@ -408,10 +409,10 @@ int jffs2_do_remount_fs(struct super_block *sb, int *flags, char *data)
 		mutex_unlock(&c->alloc_sem);
 	}
 
-	if (!(*flags & SB_RDONLY))
+	if (!(fc->sb_flags & SB_RDONLY))
 		jffs2_start_garbage_collect_thread(c);
 
-	*flags |= SB_NOATIME;
+	fc->sb_flags |= SB_NOATIME;
 	return 0;
 }
 
@@ -508,7 +509,7 @@ static int calculate_inocache_hashsize(uint32_t flash_size)
 	return hashsize;
 }
 
-int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
+int jffs2_do_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct jffs2_sb_info *c;
 	struct inode *root_i;
@@ -523,11 +524,11 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
 
 #ifndef CONFIG_JFFS2_FS_WRITEBUFFER
 	if (c->mtd->type == MTD_NANDFLASH) {
-		pr_err("Cannot operate on NAND flash unless jffs2 NAND support is compiled in\n");
+		errorf(fc, "Cannot operate on NAND flash unless jffs2 NAND support is compiled in");
 		return -EINVAL;
 	}
 	if (c->mtd->type == MTD_DATAFLASH) {
-		pr_err("Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in\n");
+		errorf(fc, "Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in");
 		return -EINVAL;
 	}
 #endif
@@ -541,12 +542,12 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
 	 */
 	if ((c->sector_size * blocks) != c->flash_size) {
 		c->flash_size = c->sector_size * blocks;
-		pr_info("Flash size not aligned to erasesize, reducing to %dKiB\n",
-			c->flash_size / 1024);
+		infof(fc, "Flash size not aligned to erasesize, reducing to %dKiB",
+		      c->flash_size / 1024);
 	}
 
 	if (c->flash_size < 5*c->sector_size) {
-		pr_err("Too few erase blocks (%d)\n",
+		errorf(fc, "Too few erase blocks (%d)",
 		       c->flash_size / c->sector_size);
 		return -EINVAL;
 	}
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index a2dbbb3f4c74..ac23ae5cdb54 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -172,8 +172,8 @@ void jffs2_dirty_inode(struct inode *inode, int flags);
 struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode,
 			       struct jffs2_raw_inode *ri);
 int jffs2_statfs (struct dentry *, struct kstatfs *);
-int jffs2_do_remount_fs(struct super_block *, int *, char *);
-int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
+int jffs2_do_remount_fs(struct super_block *sb, struct fs_context *fc);
+int jffs2_do_fill_super(struct super_block *sb, struct fs_context *fc);
 void jffs2_gc_release_inode(struct jffs2_sb_info *c,
 			    struct jffs2_inode_info *f);
 struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index bb6ae387469f..69dcc4e0b243 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -19,7 +19,8 @@
 #include <linux/fs.h>
 #include <linux/err.h>
 #include <linux/mount.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/jffs2.h>
 #include <linux/pagemap.h>
 #include <linux/mtd/super.h>
@@ -160,96 +161,77 @@ static const struct export_operations jffs2_export_ops = {
 /*
  * JFFS2 mount options.
  *
+ * Opt_source: The source device
  * Opt_override_compr: override default compressor
  * Opt_rp_size: size of reserved pool in KiB
- * Opt_err: just end of array marker
  */
 enum {
+	Opt_source,
 	Opt_override_compr,
 	Opt_rp_size,
-	Opt_err,
 };
 
-static const match_table_t tokens = {
-	{Opt_override_compr, "compr=%s"},
-	{Opt_rp_size, "rp_size=%u"},
-	{Opt_err, NULL},
+static const struct fs_parameter_spec jffs2_param_specs[] = {
+	fsparam_string	("source",	Opt_source),
+	fsparam_enum	("compr",	Opt_override_compr),
+	fsparam_u32	("rp_size",	Opt_rp_size),
+	{}
 };
 
-static int jffs2_parse_options(struct jffs2_sb_info *c, char *data)
-{
-	substring_t args[MAX_OPT_ARGS];
-	char *p, *name;
-	unsigned int opt;
-
-	if (!data)
-		return 0;
-
-	while ((p = strsep(&data, ","))) {
-		int token;
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_override_compr:
-			name = match_strdup(&args[0]);
-
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "none"))
-				c->mount_opts.compr = JFFS2_COMPR_MODE_NONE;
+static const struct fs_parameter_enum jffs2_param_enums[] = {
+	{ Opt_override_compr,	"none",	JFFS2_COMPR_MODE_NONE },
 #ifdef CONFIG_JFFS2_LZO
-			else if (!strcmp(name, "lzo"))
-				c->mount_opts.compr = JFFS2_COMPR_MODE_FORCELZO;
+	{ Opt_override_compr,	"lzo",	JFFS2_COMPR_MODE_FORCELZO },
 #endif
 #ifdef CONFIG_JFFS2_ZLIB
-			else if (!strcmp(name, "zlib"))
-				c->mount_opts.compr =
-						JFFS2_COMPR_MODE_FORCEZLIB;
+	{ Opt_override_compr,	"zlib",	JFFS2_COMPR_MODE_FORCEZLIB },
 #endif
-			else {
-				pr_err("Error: unknown compressor \"%s\"\n",
-				       name);
-				kfree(name);
-				return -EINVAL;
-			}
-			kfree(name);
-			c->mount_opts.override_compr = true;
-			break;
-		case Opt_rp_size:
-			if (match_int(&args[0], &opt))
-				return -EINVAL;
-			opt *= 1024;
-			if (opt > c->mtd->size) {
-				pr_warn("Too large reserve pool specified, max "
-					"is %llu KB\n", c->mtd->size / 1024);
-				return -EINVAL;
-			}
-			c->mount_opts.rp_size = opt;
-			break;
-		default:
-			pr_err("Error: unrecognized mount option '%s' or missing value\n",
-			       p);
-			return -EINVAL;
-		}
+	{}
+};
+
+const struct fs_parameter_description jffs2_fs_parameters = {
+	.name		= "jffs2",
+	.specs		= jffs2_param_specs,
+	.enums		= jffs2_param_enums,
+};
+
+static int jffs2_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct fs_parse_result result;
+	struct jffs2_sb_info *c = fc->s_fs_info;
+	int opt;
+
+	opt = fs_parse(fc, &jffs2_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_override_compr:
+		c->mount_opts.compr = result.uint_32;
+		c->mount_opts.override_compr = true;
+		break;
+	case Opt_rp_size:
+		if (result.uint_32 > UINT_MAX / 1024)
+			return invalf(fc, "jffs2: rp_size unrepresentable");
+		opt = result.uint_32 * 1024;
+		if (opt > c->mtd->size)
+			return invalf(fc, "jffs2: Too large reserve pool specified, max is %llu KB",
+				      c->mtd->size / 1024);
+		c->mount_opts.rp_size = opt;
+		break;
+	default:
+		return -EINVAL;
 	}
 
 	return 0;
 }
 
-static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data)
+static int jffs2_reconfigure(struct fs_context *fc)
 {
-	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-	int err;
+	struct super_block *sb = fc->root->d_sb;
 
 	sync_filesystem(sb);
-	err = jffs2_parse_options(c, data);
-	if (err)
-		return -EINVAL;
-
-	return jffs2_do_remount_fs(sb, flags, data);
+	return jffs2_do_remount_fs(sb, fc);
 }
 
 static const struct super_operations jffs2_super_operations =
@@ -258,7 +240,6 @@ static const struct super_operations jffs2_super_operations =
 	.destroy_inode =jffs2_destroy_inode,
 	.put_super =	jffs2_put_super,
 	.statfs =	jffs2_statfs,
-	.remount_fs =	jffs2_remount_fs,
 	.evict_inode =	jffs2_evict_inode,
 	.dirty_inode =	jffs2_dirty_inode,
 	.show_options =	jffs2_show_options,
@@ -268,26 +249,16 @@ static const struct super_operations jffs2_super_operations =
 /*
  * fill in the superblock
  */
-static int jffs2_fill_super(struct super_block *sb, void *data, int silent)
+static int jffs2_fill_super(struct super_block *sb, struct fs_context *fc)
 {
-	struct jffs2_sb_info *c;
-	int ret;
+	struct jffs2_sb_info *c = sb->s_fs_info;
 
 	jffs2_dbg(1, "jffs2_get_sb_mtd():"
 		  " New superblock for device %d (\"%s\")\n",
 		  sb->s_mtd->index, sb->s_mtd->name);
 
-	c = kzalloc(sizeof(*c), GFP_KERNEL);
-	if (!c)
-		return -ENOMEM;
-
 	c->mtd = sb->s_mtd;
 	c->os_priv = sb;
-	sb->s_fs_info = c;
-
-	ret = jffs2_parse_options(c, data);
-	if (ret)
-		return -EINVAL;
 
 	/* Initialize JFFS2 superblock locks, the further initialization will
 	 * be done later */
@@ -305,15 +276,37 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent)
 #ifdef CONFIG_JFFS2_FS_POSIX_ACL
 	sb->s_flags |= SB_POSIXACL;
 #endif
-	ret = jffs2_do_fill_super(sb, data, silent);
-	return ret;
+	return jffs2_do_fill_super(sb, fc);
 }
 
-static struct dentry *jffs2_mount(struct file_system_type *fs_type,
-			int flags, const char *dev_name,
-			void *data)
+static int jffs2_get_tree(struct fs_context *fc)
 {
-	return mount_mtd(fs_type, flags, dev_name, data, jffs2_fill_super);
+	return vfs_get_mtd_super(fc, jffs2_fill_super);
+}
+
+static void jffs2_free_fc(struct fs_context *fc)
+{
+	kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations jffs2_context_ops = {
+	.free		= jffs2_free_fc,
+	.parse_param	= jffs2_parse_param,
+	.get_tree	= jffs2_get_tree,
+	.reconfigure	= jffs2_reconfigure,
+};
+
+static int jffs2_init_fs_context(struct fs_context *fc)
+{
+	struct jffs2_sb_info *ctx;
+
+	ctx = kzalloc(sizeof(struct jffs2_sb_info), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	fc->s_fs_info = ctx;
+	fc->ops = &jffs2_context_ops;
+	return 0;
 }
 
 static void jffs2_put_super (struct super_block *sb)
@@ -350,7 +343,8 @@ static void jffs2_kill_sb(struct super_block *sb)
 static struct file_system_type jffs2_fs_type = {
 	.owner =	THIS_MODULE,
 	.name =		"jffs2",
-	.mount =	jffs2_mount,
+	.init_fs_context = jffs2_init_fs_context,
+	.parameters =	&jffs2_fs_parameters,
 	.kill_sb =	jffs2_kill_sb,
 };
 MODULE_ALIAS_FS("jffs2");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 50/68] mtd: Kill mount_mtd()
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (48 preceding siblings ...)
  2019-03-27 23:46 ` [RFC PATCH 49/68] vfs: Convert jffs2 " David Howells
@ 2019-03-27 23:46 ` David Howells
  2019-03-27 23:47 ` [RFC PATCH 51/68] vfs: Convert squashfs to use the new mount API David Howells
                   ` (17 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:46 UTC (permalink / raw)
  To: viro
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, linux-mtd, linux-fsdevel, linux-kernel,
	dhowells

Kill mount_mtd() as it has now been replaced by vfs_get_mtd_super() in the
new mount API and nothing now uses it.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: David Woodhouse <dwmw2@infradead.org>
cc: Brian Norris <computersforpeace@gmail.com>
cc: Boris Brezillon <bbrezillon@kernel.org>
cc: Marek Vasut <marek.vasut@gmail.com>
cc: Richard Weinberger <richard@nod.at>
cc: linux-mtd@lists.infradead.org
---

 drivers/mtd/mtdsuper.c    |  189 ---------------------------------------------
 include/linux/mtd/super.h |    3 -
 2 files changed, 192 deletions(-)

diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c
index cf165f7c8632..428beb766166 100644
--- a/drivers/mtd/mtdsuper.c
+++ b/drivers/mtd/mtdsuper.c
@@ -210,195 +210,6 @@ int vfs_get_mtd_super(struct fs_context *fc,
 }
 EXPORT_SYMBOL_GPL(vfs_get_mtd_super);
 
-/*
- * compare superblocks to see if they're equivalent
- * - they are if the underlying MTD device is the same
- */
-static int get_sb_mtd_compare(struct super_block *sb, void *_mtd)
-{
-	struct mtd_info *mtd = _mtd;
-
-	if (sb->s_mtd == mtd) {
-		pr_debug("MTDSB: Match on device %d (\"%s\")\n",
-		      mtd->index, mtd->name);
-		return 1;
-	}
-
-	pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
-	      sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
-	return 0;
-}
-
-/*
- * mark the superblock by the MTD device it is using
- * - set the device number to be the correct MTD block device for pesuperstence
- *   of NFS exports
- */
-static int get_sb_mtd_set(struct super_block *sb, void *_mtd)
-{
-	struct mtd_info *mtd = _mtd;
-
-	sb->s_mtd = mtd;
-	sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
-	sb->s_bdi = bdi_get(mtd_bdi);
-
-	return 0;
-}
-
-/*
- * get a superblock on an MTD-backed filesystem
- */
-static struct dentry *mount_mtd_aux(struct file_system_type *fs_type, int flags,
-			  const char *dev_name, void *data,
-			  struct mtd_info *mtd,
-			  int (*fill_super)(struct super_block *, void *, int))
-{
-	struct super_block *sb;
-	int ret;
-
-	sb = sget(fs_type, get_sb_mtd_compare, get_sb_mtd_set, flags, mtd);
-	if (IS_ERR(sb))
-		goto out_error;
-
-	if (sb->s_root)
-		goto already_mounted;
-
-	/* fresh new superblock */
-	pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
-	      mtd->index, mtd->name);
-
-	ret = fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
-	if (ret < 0) {
-		deactivate_locked_super(sb);
-		return ERR_PTR(ret);
-	}
-
-	/* go */
-	sb->s_flags |= SB_ACTIVE;
-	return dget(sb->s_root);
-
-	/* new mountpoint for an already mounted superblock */
-already_mounted:
-	pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n",
-	      mtd->index, mtd->name);
-	put_mtd_device(mtd);
-	return dget(sb->s_root);
-
-out_error:
-	put_mtd_device(mtd);
-	return ERR_CAST(sb);
-}
-
-/*
- * get a superblock on an MTD-backed filesystem by MTD device number
- */
-static struct dentry *mount_mtd_nr(struct file_system_type *fs_type, int flags,
-			 const char *dev_name, void *data, int mtdnr,
-			 int (*fill_super)(struct super_block *, void *, int))
-{
-	struct mtd_info *mtd;
-
-	mtd = get_mtd_device(NULL, mtdnr);
-	if (IS_ERR(mtd)) {
-		pr_debug("MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
-		return ERR_CAST(mtd);
-	}
-
-	return mount_mtd_aux(fs_type, flags, dev_name, data, mtd, fill_super);
-}
-
-/*
- * set up an MTD-based superblock
- */
-struct dentry *mount_mtd(struct file_system_type *fs_type, int flags,
-	       const char *dev_name, void *data,
-	       int (*fill_super)(struct super_block *, void *, int))
-{
-#ifdef CONFIG_BLOCK
-	struct block_device *bdev;
-	int ret, major;
-#endif
-	int mtdnr;
-
-	if (!dev_name)
-		return ERR_PTR(-EINVAL);
-
-	pr_debug("MTDSB: dev_name \"%s\"\n", dev_name);
-
-	/* the preferred way of mounting in future; especially when
-	 * CONFIG_BLOCK=n - we specify the underlying MTD device by number or
-	 * by name, so that we don't require block device support to be present
-	 * in the kernel. */
-	if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
-		if (dev_name[3] == ':') {
-			struct mtd_info *mtd;
-
-			/* mount by MTD device name */
-			pr_debug("MTDSB: mtd:%%s, name \"%s\"\n",
-			      dev_name + 4);
-
-			mtd = get_mtd_device_nm(dev_name + 4);
-			if (!IS_ERR(mtd))
-				return mount_mtd_aux(
-					fs_type, flags,
-					dev_name, data, mtd,
-					fill_super);
-
-			printk(KERN_NOTICE "MTD:"
-			       " MTD device with name \"%s\" not found.\n",
-			       dev_name + 4);
-
-		} else if (isdigit(dev_name[3])) {
-			/* mount by MTD device number name */
-			char *endptr;
-
-			mtdnr = simple_strtoul(dev_name + 3, &endptr, 0);
-			if (!*endptr) {
-				/* It was a valid number */
-				pr_debug("MTDSB: mtd%%d, mtdnr %d\n",
-				      mtdnr);
-				return mount_mtd_nr(fs_type, flags,
-						     dev_name, data,
-						     mtdnr, fill_super);
-			}
-		}
-	}
-
-#ifdef CONFIG_BLOCK
-	/* try the old way - the hack where we allowed users to mount
-	 * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
-	 */
-	bdev = lookup_bdev(dev_name);
-	if (IS_ERR(bdev)) {
-		ret = PTR_ERR(bdev);
-		pr_debug("MTDSB: lookup_bdev() returned %d\n", ret);
-		return ERR_PTR(ret);
-	}
-	pr_debug("MTDSB: lookup_bdev() returned 0\n");
-
-	ret = -EINVAL;
-
-	major = MAJOR(bdev->bd_dev);
-	mtdnr = MINOR(bdev->bd_dev);
-	bdput(bdev);
-
-	if (major != MTD_BLOCK_MAJOR)
-		goto not_an_MTD_device;
-
-	return mount_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super);
-
-not_an_MTD_device:
-#endif /* CONFIG_BLOCK */
-
-	if (!(flags & SB_SILENT))
-		printk(KERN_NOTICE
-		       "MTD: Attempt to mount non-MTD device \"%s\"\n",
-		       dev_name);
-	return ERR_PTR(-EINVAL);
-}
-
-EXPORT_SYMBOL_GPL(mount_mtd);
-
 /*
  * destroy an MTD-based superblock
  */
diff --git a/include/linux/mtd/super.h b/include/linux/mtd/super.h
index 7ebd69e5c640..c8bfaf0b8fe7 100644
--- a/include/linux/mtd/super.h
+++ b/include/linux/mtd/super.h
@@ -21,9 +21,6 @@
 extern int vfs_get_mtd_super(struct fs_context *fc,
 			     int (*fill_super)(struct super_block *sb,
 					       struct fs_context *fc));
-extern struct dentry *mount_mtd(struct file_system_type *fs_type, int flags,
-		      const char *dev_name, void *data,
-		      int (*fill_super)(struct super_block *, void *, int));
 extern void kill_mtd_super(struct super_block *sb);
 
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 51/68] vfs: Convert squashfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (49 preceding siblings ...)
  2019-03-27 23:46 ` [RFC PATCH 50/68] mtd: Kill mount_mtd() David Howells
@ 2019-03-27 23:47 ` David Howells
  2019-03-27 23:47 ` [RFC PATCH 52/68] vfs: Convert ceph " David Howells
                   ` (16 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:47 UTC (permalink / raw)
  To: viro
  Cc: Phillip Lougher, squashfs-devel, linux-fsdevel, linux-kernel, dhowells

Convert the squashfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Phillip Lougher <phillip@squashfs.org.uk>
cc: squashfs-devel@lists.sourceforge.net
---

 fs/squashfs/super.c |  100 ++++++++++++++++++++++++++++-----------------------
 1 file changed, 55 insertions(+), 45 deletions(-)

diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 40e657386fa5..672011725eb2 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -30,6 +30,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/fs.h>
+#include <linux/fs_context.h>
 #include <linux/vfs.h>
 #include <linux/slab.h>
 #include <linux/mutex.h>
@@ -49,26 +50,27 @@
 static struct file_system_type squashfs_fs_type;
 static const struct super_operations squashfs_super_ops;
 
-static const struct squashfs_decompressor *supported_squashfs_filesystem(short
-	major, short minor, short id)
+static const struct squashfs_decompressor *supported_squashfs_filesystem(
+	struct fs_context *fc,
+	short major, short minor, short id)
 {
 	const struct squashfs_decompressor *decompressor;
 
 	if (major < SQUASHFS_MAJOR) {
-		ERROR("Major/Minor mismatch, older Squashfs %d.%d "
-			"filesystems are unsupported\n", major, minor);
+		errorf(fc, "Major/Minor mismatch, older Squashfs %d.%d "
+		       "filesystems are unsupported", major, minor);
 		return NULL;
 	} else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) {
-		ERROR("Major/Minor mismatch, trying to mount newer "
-			"%d.%d filesystem\n", major, minor);
-		ERROR("Please update your kernel\n");
+		errorf(fc, "Major/Minor mismatch, trying to mount newer "
+		       "%d.%d filesystem", major, minor);
+		errorf(fc, "Please update your kernel");
 		return NULL;
 	}
 
 	decompressor = squashfs_lookup_decompressor(id);
 	if (!decompressor->supported) {
-		ERROR("Filesystem uses \"%s\" compression. This is not "
-			"supported\n", decompressor->name);
+		errorf(fc, "Filesystem uses \"%s\" compression. This is not supported",
+		       decompressor->name);
 		return NULL;
 	}
 
@@ -76,7 +78,7 @@ static const struct squashfs_decompressor *supported_squashfs_filesystem(short
 }
 
 
-static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
+static int squashfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct squashfs_sb_info *msblk;
 	struct squashfs_super_block *sblk = NULL;
@@ -111,7 +113,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	sblk = squashfs_read_table(sb, SQUASHFS_START, sizeof(*sblk));
 
 	if (IS_ERR(sblk)) {
-		ERROR("unable to read squashfs_super_block\n");
+		errorf(fc, "unable to read squashfs_super_block");
 		err = PTR_ERR(sblk);
 		sblk = NULL;
 		goto failed_mount;
@@ -122,14 +124,15 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	/* Check it is a SQUASHFS superblock */
 	sb->s_magic = le32_to_cpu(sblk->s_magic);
 	if (sb->s_magic != SQUASHFS_MAGIC) {
-		if (!silent)
-			ERROR("Can't find a SQUASHFS superblock on %pg\n",
-						sb->s_bdev);
+		if (!(fc->sb_flags & SB_SILENT))
+			errorf(fc, "Can't find a SQUASHFS superblock on %pg",
+			       sb->s_bdev);
 		goto failed_mount;
 	}
 
 	/* Check the MAJOR & MINOR versions and lookup compression type */
 	msblk->decompressor = supported_squashfs_filesystem(
+			fc,
 			le16_to_cpu(sblk->s_major),
 			le16_to_cpu(sblk->s_minor),
 			le16_to_cpu(sblk->compression));
@@ -146,15 +149,15 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	/* Check block size for sanity */
 	msblk->block_size = le32_to_cpu(sblk->block_size);
 	if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE)
-		goto failed_mount;
+		goto insanity;
 
 	/*
 	 * Check the system page size is not larger than the filesystem
 	 * block size (by default 128K).  This is currently not supported.
 	 */
 	if (PAGE_SIZE > msblk->block_size) {
-		ERROR("Page size > filesystem block size (%d).  This is "
-			"currently not supported!\n", msblk->block_size);
+		errorf(fc, "Page size > filesystem block size (%d).  This is "
+		       "currently not supported!", msblk->block_size);
 		goto failed_mount;
 	}
 
@@ -165,12 +168,12 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 
 	/* Check that block_size and block_log match */
 	if (msblk->block_size != (1 << msblk->block_log))
-		goto failed_mount;
+		goto insanity;
 
 	/* Check the root inode for sanity */
 	root_inode = le64_to_cpu(sblk->root_inode);
 	if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE)
-		goto failed_mount;
+		goto insanity;
 
 	msblk->inode_table = le64_to_cpu(sblk->inode_table_start);
 	msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
@@ -210,7 +213,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	msblk->read_page = squashfs_cache_init("data",
 		squashfs_max_decompressors(), msblk->block_size);
 	if (msblk->read_page == NULL) {
-		ERROR("Failed to allocate read_page block\n");
+		errorf(fc, "Failed to allocate read_page block");
 		goto failed_mount;
 	}
 
@@ -218,7 +221,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	if (IS_ERR(msblk->stream)) {
 		err = PTR_ERR(msblk->stream);
 		msblk->stream = NULL;
-		goto failed_mount;
+		goto insanity;
 	}
 
 	/* Handle xattrs */
@@ -233,7 +236,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	msblk->xattr_id_table = squashfs_read_xattr_id_table(sb,
 		xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids);
 	if (IS_ERR(msblk->xattr_id_table)) {
-		ERROR("unable to read xattr id index table\n");
+		errorf(fc, "unable to read xattr id index table");
 		err = PTR_ERR(msblk->xattr_id_table);
 		msblk->xattr_id_table = NULL;
 		if (err != -ENOTSUPP)
@@ -247,7 +250,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 		le64_to_cpu(sblk->id_table_start), next_table,
 		le16_to_cpu(sblk->no_ids));
 	if (IS_ERR(msblk->id_table)) {
-		ERROR("unable to read id index table\n");
+		errorf(fc, "unable to read id index table");
 		err = PTR_ERR(msblk->id_table);
 		msblk->id_table = NULL;
 		goto failed_mount;
@@ -263,7 +266,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
 		lookup_table_start, next_table, msblk->inodes);
 	if (IS_ERR(msblk->inode_lookup_table)) {
-		ERROR("unable to read inode lookup table\n");
+		errorf(fc, "unable to read inode lookup table");
 		err = PTR_ERR(msblk->inode_lookup_table);
 		msblk->inode_lookup_table = NULL;
 		goto failed_mount;
@@ -288,7 +291,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	msblk->fragment_index = squashfs_read_fragment_index_table(sb,
 		le64_to_cpu(sblk->fragment_table_start), next_table, fragments);
 	if (IS_ERR(msblk->fragment_index)) {
-		ERROR("unable to read fragment index table\n");
+		errorf(fc, "unable to read fragment index table");
 		err = PTR_ERR(msblk->fragment_index);
 		msblk->fragment_index = NULL;
 		goto failed_mount;
@@ -299,13 +302,13 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	/* Sanity check directory_table */
 	if (msblk->directory_table > next_table) {
 		err = -EINVAL;
-		goto failed_mount;
+		goto insanity;
 	}
 
 	/* Sanity check inode_table */
 	if (msblk->inode_table >= msblk->directory_table) {
 		err = -EINVAL;
-		goto failed_mount;
+		goto insanity;
 	}
 
 	/* allocate root */
@@ -334,6 +337,8 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	kfree(sblk);
 	return 0;
 
+insanity:
+	errorf(fc, "squashfs image failed sanity check");
 failed_mount:
 	squashfs_cache_delete(msblk->block_cache);
 	squashfs_cache_delete(msblk->fragment_cache);
@@ -349,6 +354,28 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	return err;
 }
 
+static int squashfs_get_tree(struct fs_context *fc)
+{
+	return vfs_get_block_super(fc, squashfs_fill_super);
+}
+
+static int squashfs_reconfigure(struct fs_context *fc)
+{
+	sync_filesystem(fc->root->d_sb);
+	fc->sb_flags |= SB_RDONLY;
+	return 0;
+}
+
+static const struct fs_context_operations squashfs_context_ops = {
+	.get_tree	= squashfs_get_tree,
+	.reconfigure	= squashfs_reconfigure,
+};
+
+static int squashfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &squashfs_context_ops;
+	return 0;
+}
 
 static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
@@ -371,14 +398,6 @@ static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 }
 
 
-static int squashfs_remount(struct super_block *sb, int *flags, char *data)
-{
-	sync_filesystem(sb);
-	*flags |= SB_RDONLY;
-	return 0;
-}
-
-
 static void squashfs_put_super(struct super_block *sb)
 {
 	if (sb->s_fs_info) {
@@ -397,14 +416,6 @@ static void squashfs_put_super(struct super_block *sb)
 	}
 }
 
-
-static struct dentry *squashfs_mount(struct file_system_type *fs_type,
-				int flags, const char *dev_name, void *data)
-{
-	return mount_bdev(fs_type, flags, dev_name, data, squashfs_fill_super);
-}
-
-
 static struct kmem_cache *squashfs_inode_cachep;
 
 
@@ -488,7 +499,7 @@ static void squashfs_destroy_inode(struct inode *inode)
 static struct file_system_type squashfs_fs_type = {
 	.owner = THIS_MODULE,
 	.name = "squashfs",
-	.mount = squashfs_mount,
+	.init_fs_context = squashfs_init_fs_context,
 	.kill_sb = kill_block_super,
 	.fs_flags = FS_REQUIRES_DEV
 };
@@ -499,7 +510,6 @@ static const struct super_operations squashfs_super_ops = {
 	.destroy_inode = squashfs_destroy_inode,
 	.statfs = squashfs_statfs,
 	.put_super = squashfs_put_super,
-	.remount_fs = squashfs_remount
 };
 
 module_init(init_squashfs_fs);


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 52/68] vfs: Convert ceph to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (50 preceding siblings ...)
  2019-03-27 23:47 ` [RFC PATCH 51/68] vfs: Convert squashfs to use the new mount API David Howells
@ 2019-03-27 23:47 ` David Howells
  2019-04-27 12:27   ` Jeff Layton
  2019-03-27 23:47 ` [RFC PATCH 53/68] vfs: Convert functionfs " David Howells
                   ` (15 subsequent siblings)
  67 siblings, 1 reply; 87+ messages in thread
From: David Howells @ 2019-03-27 23:47 UTC (permalink / raw)
  To: viro
  Cc: Yan, Zheng, Ilya Dryomov, Sage Weil, ceph-devel, linux-fsdevel,
	linux-kernel, dhowells

Convert the ceph filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: "Yan, Zheng" <zyan@redhat.com>
cc: Ilya Dryomov <idryomov@gmail.com>
cc: Sage Weil <sage@redhat.com>
cc: ceph-devel@vger.kernel.org
---

 drivers/block/rbd.c             |  363 +++++++++++---------
 fs/ceph/cache.c                 |    9 -
 fs/ceph/cache.h                 |    5 
 fs/ceph/super.c                 |  697 +++++++++++++++++++--------------------
 fs/ceph/super.h                 |    1 
 fs/fs_context.c                 |    2 
 fs/fs_parser.c                  |    2 
 include/linux/ceph/ceph_debug.h |    1 
 include/linux/ceph/libceph.h    |   17 +
 net/ceph/ceph_common.c          |  410 ++++++++++-------------
 10 files changed, 729 insertions(+), 778 deletions(-)

diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 2210c1b9491b..1970af223d4b 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -34,7 +34,7 @@
 #include <linux/ceph/cls_lock_client.h>
 #include <linux/ceph/striper.h>
 #include <linux/ceph/decode.h>
-#include <linux/parser.h>
+#include <linux/fs_parser.h>
 #include <linux/bsearch.h>
 
 #include <linux/kernel.h>
@@ -747,40 +747,6 @@ static struct rbd_client *rbd_client_find(struct ceph_options *ceph_opts)
 /*
  * (Per device) rbd map options
  */
-enum {
-	Opt_queue_depth,
-	Opt_alloc_size,
-	Opt_lock_timeout,
-	Opt_last_int,
-	/* int args above */
-	Opt_pool_ns,
-	Opt_last_string,
-	/* string args above */
-	Opt_read_only,
-	Opt_read_write,
-	Opt_lock_on_read,
-	Opt_exclusive,
-	Opt_notrim,
-	Opt_err
-};
-
-static match_table_t rbd_opts_tokens = {
-	{Opt_queue_depth, "queue_depth=%d"},
-	{Opt_alloc_size, "alloc_size=%d"},
-	{Opt_lock_timeout, "lock_timeout=%d"},
-	/* int args above */
-	{Opt_pool_ns, "_pool_ns=%s"},
-	/* string args above */
-	{Opt_read_only, "read_only"},
-	{Opt_read_only, "ro"},		/* Alternate spelling */
-	{Opt_read_write, "read_write"},
-	{Opt_read_write, "rw"},		/* Alternate spelling */
-	{Opt_lock_on_read, "lock_on_read"},
-	{Opt_exclusive, "exclusive"},
-	{Opt_notrim, "notrim"},
-	{Opt_err, NULL}
-};
-
 struct rbd_options {
 	int	queue_depth;
 	int	alloc_size;
@@ -799,85 +765,98 @@ struct rbd_options {
 #define RBD_EXCLUSIVE_DEFAULT	false
 #define RBD_TRIM_DEFAULT	true
 
-struct parse_rbd_opts_ctx {
-	struct rbd_spec		*spec;
-	struct rbd_options	*opts;
+enum {
+	Opt_alloc_size,
+	Opt_exclusive,
+	Opt_lock_on_read,
+	Opt_lock_timeout,
+	Opt_notrim,
+	Opt_pool_ns,
+	Opt_queue_depth,
+	Opt_read_only,
+	Opt_read_write,
+};
+
+static const struct fs_parameter_spec rbd_param_specs[] = {
+	fsparam_u32	("alloc_size",			Opt_alloc_size),
+	fsparam_flag	("exclusive",			Opt_exclusive),
+	fsparam_flag	("lock_on_read",		Opt_lock_on_read),
+	fsparam_u32	("lock_timeout",		Opt_lock_timeout),
+	fsparam_flag	("notrim",			Opt_notrim),
+	fsparam_string	("_pool_ns",			Opt_pool_ns),
+	fsparam_u32	("queue_depth",			Opt_queue_depth),
+	fsparam_flag	("ro",				Opt_read_only),
+	fsparam_flag	("rw",				Opt_read_write),
+	{}
+};
+
+static const struct fs_parameter_description rbd_parameters = {
+	.name		= "rbd",
+	.specs		= rbd_param_specs,
 };
 
-static int parse_rbd_opts_token(char *c, void *private)
+static int rbd_parse_param(struct ceph_config_context *ctx, struct fs_parameter *param)
 {
-	struct parse_rbd_opts_ctx *pctx = private;
-	substring_t argstr[MAX_OPT_ARGS];
-	int token, intval, ret;
+	struct rbd_options *opts = ctx->rbd_opts;
+	struct rbd_spec *spec = ctx->rbd_spec;
+	struct fs_parse_result result;
+	int ret, opt;
 
-	token = match_token(c, rbd_opts_tokens, argstr);
-	if (token < Opt_last_int) {
-		ret = match_int(&argstr[0], &intval);
-		if (ret < 0) {
-			pr_err("bad option arg (not int) at '%s'\n", c);
-			return ret;
-		}
-		dout("got int token %d val %d\n", token, intval);
-	} else if (token > Opt_last_int && token < Opt_last_string) {
-		dout("got string token %d val %s\n", token, argstr[0].from);
-	} else {
-		dout("got token %d\n", token);
-	}
+	ret = ceph_parse_option(ctx->opt, NULL, param);
+	if (ret != -ENOPARAM)
+		return ret;
+
+	opt = fs_parse(NULL, &rbd_parameters, param, &result);
+	if (opt < 0)
+		return opt;
 
-	switch (token) {
+	switch (opt) {
 	case Opt_queue_depth:
-		if (intval < 1) {
-			pr_err("queue_depth out of range\n");
-			return -EINVAL;
-		}
-		pctx->opts->queue_depth = intval;
+		if (result.uint_32 < 1)
+			goto out_of_range;
+		opts->queue_depth = result.uint_32;
 		break;
 	case Opt_alloc_size:
-		if (intval < SECTOR_SIZE) {
-			pr_err("alloc_size out of range\n");
-			return -EINVAL;
-		}
-		if (!is_power_of_2(intval)) {
-			pr_err("alloc_size must be a power of 2\n");
-			return -EINVAL;
-		}
-		pctx->opts->alloc_size = intval;
+		if (result.uint_32 < SECTOR_SIZE)
+			goto out_of_range;
+		if (!is_power_of_2(result.uint_32))
+			return invalf(NULL, "alloc_size must be a power of 2\n");
+		opts->alloc_size = result.uint_32;
 		break;
 	case Opt_lock_timeout:
 		/* 0 is "wait forever" (i.e. infinite timeout) */
-		if (intval < 0 || intval > INT_MAX / 1000) {
-			pr_err("lock_timeout out of range\n");
-			return -EINVAL;
-		}
-		pctx->opts->lock_timeout = msecs_to_jiffies(intval * 1000);
+		if (result.uint_32 > INT_MAX / 1000)
+			goto out_of_range;
+		opts->lock_timeout = msecs_to_jiffies(result.uint_32 * 1000);
 		break;
 	case Opt_pool_ns:
-		kfree(pctx->spec->pool_ns);
-		pctx->spec->pool_ns = match_strdup(argstr);
-		if (!pctx->spec->pool_ns)
-			return -ENOMEM;
+		kfree(spec->pool_ns);
+		spec->pool_ns = param->string;
+		param->string = NULL;
 		break;
 	case Opt_read_only:
-		pctx->opts->read_only = true;
+		opts->read_only = true;
 		break;
 	case Opt_read_write:
-		pctx->opts->read_only = false;
+		opts->read_only = false;
 		break;
 	case Opt_lock_on_read:
-		pctx->opts->lock_on_read = true;
+		opts->lock_on_read = true;
 		break;
 	case Opt_exclusive:
-		pctx->opts->exclusive = true;
+		opts->exclusive = true;
 		break;
 	case Opt_notrim:
-		pctx->opts->trim = false;
+		opts->trim = false;
 		break;
 	default:
-		/* libceph prints "bad option" msg */
 		return -EINVAL;
 	}
 
 	return 0;
+
+out_of_range:
+	return invalf(NULL, "ceph: %s out of range", param->key);
 }
 
 static char* obj_op_name(enum obj_operation_type op_type)
@@ -5428,22 +5407,80 @@ static inline size_t next_token(const char **buf)
  *
  * Note: uses GFP_KERNEL for allocation.
  */
-static inline char *dup_token(const char **buf, size_t *lenp)
+static inline char *dup_token(const char **buf)
 {
 	char *dup;
 	size_t len;
 
 	len = next_token(buf);
-	dup = kmemdup(*buf, len + 1, GFP_KERNEL);
-	if (!dup)
-		return NULL;
-	*(dup + len) = '\0';
-	*buf += len;
+	dup = kmemdup_nul(*buf, len, GFP_KERNEL);
+	if (dup)
+		*buf += len;
+	return dup;
+}
 
-	if (lenp)
-		*lenp = len;
+/*
+ * Parse the parameter string.
+ */
+static int rbd_parse_monolithic(struct ceph_config_context *ctx, const char *data)
+{
+	const char *sep, *key, *eq, *value;
+	char key_buf[32];
+	size_t size, klen;
+	int ret = 0;
 
-	return dup;
+	struct fs_parameter param = {
+		.key	= key_buf,
+		.type	= fs_value_is_string,
+	};
+
+	do {
+		key = data;
+		sep = strchr(data, ',');
+		if (sep) {
+			data = sep + 1;
+			size = sep - key;
+		} else {
+			data = NULL;
+			size = strlen(key);
+		}
+
+		if (!size)
+			continue;
+
+		eq = memchr(key, '=', sep - key);
+		if (eq) {
+			klen = eq - key;
+			if (klen == 0)
+				return invalf(NULL, "Invalid option \"\"");
+			value = eq + 1;
+			param.size = sep - value;
+		} else {
+			klen = size;
+			value = NULL;
+			param.size = 0;
+		}
+
+		if (klen >= sizeof(key_buf))
+			return invalf(NULL, "Unknown option %*.*s",
+				      (int)klen, (int)klen, key);
+		memcpy(key_buf, key, klen);
+		key_buf[klen] = 0;
+
+		if (param.size > 0) {
+			param.string = kmemdup_nul(value, param.size,
+						   GFP_KERNEL);
+			if (!param.string)
+				return -ENOMEM;
+		}
+
+		ret = rbd_parse_param(ctx, &param);
+		kfree(param.string);
+		if (ret < 0)
+			break;
+	} while (data);
+
+	return ret;
 }
 
 /*
@@ -5487,18 +5524,11 @@ static inline char *dup_token(const char **buf, size_t *lenp)
  *      created.  The image head is used if no snapshot id is
  *      provided.  Snapshot mappings are always read-only.
  */
-static int rbd_add_parse_args(const char *buf,
-				struct ceph_options **ceph_opts,
-				struct rbd_options **opts,
-				struct rbd_spec **rbd_spec)
+static int rbd_add_parse_args(const char *buf, struct ceph_config_context *ctx)
 {
-	size_t len;
-	char *options;
-	const char *mon_addrs;
+	const char *options, *mon_addrs;
+	size_t len, options_len, mon_addrs_size;
 	char *snap_name;
-	size_t mon_addrs_size;
-	struct parse_rbd_opts_ctx pctx = { 0 };
-	struct ceph_options *copts;
 	int ret;
 
 	/* The first four tokens are required */
@@ -5509,36 +5539,35 @@ static int rbd_add_parse_args(const char *buf,
 		return -EINVAL;
 	}
 	mon_addrs = buf;
-	mon_addrs_size = len + 1;
+	mon_addrs_size = len;
 	buf += len;
 
-	ret = -EINVAL;
-	options = dup_token(&buf, NULL);
-	if (!options)
-		return -ENOMEM;
-	if (!*options) {
+	options_len = next_token(&buf);
+	if (options_len == 0) {
 		rbd_warn(NULL, "no options provided");
-		goto out_err;
+		return -EINVAL;
 	}
+	options = buf;
+	buf += len;
 
-	pctx.spec = rbd_spec_alloc();
-	if (!pctx.spec)
-		goto out_mem;
+	ctx->rbd_spec = rbd_spec_alloc();
+	if (!ctx->rbd_spec)
+		return -ENOMEM;
 
-	pctx.spec->pool_name = dup_token(&buf, NULL);
-	if (!pctx.spec->pool_name)
-		goto out_mem;
-	if (!*pctx.spec->pool_name) {
+	ctx->rbd_spec->pool_name = dup_token(&buf);
+	if (!ctx->rbd_spec->pool_name)
+		return -ENOMEM;
+	if (!*ctx->rbd_spec->pool_name) {
 		rbd_warn(NULL, "no pool name provided");
-		goto out_err;
+		return -EINVAL;
 	}
 
-	pctx.spec->image_name = dup_token(&buf, NULL);
-	if (!pctx.spec->image_name)
-		goto out_mem;
-	if (!*pctx.spec->image_name) {
+	ctx->rbd_spec->image_name = dup_token(&buf);
+	if (!ctx->rbd_spec->image_name)
+		return -ENOMEM;
+	if (!*ctx->rbd_spec->image_name) {
 		rbd_warn(NULL, "no image name provided");
-		goto out_err;
+		return -EINVAL;
 	}
 
 	/*
@@ -5550,51 +5579,37 @@ static int rbd_add_parse_args(const char *buf,
 		buf = RBD_SNAP_HEAD_NAME; /* No snapshot supplied */
 		len = sizeof (RBD_SNAP_HEAD_NAME) - 1;
 	} else if (len > RBD_MAX_SNAP_NAME_LEN) {
-		ret = -ENAMETOOLONG;
-		goto out_err;
+		return -ENAMETOOLONG;
 	}
-	snap_name = kmemdup(buf, len + 1, GFP_KERNEL);
+
+	snap_name = kmemdup_nul(buf, len, GFP_KERNEL);
 	if (!snap_name)
-		goto out_mem;
-	*(snap_name + len) = '\0';
-	pctx.spec->snap_name = snap_name;
+		return -ENOMEM;
+	ctx->rbd_spec->snap_name = snap_name;
 
 	/* Initialize all rbd options to the defaults */
 
-	pctx.opts = kzalloc(sizeof(*pctx.opts), GFP_KERNEL);
-	if (!pctx.opts)
-		goto out_mem;
-
-	pctx.opts->read_only = RBD_READ_ONLY_DEFAULT;
-	pctx.opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
-	pctx.opts->alloc_size = RBD_ALLOC_SIZE_DEFAULT;
-	pctx.opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
-	pctx.opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
-	pctx.opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
-	pctx.opts->trim = RBD_TRIM_DEFAULT;
-
-	copts = ceph_parse_options(options, mon_addrs,
-				   mon_addrs + mon_addrs_size - 1,
-				   parse_rbd_opts_token, &pctx);
-	if (IS_ERR(copts)) {
-		ret = PTR_ERR(copts);
-		goto out_err;
-	}
-	kfree(options);
+	ctx->rbd_opts = kzalloc(sizeof(*ctx->rbd_opts), GFP_KERNEL);
+	if (!ctx->rbd_opts)
+		return -ENOMEM;
 
-	*ceph_opts = copts;
-	*opts = pctx.opts;
-	*rbd_spec = pctx.spec;
+	ctx->rbd_opts->read_only = RBD_READ_ONLY_DEFAULT;
+	ctx->rbd_opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
+	ctx->rbd_opts->alloc_size = RBD_ALLOC_SIZE_DEFAULT;
+	ctx->rbd_opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
+	ctx->rbd_opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
+	ctx->rbd_opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
+	ctx->rbd_opts->trim = RBD_TRIM_DEFAULT;
 
-	return 0;
-out_mem:
-	ret = -ENOMEM;
-out_err:
-	kfree(pctx.opts);
-	rbd_spec_put(pctx.spec);
-	kfree(options);
+	ctx->opt = ceph_alloc_options();
+	if (!ctx->opt)
+		return -ENOMEM;
 
-	return ret;
+	ret = ceph_parse_server_specs(ctx->opt, NULL, mon_addrs, mon_addrs_size);
+	if (ret < 0)
+		return ret;
+
+	return rbd_parse_monolithic(ctx, options);
 }
 
 static void rbd_dev_image_unlock(struct rbd_device *rbd_dev)
@@ -6001,10 +6016,8 @@ static ssize_t do_rbd_add(struct bus_type *bus,
 			  const char *buf,
 			  size_t count)
 {
+	struct ceph_config_context ctx = {};
 	struct rbd_device *rbd_dev = NULL;
-	struct ceph_options *ceph_opts = NULL;
-	struct rbd_options *rbd_opts = NULL;
-	struct rbd_spec *spec = NULL;
 	struct rbd_client *rbdc;
 	int rc;
 
@@ -6012,33 +6025,34 @@ static ssize_t do_rbd_add(struct bus_type *bus,
 		return -ENODEV;
 
 	/* parse add command */
-	rc = rbd_add_parse_args(buf, &ceph_opts, &rbd_opts, &spec);
+	rc = rbd_add_parse_args(buf, &ctx);
 	if (rc < 0)
 		goto out;
 
-	rbdc = rbd_get_client(ceph_opts);
+	rbdc = rbd_get_client(ctx.opt);
 	if (IS_ERR(rbdc)) {
 		rc = PTR_ERR(rbdc);
 		goto err_out_args;
 	}
 
 	/* pick the pool */
-	rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, spec->pool_name);
+	rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap,
+				    ctx.rbd_spec->pool_name);
 	if (rc < 0) {
 		if (rc == -ENOENT)
-			pr_info("pool %s does not exist\n", spec->pool_name);
+			pr_info("pool %s does not exist\n", ctx.rbd_spec->pool_name);
 		goto err_out_client;
 	}
-	spec->pool_id = (u64)rc;
+	ctx.rbd_spec->pool_id = (u64)rc;
 
-	rbd_dev = rbd_dev_create(rbdc, spec, rbd_opts);
+	rbd_dev = rbd_dev_create(rbdc, ctx.rbd_spec, ctx.rbd_opts);
 	if (!rbd_dev) {
 		rc = -ENOMEM;
 		goto err_out_client;
 	}
 	rbdc = NULL;		/* rbd_dev now owns this */
-	spec = NULL;		/* rbd_dev now owns this */
-	rbd_opts = NULL;	/* rbd_dev now owns this */
+	ctx.rbd_spec = NULL;	/* rbd_dev now owns this */
+	ctx.rbd_opts = NULL;	/* rbd_dev now owns this */
 
 	rbd_dev->config_info = kstrdup(buf, GFP_KERNEL);
 	if (!rbd_dev->config_info) {
@@ -6106,8 +6120,9 @@ static ssize_t do_rbd_add(struct bus_type *bus,
 err_out_client:
 	rbd_put_client(rbdc);
 err_out_args:
-	rbd_spec_put(spec);
-	kfree(rbd_opts);
+	rbd_spec_put(ctx.rbd_spec);
+	kfree(ctx.rbd_opts);
+	ceph_destroy_options(ctx.opt);
 	goto out;
 }
 
diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c
index 1bf3502bdd6f..890d914ff5d5 100644
--- a/fs/ceph/cache.c
+++ b/fs/ceph/cache.c
@@ -21,6 +21,7 @@
  *
  */
 
+#include <linux/fs_context.h>
 #include "super.h"
 #include "cache.h"
 
@@ -62,7 +63,7 @@ void ceph_fscache_unregister(void)
 	fscache_unregister_netfs(&ceph_cache_netfs);
 }
 
-int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
+int ceph_fscache_register_fs(struct fs_context *fc, struct ceph_fs_client* fsc)
 {
 	const struct ceph_fsid *fsid = &fsc->client->fsid;
 	const char *fscache_uniq = fsc->mount_options->fscache_uniq;
@@ -79,8 +80,8 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
 		if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len))
 			continue;
 
-		pr_err("fscache cookie already registered for fsid %pU\n", fsid);
-		pr_err("  use fsc=%%s mount option to specify a uniquifier\n");
+		errorf(fc, "fscache cookie already registered for fsid %pU\n", fsid);
+		errorf(fc, "  use fsc=%%s mount option to specify a uniquifier\n");
 		err = -EBUSY;
 		goto out_unlock;
 	}
@@ -108,7 +109,7 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
 		list_add_tail(&ent->list, &ceph_fscache_list);
 	} else {
 		kfree(ent);
-		pr_err("unable to register fscache cookie for fsid %pU\n",
+		errorf(fc, "unable to register fscache cookie for fsid %pU\n",
 		       fsid);
 		/* all other fs ignore this error */
 	}
diff --git a/fs/ceph/cache.h b/fs/ceph/cache.h
index 7e72c7594f0c..0df0eb9e7c33 100644
--- a/fs/ceph/cache.h
+++ b/fs/ceph/cache.h
@@ -31,7 +31,7 @@ extern struct fscache_netfs ceph_cache_netfs;
 int ceph_fscache_register(void);
 void ceph_fscache_unregister(void);
 
-int ceph_fscache_register_fs(struct ceph_fs_client* fsc);
+int ceph_fscache_register_fs(struct fs_context *fc, struct ceph_fs_client* fsc);
 void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc);
 
 void ceph_fscache_register_inode_cookie(struct inode *inode);
@@ -103,7 +103,8 @@ static inline void ceph_fscache_unregister(void)
 {
 }
 
-static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
+static inline int ceph_fscache_register_fs(struct fs_context *fc,
+					   struct ceph_fs_client *fsc)
 {
 	return 0;
 }
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 6d5bb2f74612..edfe9cf08fe5 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -8,7 +8,8 @@
 #include <linux/in6.h>
 #include <linux/module.h>
 #include <linux/mount.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
@@ -128,272 +129,290 @@ static int ceph_sync_fs(struct super_block *sb, int wait)
  * mount options
  */
 enum {
-	Opt_wsize,
-	Opt_rsize,
-	Opt_rasize,
-	Opt_caps_wanted_delay_min,
-	Opt_caps_wanted_delay_max,
+	Opt_acl,
+	Opt_asyncreaddir,
 	Opt_caps_max,
-	Opt_readdir_max_entries,
-	Opt_readdir_max_bytes,
+	Opt_caps_wanted_delay_max,
+	Opt_caps_wanted_delay_min,
 	Opt_congestion_kb,
-	Opt_last_int,
-	/* int args above */
-	Opt_snapdirname,
-	Opt_mds_namespace,
-	Opt_fscache_uniq,
-	Opt_last_string,
-	/* string args above */
-	Opt_dirstat,
-	Opt_nodirstat,
-	Opt_rbytes,
-	Opt_norbytes,
-	Opt_asyncreaddir,
-	Opt_noasyncreaddir,
+	Opt_copyfrom,
 	Opt_dcache,
-	Opt_nodcache,
-	Opt_ino32,
-	Opt_noino32,
+	Opt_dirstat,
 	Opt_fscache,
-	Opt_nofscache,
+	Opt_ino32,
+	Opt_mds_namespace,
 	Opt_poolperm,
-	Opt_nopoolperm,
-	Opt_require_active_mds,
-	Opt_norequire_active_mds,
-#ifdef CONFIG_CEPH_FS_POSIX_ACL
-	Opt_acl,
-#endif
-	Opt_noacl,
 	Opt_quotadf,
-	Opt_noquotadf,
-	Opt_copyfrom,
-	Opt_nocopyfrom,
+	Opt_rasize,
+	Opt_rbytes,
+	Opt_readdir_max_bytes,
+	Opt_readdir_max_entries,
+	Opt_require_active_mds,
+	Opt_rsize,
+	Opt_snapdirname,
+	Opt_source,
+	Opt_wsize,
 };
 
-static match_table_t fsopt_tokens = {
-	{Opt_wsize, "wsize=%d"},
-	{Opt_rsize, "rsize=%d"},
-	{Opt_rasize, "rasize=%d"},
-	{Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"},
-	{Opt_caps_wanted_delay_max, "caps_wanted_delay_max=%d"},
-	{Opt_caps_max, "caps_max=%d"},
-	{Opt_readdir_max_entries, "readdir_max_entries=%d"},
-	{Opt_readdir_max_bytes, "readdir_max_bytes=%d"},
-	{Opt_congestion_kb, "write_congestion_kb=%d"},
-	/* int args above */
-	{Opt_snapdirname, "snapdirname=%s"},
-	{Opt_mds_namespace, "mds_namespace=%s"},
-	{Opt_fscache_uniq, "fsc=%s"},
-	/* string args above */
-	{Opt_dirstat, "dirstat"},
-	{Opt_nodirstat, "nodirstat"},
-	{Opt_rbytes, "rbytes"},
-	{Opt_norbytes, "norbytes"},
-	{Opt_asyncreaddir, "asyncreaddir"},
-	{Opt_noasyncreaddir, "noasyncreaddir"},
-	{Opt_dcache, "dcache"},
-	{Opt_nodcache, "nodcache"},
-	{Opt_ino32, "ino32"},
-	{Opt_noino32, "noino32"},
-	{Opt_fscache, "fsc"},
-	{Opt_nofscache, "nofsc"},
-	{Opt_poolperm, "poolperm"},
-	{Opt_nopoolperm, "nopoolperm"},
-	{Opt_require_active_mds, "require_active_mds"},
-	{Opt_norequire_active_mds, "norequire_active_mds"},
-#ifdef CONFIG_CEPH_FS_POSIX_ACL
-	{Opt_acl, "acl"},
-#endif
-	{Opt_noacl, "noacl"},
-	{Opt_quotadf, "quotadf"},
-	{Opt_noquotadf, "noquotadf"},
-	{Opt_copyfrom, "copyfrom"},
-	{Opt_nocopyfrom, "nocopyfrom"},
-	{-1, NULL}
+static const struct fs_parameter_spec ceph_param_specs[] = {
+	fsparam_flag_no ("acl",				Opt_acl),
+	fsparam_flag_no ("asyncreaddir",		Opt_asyncreaddir),
+	fsparam_u32	("caps_max",			Opt_caps_max),
+	fsparam_u32	("caps_wanted_delay_max",	Opt_caps_wanted_delay_max),
+	fsparam_u32	("caps_wanted_delay_min",	Opt_caps_wanted_delay_min),
+	fsparam_s32	("write_congestion_kb",		Opt_congestion_kb),
+	fsparam_flag_no ("copyfrom",			Opt_copyfrom),
+	fsparam_flag_no ("dcache",			Opt_dcache),
+	fsparam_flag_no ("dirstat",			Opt_dirstat),
+	__fsparam	(fs_param_is_string, "fsc",	Opt_fscache,
+			 fs_param_neg_with_no | fs_param_v_optional),
+	fsparam_flag_no ("ino32",			Opt_ino32),
+	fsparam_string	("mds_namespace",		Opt_mds_namespace),
+	fsparam_flag_no ("poolperm",			Opt_poolperm),
+	fsparam_flag_no ("quotadf",			Opt_quotadf),
+	fsparam_u32	("rasize",			Opt_rasize),
+	fsparam_flag_no ("rbytes",			Opt_rbytes),
+	fsparam_s32	("readdir_max_bytes",		Opt_readdir_max_bytes),
+	fsparam_s32	("readdir_max_entries",		Opt_readdir_max_entries),
+	fsparam_flag_no ("require_active_mds",		Opt_require_active_mds),
+	fsparam_u32	("rsize",			Opt_rsize),
+	fsparam_string	("snapdirname",			Opt_snapdirname),
+	fsparam_string	("source",			Opt_source),
+	fsparam_u32	("wsize",			Opt_wsize),
+	{}
 };
 
-static int parse_fsopt_token(char *c, void *private)
+static const struct fs_parameter_description ceph_fs_parameters = {
+        .name           = "ceph",
+        .specs          = ceph_param_specs,
+};
+
+/*
+ * Parse the source parameter.  Distinguish the server list from the path.
+ * Internally we do not include the leading '/' in the path.
+ *
+ * The source will look like:
+ *     <server_spec>[,<server_spec>...]:[<path>]
+ * where
+ *     <server_spec> is <ip>[:<port>]
+ *     <path> is optional, but if present must begin with '/'
+ */
+static int ceph_parse_source(struct fs_context *fc, struct fs_parameter *param)
 {
-	struct ceph_mount_options *fsopt = private;
-	substring_t argstr[MAX_OPT_ARGS];
-	int token, intval, ret;
-
-	token = match_token((char *)c, fsopt_tokens, argstr);
-	if (token < 0)
-		return -EINVAL;
-
-	if (token < Opt_last_int) {
-		ret = match_int(&argstr[0], &intval);
-		if (ret < 0) {
-			pr_err("bad option arg (not int) at '%s'\n", c);
-			return ret;
+	struct ceph_config_context *ctx = fc->fs_private;
+	struct ceph_mount_options *fsopt = ctx->mount_options;
+	char *dev_name = param->string, *dev_name_end;
+	int ret;
+
+	dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name);
+
+	if (fc->source)
+		return invalf(fc, "Multiple sources specified");
+	if (!dev_name || !*dev_name)
+		return invalf(fc, "Empty source");
+	if (dev_name[0] == '/')
+		return invalf(fc, "Missing colon");
+
+	dev_name_end = strchr(dev_name + 1, '/');
+	if (dev_name_end) {
+		if (strlen(dev_name_end) > 1) {
+			kfree(fsopt->server_path);
+			fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
+			if (!fsopt->server_path)
+				return -ENOMEM;
 		}
-		dout("got int token %d val %d\n", token, intval);
-	} else if (token > Opt_last_int && token < Opt_last_string) {
-		dout("got string token %d val %s\n", token,
-		     argstr[0].from);
 	} else {
-		dout("got token %d\n", token);
+		dev_name_end = dev_name + strlen(dev_name);
+	}
+
+	/* Trim off the path and the colon separator */
+	dev_name_end--;
+	if (*dev_name_end != ':')
+		return invalf(fc, "device name is missing path (no : separator in %s)\n",
+			      dev_name);
+	*dev_name_end = 0;
+
+	dout("device name '%s'\n", dev_name);
+	if (fsopt->server_path)
+		dout("server path '%s'\n", fsopt->server_path);
+
+	param->size = dev_name_end - dev_name;
+	ret = ceph_parse_server_specs(ctx->opt, fc,
+				      param->string, dev_name_end - dev_name);
+	if (ret == 0) {
+		fc->source = param->string;
+		param->string = NULL;
 	}
 
-	switch (token) {
+	return 0;
+}
+
+static int ceph_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct ceph_config_context *ctx = fc->fs_private;
+	struct ceph_mount_options *fsopt = ctx->mount_options;
+	struct fs_parse_result result;
+	int ret, opt;
+
+	ret = ceph_parse_option(ctx->opt, fc, param);
+	if (ret != -ENOPARAM)
+		return ret;
+
+	opt = fs_parse(fc, &ceph_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_source:
+		return ceph_parse_source(fc, param);
 	case Opt_snapdirname:
 		kfree(fsopt->snapdir_name);
-		fsopt->snapdir_name = kstrndup(argstr[0].from,
-					       argstr[0].to-argstr[0].from,
-					       GFP_KERNEL);
-		if (!fsopt->snapdir_name)
-			return -ENOMEM;
+		fsopt->snapdir_name = param->string;
+		param->string = NULL;
 		break;
 	case Opt_mds_namespace:
 		kfree(fsopt->mds_namespace);
-		fsopt->mds_namespace = kstrndup(argstr[0].from,
-						argstr[0].to-argstr[0].from,
-						GFP_KERNEL);
-		if (!fsopt->mds_namespace)
-			return -ENOMEM;
+		fsopt->mds_namespace = param->string;
+		param->string = NULL;
 		break;
-	case Opt_fscache_uniq:
-		kfree(fsopt->fscache_uniq);
-		fsopt->fscache_uniq = kstrndup(argstr[0].from,
-					       argstr[0].to-argstr[0].from,
-					       GFP_KERNEL);
-		if (!fsopt->fscache_uniq)
-			return -ENOMEM;
-		fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
-		break;
-		/* misc */
 	case Opt_wsize:
-		if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_WRITE_SIZE)
-			return -EINVAL;
-		fsopt->wsize = ALIGN(intval, PAGE_SIZE);
+		if (result.uint_32 < (int)PAGE_SIZE || result.uint_32 > CEPH_MAX_WRITE_SIZE)
+			goto invalid_value;
+		fsopt->wsize = ALIGN(result.uint_32, PAGE_SIZE);
 		break;
 	case Opt_rsize:
-		if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_READ_SIZE)
-			return -EINVAL;
-		fsopt->rsize = ALIGN(intval, PAGE_SIZE);
+		if (result.uint_32 < (int)PAGE_SIZE || result.uint_32 > CEPH_MAX_READ_SIZE)
+			goto invalid_value;
+		fsopt->rsize = ALIGN(result.uint_32, PAGE_SIZE);
 		break;
 	case Opt_rasize:
-		if (intval < 0)
-			return -EINVAL;
-		fsopt->rasize = ALIGN(intval, PAGE_SIZE);
+		fsopt->rasize = ALIGN(result.uint_32, PAGE_SIZE);
 		break;
 	case Opt_caps_wanted_delay_min:
-		if (intval < 1)
-			return -EINVAL;
-		fsopt->caps_wanted_delay_min = intval;
+		if (result.uint_32 < 1)
+			goto invalid_value;
+		fsopt->caps_wanted_delay_min = result.uint_32;
 		break;
 	case Opt_caps_wanted_delay_max:
-		if (intval < 1)
-			return -EINVAL;
-		fsopt->caps_wanted_delay_max = intval;
+		if (result.uint_32 < 1)
+			goto invalid_value;
+		fsopt->caps_wanted_delay_max = result.uint_32;
 		break;
 	case Opt_caps_max:
-		if (intval < 0)
-			return -EINVAL;
-		fsopt->caps_max = intval;
+		fsopt->caps_max = result.uint_32;
 		break;
 	case Opt_readdir_max_entries:
-		if (intval < 1)
-			return -EINVAL;
-		fsopt->max_readdir = intval;
+		if (result.uint_32 < 1)
+			goto invalid_value;
+		fsopt->max_readdir = result.uint_32;
 		break;
 	case Opt_readdir_max_bytes:
-		if (intval < (int)PAGE_SIZE && intval != 0)
-			return -EINVAL;
-		fsopt->max_readdir_bytes = intval;
+		if (result.uint_32 < (int)PAGE_SIZE && result.uint_32 != 0)
+			goto invalid_value;
+		fsopt->max_readdir_bytes = result.uint_32;
 		break;
 	case Opt_congestion_kb:
-		if (intval < 1024) /* at least 1M */
-			return -EINVAL;
-		fsopt->congestion_kb = intval;
+		if (result.uint_32 < 1024) /* at least 1M */
+			goto invalid_value;
+		fsopt->congestion_kb = result.uint_32;
 		break;
 	case Opt_dirstat:
-		fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT;
-		break;
-	case Opt_nodirstat:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT;
+		if (!result.negated)
+			fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT;
+		else
+			fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT;
 		break;
 	case Opt_rbytes:
-		fsopt->flags |= CEPH_MOUNT_OPT_RBYTES;
-		break;
-	case Opt_norbytes:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES;
+		if (!result.negated)
+			fsopt->flags |= CEPH_MOUNT_OPT_RBYTES;
+		else
+			fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES;
 		break;
 	case Opt_asyncreaddir:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR;
-		break;
-	case Opt_noasyncreaddir:
-		fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
+		if (!result.negated)
+			fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR;
+		else
+			fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
 		break;
 	case Opt_dcache:
-		fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
-		break;
-	case Opt_nodcache:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
+		if (!result.negated)
+			fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
+		else
+			fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
 		break;
 	case Opt_ino32:
-		fsopt->flags |= CEPH_MOUNT_OPT_INO32;
-		break;
-	case Opt_noino32:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
+		if (!result.negated)
+			fsopt->flags |= CEPH_MOUNT_OPT_INO32;
+		else
+			fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
 		break;
+
 	case Opt_fscache:
-		fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
-		kfree(fsopt->fscache_uniq);
-		fsopt->fscache_uniq = NULL;
-		break;
-	case Opt_nofscache:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
 		kfree(fsopt->fscache_uniq);
 		fsopt->fscache_uniq = NULL;
+		if (result.negated) {
+			fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
+		} else {
+			fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
+			fsopt->fscache_uniq = param->string;
+			param->string = NULL;
+		}
 		break;
+
 	case Opt_poolperm:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM;
-		break;
-	case Opt_nopoolperm:
-		fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM;
+		if (!result.negated)
+			fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM;
+		else
+			fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM;
 		break;
 	case Opt_require_active_mds:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT;
-		break;
-	case Opt_norequire_active_mds:
-		fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
+		if (!result.negated)
+			fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT;
+		else
+			fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
 		break;
 	case Opt_quotadf:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
-		break;
-	case Opt_noquotadf:
-		fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
+		if (!result.negated)
+			fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
+		else
+			fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
 		break;
 	case Opt_copyfrom:
-		fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM;
+		if (!result.negated)
+			fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM;
+		else
+			fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM;
 		break;
-	case Opt_nocopyfrom:
-		fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM;
-		break;
-#ifdef CONFIG_CEPH_FS_POSIX_ACL
 	case Opt_acl:
-		fsopt->sb_flags |= SB_POSIXACL;
-		break;
+		if (!result.negated) {
+#ifdef CONFIG_CEPH_FS_POSIX_ACL
+			fc->sb_flags |= SB_POSIXACL;
+#else
+			return invalf(fc, "POSIX ACL support is disabled");
 #endif
-	case Opt_noacl:
-		fsopt->sb_flags &= ~SB_POSIXACL;
+		} else {
+			fc->sb_flags &= ~SB_POSIXACL;
+		}
 		break;
 	default:
-		BUG_ON(token);
+		BUG();
 	}
 	return 0;
+
+invalid_value:
+	return invalf(fc, "ceph: Invalid value for %s", param->key);
 }
 
 static void destroy_mount_options(struct ceph_mount_options *args)
 {
-	dout("destroy_mount_options %p\n", args);
-	kfree(args->snapdir_name);
-	kfree(args->mds_namespace);
-	kfree(args->server_path);
-	kfree(args->fscache_uniq);
-	kfree(args);
+	if (args) {
+		dout("destroy_mount_options %p\n", args);
+		kfree(args->snapdir_name);
+		kfree(args->mds_namespace);
+		kfree(args->server_path);
+		kfree(args->fscache_uniq);
+		kfree(args);
+	}
 }
 
 static int strcmp_null(const char *s1, const char *s2)
@@ -436,91 +455,6 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
 	return ceph_compare_options(new_opt, fsc->client);
 }
 
-static int parse_mount_options(struct ceph_mount_options **pfsopt,
-			       struct ceph_options **popt,
-			       int flags, char *options,
-			       const char *dev_name)
-{
-	struct ceph_mount_options *fsopt;
-	const char *dev_name_end;
-	int err;
-
-	if (!dev_name || !*dev_name)
-		return -EINVAL;
-
-	fsopt = kzalloc(sizeof(*fsopt), GFP_KERNEL);
-	if (!fsopt)
-		return -ENOMEM;
-
-	dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name);
-
-	fsopt->sb_flags = flags;
-	fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
-
-	fsopt->wsize = CEPH_MAX_WRITE_SIZE;
-	fsopt->rsize = CEPH_MAX_READ_SIZE;
-	fsopt->rasize = CEPH_RASIZE_DEFAULT;
-	fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
-	if (!fsopt->snapdir_name) {
-		err = -ENOMEM;
-		goto out;
-	}
-
-	fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
-	fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
-	fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
-	fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
-	fsopt->congestion_kb = default_congestion_kb();
-
-	/*
-	 * Distinguish the server list from the path in "dev_name".
-	 * Internally we do not include the leading '/' in the path.
-	 *
-	 * "dev_name" will look like:
-	 *     <server_spec>[,<server_spec>...]:[<path>]
-	 * where
-	 *     <server_spec> is <ip>[:<port>]
-	 *     <path> is optional, but if present must begin with '/'
-	 */
-	dev_name_end = strchr(dev_name, '/');
-	if (dev_name_end) {
-		if (strlen(dev_name_end) > 1) {
-			fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
-			if (!fsopt->server_path) {
-				err = -ENOMEM;
-				goto out;
-			}
-		}
-	} else {
-		dev_name_end = dev_name + strlen(dev_name);
-	}
-	err = -EINVAL;
-	dev_name_end--;		/* back up to ':' separator */
-	if (dev_name_end < dev_name || *dev_name_end != ':') {
-		pr_err("device name is missing path (no : separator in %s)\n",
-				dev_name);
-		goto out;
-	}
-	dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name);
-	if (fsopt->server_path)
-		dout("server path '%s'\n", fsopt->server_path);
-
-	*popt = ceph_parse_options(options, dev_name, dev_name_end,
-				 parse_fsopt_token, (void *)fsopt);
-	if (IS_ERR(*popt)) {
-		err = PTR_ERR(*popt);
-		goto out;
-	}
-
-	/* success */
-	*pfsopt = fsopt;
-	return 0;
-
-out:
-	destroy_mount_options(fsopt);
-	return err;
-}
-
 /**
  * ceph_show_options - Show mount options in /proc/mounts
  * @m: seq_file to write to
@@ -564,7 +498,7 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
 		seq_puts(m, ",noquotadf");
 
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
-	if (fsopt->sb_flags & SB_POSIXACL)
+	if (root->d_sb->s_flags & SB_POSIXACL)
 		seq_puts(m, ",acl");
 	else
 		seq_puts(m, ",noacl");
@@ -624,12 +558,10 @@ static int extra_mon_dispatch(struct ceph_client *client, struct ceph_msg *msg)
 
 /*
  * create a new fs client
- *
- * Success or not, this function consumes @fsopt and @opt.
  */
-static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
-					struct ceph_options *opt)
+static struct ceph_fs_client *create_fs_client(struct fs_context *fc)
 {
+	struct ceph_config_context *ctx = fc->fs_private;
 	struct ceph_fs_client *fsc;
 	int page_count;
 	size_t size;
@@ -641,17 +573,18 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
 		goto fail;
 	}
 
-	fsc->client = ceph_create_client(opt, fsc);
+	fsc->client = ceph_create_client(ctx->opt, fsc);
 	if (IS_ERR(fsc->client)) {
+		errorf(fc, "ceph: Failed to create client");
 		err = PTR_ERR(fsc->client);
 		goto fail;
 	}
-	opt = NULL; /* fsc->client now owns this */
+	ctx->opt = NULL; /* fsc->client now owns this */
 
 	fsc->client->extra_mon_dispatch = extra_mon_dispatch;
 	ceph_set_opt(fsc->client, ABORT_ON_FULL);
 
-	if (!fsopt->mds_namespace) {
+	if (!ctx->mount_options->mds_namespace) {
 		ceph_monc_want_map(&fsc->client->monc, CEPH_SUB_MDSMAP,
 				   0, true);
 	} else {
@@ -659,7 +592,8 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
 				   0, false);
 	}
 
-	fsc->mount_options = fsopt;
+	fsc->mount_options = ctx->mount_options;
+	ctx->mount_options = NULL;
 
 	fsc->sb = NULL;
 	fsc->mount_state = CEPH_MOUNT_MOUNTING;
@@ -706,9 +640,6 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
 	ceph_destroy_client(fsc->client);
 fail:
 	kfree(fsc);
-	if (opt)
-		ceph_destroy_options(opt);
-	destroy_mount_options(fsopt);
 	return ERR_PTR(err);
 }
 
@@ -912,7 +843,7 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
 /*
  * mount: join the ceph cluster, and open root directory.
  */
-static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
+static int ceph_real_mount(struct fs_context *fc, struct ceph_fs_client *fsc)
 {
 	int err;
 	unsigned long started = jiffies;  /* note the start time */
@@ -929,7 +860,7 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
 
 		/* setup fscache */
 		if (fsc->mount_options->flags & CEPH_MOUNT_OPT_FSCACHE) {
-			err = ceph_fscache_register_fs(fsc);
+			err = ceph_fscache_register_fs(fc, fsc);
 			if (err < 0)
 				goto out;
 		}
@@ -943,41 +874,41 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
 		}
 
 		err = ceph_fs_debugfs_init(fsc);
-		if (err < 0)
+		if (err < 0) {
+			errorf(fc, "ceph: Can't create debugfs entries: %d",
+			       err);
 			goto out;
+		}
 
 		root = open_root_dentry(fsc, path, started);
 		if (IS_ERR(root)) {
 			err = PTR_ERR(root);
 			goto out;
 		}
-		fsc->sb->s_root = dget(root);
-	} else {
-		root = dget(fsc->sb->s_root);
+		fsc->sb->s_root = root;
 	}
 
+	fc->root = dget(fsc->sb->s_root);
 	fsc->mount_state = CEPH_MOUNT_MOUNTED;
 	dout("mount success\n");
 	mutex_unlock(&fsc->client->mount_mutex);
-	return root;
+	return err;
 
 out:
 	mutex_unlock(&fsc->client->mount_mutex);
-	return ERR_PTR(err);
+	return err;
 }
 
-static int ceph_set_super(struct super_block *s, void *data)
+static int ceph_set_super(struct super_block *s, struct fs_context *fc)
 {
-	struct ceph_fs_client *fsc = data;
+	struct ceph_fs_client *fsc = s->s_fs_info;
 	int ret;
 
-	dout("set_super %p data %p\n", s, data);
+	dout("set_super %p\n", s);
 
-	s->s_flags = fsc->mount_options->sb_flags;
 	s->s_maxbytes = MAX_LFS_FILESIZE;
 
 	s->s_xattr = ceph_xattr_handlers;
-	s->s_fs_info = fsc;
 	fsc->sb = s;
 	fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */
 
@@ -987,24 +918,18 @@ static int ceph_set_super(struct super_block *s, void *data)
 
 	s->s_time_gran = 1000;  /* 1000 ns == 1 us */
 
-	ret = set_anon_super(s, NULL);  /* what is that second arg for? */
+	ret = set_anon_super_fc(s, fc);
 	if (ret != 0)
-		goto fail;
-
-	return ret;
-
-fail:
-	s->s_fs_info = NULL;
-	fsc->sb = NULL;
+		fsc->sb = NULL;
 	return ret;
 }
 
 /*
  * share superblock if same fs AND options
  */
-static int ceph_compare_super(struct super_block *sb, void *data)
+static int ceph_compare_super(struct super_block *sb, struct fs_context *fc)
 {
-	struct ceph_fs_client *new = data;
+	struct ceph_fs_client *new = fc->s_fs_info;
 	struct ceph_mount_options *fsopt = new->mount_options;
 	struct ceph_options *opt = new->client->options;
 	struct ceph_fs_client *other = ceph_sb_to_client(sb);
@@ -1020,7 +945,7 @@ static int ceph_compare_super(struct super_block *sb, void *data)
 		dout("fsid doesn't match\n");
 		return 0;
 	}
-	if (fsopt->sb_flags != other->mount_options->sb_flags) {
+	if (fc->sb_flags != (sb->s_flags & ~SB_BORN)) {
 		dout("flags differ\n");
 		return 0;
 	}
@@ -1050,46 +975,41 @@ static int ceph_setup_bdi(struct super_block *sb, struct ceph_fs_client *fsc)
 	return 0;
 }
 
-static struct dentry *ceph_mount(struct file_system_type *fs_type,
-		       int flags, const char *dev_name, void *data)
+static int ceph_get_tree(struct fs_context *fc)
 {
-	struct super_block *sb;
 	struct ceph_fs_client *fsc;
-	struct dentry *res;
+	struct super_block *sb;
 	int err;
-	int (*compare_super)(struct super_block *, void *) = ceph_compare_super;
-	struct ceph_mount_options *fsopt = NULL;
-	struct ceph_options *opt = NULL;
+	int (*compare_super)(struct super_block *, struct fs_context *) =
+		ceph_compare_super;
+
+	dout("ceph_get_tree\n");
 
-	dout("ceph_mount\n");
+	if (!fc->source)
+		return invalf(fc, "source parameter not specified");
 
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
-	flags |= SB_POSIXACL;
+	fc->sb_flags |= SB_POSIXACL;
 #endif
-	err = parse_mount_options(&fsopt, &opt, flags, data, dev_name);
-	if (err < 0) {
-		res = ERR_PTR(err);
-		goto out_final;
-	}
 
 	/* create client (which we may/may not use) */
-	fsc = create_fs_client(fsopt, opt);
+	fsc = create_fs_client(fc);
 	if (IS_ERR(fsc)) {
-		res = ERR_CAST(fsc);
-		goto out_final;
+		err = PTR_ERR(fsc);
+		goto out;
 	}
 
 	err = ceph_mdsc_init(fsc);
-	if (err < 0) {
-		res = ERR_PTR(err);
+	if (err < 0)
 		goto out;
-	}
 
 	if (ceph_test_opt(fsc->client, NOSHARE))
 		compare_super = NULL;
-	sb = sget(fs_type, compare_super, ceph_set_super, flags, fsc);
+
+	fc->s_fs_info = fsc;
+	sb = sget_fc(fc, compare_super, ceph_set_super);
 	if (IS_ERR(sb)) {
-		res = ERR_CAST(sb);
+		err = PTR_ERR(sb);
 		goto out;
 	}
 
@@ -1101,30 +1021,97 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type,
 	} else {
 		dout("get_sb using new client %p\n", fsc);
 		err = ceph_setup_bdi(sb, fsc);
-		if (err < 0) {
-			res = ERR_PTR(err);
+		if (err < 0)
 			goto out_splat;
-		}
 	}
 
-	res = ceph_real_mount(fsc);
-	if (IS_ERR(res))
+	err = ceph_real_mount(fc, fsc);
+	if (err < 0)
 		goto out_splat;
-	dout("root %p inode %p ino %llx.%llx\n", res,
-	     d_inode(res), ceph_vinop(d_inode(res)));
-	return res;
+	dout("root %p inode %p ino %llx.%llx\n",
+	     fc->root, d_inode(fc->root), ceph_vinop(d_inode(fc->root)));
+	return 0;
 
 out_splat:
 	ceph_mdsc_close_sessions(fsc->mdsc);
 	deactivate_locked_super(sb);
-	goto out_final;
-
 out:
-	ceph_mdsc_destroy(fsc);
-	destroy_fs_client(fsc);
-out_final:
-	dout("ceph_mount fail %ld\n", PTR_ERR(res));
-	return res;
+	dout("ceph_mount fail %d\n", err);
+	return err;
+}
+
+static void ceph_free_fc(struct fs_context *fc)
+{
+	struct ceph_config_context *ctx = fc->fs_private;
+	struct ceph_fs_client *fsc = fc->s_fs_info;
+
+	if (fsc) {
+		ceph_mdsc_destroy(fsc);
+		destroy_fs_client(fsc);
+	}
+
+	if (ctx) {
+		destroy_mount_options(ctx->mount_options);
+		ceph_destroy_options(ctx->opt);
+		kfree(ctx);
+	}
+}
+
+static const struct fs_context_operations ceph_context_ops = {
+	.free		= ceph_free_fc,
+	.parse_param	= ceph_parse_param,
+	.get_tree	= ceph_get_tree,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+static int ceph_init_fs_context(struct fs_context *fc)
+{
+	struct ceph_config_context *ctx;
+	struct ceph_mount_options *fsopt;
+
+	ctx = kzalloc(sizeof(struct ceph_config_context), GFP_KERNEL);
+	if (!ctx)
+		goto nomem;
+
+	ctx->mount_options = kzalloc(sizeof(struct ceph_mount_options), GFP_KERNEL);
+	if (!ctx->mount_options)
+		goto nomem_ctx;
+
+	ctx->mount_options->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
+	if (!ctx->mount_options->snapdir_name)
+		goto nomem_mo;
+
+	ctx->opt = ceph_alloc_options();
+	if (!ctx->opt)
+		goto nomem_snap;
+
+	fsopt = ctx->mount_options;
+	fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
+
+	fsopt->wsize = CEPH_MAX_WRITE_SIZE;
+	fsopt->rsize = CEPH_MAX_READ_SIZE;
+	fsopt->rasize = CEPH_RASIZE_DEFAULT;
+
+	fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
+	fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
+	fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
+	fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
+	fsopt->congestion_kb = default_congestion_kb();
+
+	fc->fs_private = ctx;
+	fc->ops = &ceph_context_ops;
+	return 0;
+
+nomem_snap:
+	kfree(ctx->mount_options->snapdir_name);
+nomem_mo:
+	kfree(ctx->mount_options);
+nomem_ctx:
+	kfree(ctx);
+nomem:
+	return -ENOMEM;
 }
 
 static void ceph_kill_sb(struct super_block *s)
@@ -1153,7 +1140,7 @@ static void ceph_kill_sb(struct super_block *s)
 static struct file_system_type ceph_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "ceph",
-	.mount		= ceph_mount,
+	.init_fs_context = ceph_init_fs_context,
 	.kill_sb	= ceph_kill_sb,
 	.fs_flags	= FS_RENAME_DOES_D_MOVE,
 };
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 16c03188578e..47e0e4b4f7f8 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -72,7 +72,6 @@
 
 struct ceph_mount_options {
 	int flags;
-	int sb_flags;
 
 	int wsize;            /* max write size */
 	int rsize;            /* max read size */
diff --git a/fs/fs_context.c b/fs/fs_context.c
index f9ad2554c2b8..68822d126f50 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -195,7 +195,7 @@ EXPORT_SYMBOL(vfs_parse_fs_string);
 
 /**
  * generic_parse_monolithic - Parse key[=val][,key[=val]]* mount data
- * @ctx: The superblock configuration to fill in.
+ * @fc: The filesystem configuration to fill in.
  * @data: The data to parse
  *
  * Parse a blob of data that's in key[=val][,key[=val]]* form.  This can be
diff --git a/fs/fs_parser.c b/fs/fs_parser.c
index bd59e1725c50..a74f1c5f3599 100644
--- a/fs/fs_parser.c
+++ b/fs/fs_parser.c
@@ -64,7 +64,7 @@ static const struct fs_parameter_spec *fs_lookup_key(
 
 /*
  * fs_parse - Parse a filesystem configuration parameter
- * @fc: The filesystem context to log errors through.
+ * @fc: The filesystem context to log errors through (or NULL).
  * @desc: The parameter description to use.
  * @param: The parameter.
  * @result: Where to place the result of the parse
diff --git a/include/linux/ceph/ceph_debug.h b/include/linux/ceph/ceph_debug.h
index d5a5da838caf..fa4a84e0e018 100644
--- a/include/linux/ceph/ceph_debug.h
+++ b/include/linux/ceph/ceph_debug.h
@@ -2,6 +2,7 @@
 #ifndef _FS_CEPH_DEBUG_H
 #define _FS_CEPH_DEBUG_H
 
+#undef pr_fmt
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/string.h>
diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h
index 337d5049ff93..82fdb821f19d 100644
--- a/include/linux/ceph/libceph.h
+++ b/include/linux/ceph/libceph.h
@@ -66,6 +66,13 @@ struct ceph_options {
 	struct ceph_crypto_key *key;
 };
 
+struct ceph_config_context {
+	struct ceph_options		*opt;
+	struct ceph_mount_options	*mount_options;
+	struct rbd_spec			*rbd_spec;
+	struct rbd_options		*rbd_opts;
+};
+
 /*
  * defaults
  */
@@ -278,10 +285,12 @@ extern const char *ceph_msg_type_name(int type);
 extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid);
 extern void *ceph_kvmalloc(size_t size, gfp_t flags);
 
-extern struct ceph_options *ceph_parse_options(char *options,
-			      const char *dev_name, const char *dev_name_end,
-			      int (*parse_extra_token)(char *c, void *private),
-			      void *private);
+struct fs_parameter;
+extern struct ceph_options *ceph_alloc_options(void);
+extern int ceph_parse_server_specs(struct ceph_options *opt, struct fs_context *fc,
+				   const char *data, size_t size);
+extern int ceph_parse_option(struct ceph_options *opt, struct fs_context *fc,
+			     struct fs_parameter *param);
 int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
 			      bool show_all);
 extern void ceph_destroy_options(struct ceph_options *opt);
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index 79eac465ec65..40387d7d30ef 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -10,7 +10,7 @@
 #include <linux/module.h>
 #include <linux/mount.h>
 #include <linux/nsproxy.h>
-#include <linux/parser.h>
+#include <linux/fs_parser.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
@@ -231,70 +231,84 @@ static int parse_fsid(const char *str, struct ceph_fsid *fsid)
  * ceph options
  */
 enum {
-	Opt_osdtimeout,
-	Opt_osdkeepalivetimeout,
+	Opt_abort_on_full,
+	Opt_cephx_require_signatures,
+	Opt_cephx_sign_messages,
+	Opt_crc,
+	Opt_fsid,
+	Opt_ip,
+	Opt_key,
 	Opt_mount_timeout,
+	Opt_name,
 	Opt_osd_idle_ttl,
 	Opt_osd_request_timeout,
-	Opt_last_int,
-	/* int args above */
-	Opt_fsid,
-	Opt_name,
+	Opt_osdkeepalivetimeout,
+	Opt_osdtimeout,
 	Opt_secret,
-	Opt_key,
-	Opt_ip,
-	Opt_last_string,
-	/* string args above */
 	Opt_share,
-	Opt_noshare,
-	Opt_crc,
-	Opt_nocrc,
-	Opt_cephx_require_signatures,
-	Opt_nocephx_require_signatures,
-	Opt_cephx_sign_messages,
-	Opt_nocephx_sign_messages,
 	Opt_tcp_nodelay,
-	Opt_notcp_nodelay,
-	Opt_abort_on_full,
 };
 
-static match_table_t opt_tokens = {
-	{Opt_osdtimeout, "osdtimeout=%d"},
-	{Opt_osdkeepalivetimeout, "osdkeepalive=%d"},
-	{Opt_mount_timeout, "mount_timeout=%d"},
-	{Opt_osd_idle_ttl, "osd_idle_ttl=%d"},
-	{Opt_osd_request_timeout, "osd_request_timeout=%d"},
-	/* int args above */
-	{Opt_fsid, "fsid=%s"},
-	{Opt_name, "name=%s"},
-	{Opt_secret, "secret=%s"},
-	{Opt_key, "key=%s"},
-	{Opt_ip, "ip=%s"},
-	/* string args above */
-	{Opt_share, "share"},
-	{Opt_noshare, "noshare"},
-	{Opt_crc, "crc"},
-	{Opt_nocrc, "nocrc"},
-	{Opt_cephx_require_signatures, "cephx_require_signatures"},
-	{Opt_nocephx_require_signatures, "nocephx_require_signatures"},
-	{Opt_cephx_sign_messages, "cephx_sign_messages"},
-	{Opt_nocephx_sign_messages, "nocephx_sign_messages"},
-	{Opt_tcp_nodelay, "tcp_nodelay"},
-	{Opt_notcp_nodelay, "notcp_nodelay"},
-	{Opt_abort_on_full, "abort_on_full"},
-	{-1, NULL}
+static const struct fs_parameter_spec ceph_option_specs[] = {
+	fsparam_flag	("abort_on_full",		Opt_abort_on_full),
+	fsparam_flag_no ("cephx_require_signatures",	Opt_cephx_require_signatures),
+	fsparam_flag_no ("cephx_sign_messages",		Opt_cephx_sign_messages),
+	fsparam_flag_no ("crc",				Opt_crc),
+	fsparam_string	("fsid",			Opt_fsid),
+	fsparam_string	("ip",				Opt_ip),
+	fsparam_string	("key",				Opt_key),
+	fsparam_u32	("mount_timeout",		Opt_mount_timeout),
+	fsparam_string	("name",			Opt_name),
+	fsparam_u32	("osd_idle_ttl",		Opt_osd_idle_ttl),
+	fsparam_u32	("osd_request_timeout",		Opt_osd_request_timeout),
+	fsparam_u32	("osdkeepalive",		Opt_osdkeepalivetimeout),
+	__fsparam	(fs_param_is_s32, "osdtimeout", Opt_osdtimeout, fs_param_deprecated),
+	fsparam_string	("secret",			Opt_secret),
+	fsparam_flag_no ("share",			Opt_share),
+	fsparam_flag_no ("tcp_nodelay",			Opt_tcp_nodelay),
+	{}
+};
+
+static const struct fs_parameter_description ceph_options = {
+        .name           = "ceph",
+        .specs          = ceph_option_specs,
 };
 
+struct ceph_options *ceph_alloc_options(void)
+{
+	struct ceph_options *opt;
+	
+	opt = kzalloc(sizeof(struct ceph_options), GFP_KERNEL);
+	if (!opt)
+		return NULL;
+
+	opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr), GFP_KERNEL);
+	if (!opt->mon_addr) {
+		kfree(opt);
+		return NULL;
+	}
+
+	opt->flags = CEPH_OPT_DEFAULT;
+	opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
+	opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT;
+	opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;
+	opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT;
+	return opt;
+}
+EXPORT_SYMBOL(ceph_alloc_options);
+
 void ceph_destroy_options(struct ceph_options *opt)
 {
-	dout("destroy_options %p\n", opt);
-	kfree(opt->name);
-	if (opt->key) {
-		ceph_crypto_key_destroy(opt->key);
-		kfree(opt->key);
+	if (opt) {
+		dout("destroy_options %p\n", opt);
+		kfree(opt->name);
+		if (opt->key) {
+			ceph_crypto_key_destroy(opt->key);
+			kfree(opt->key);
+		}
+		kfree(opt->mon_addr);
+		kfree(opt);
 	}
-	kfree(opt->mon_addr);
-	kfree(opt);
 }
 EXPORT_SYMBOL(ceph_destroy_options);
 
@@ -343,217 +357,141 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) {
 	return err;
 }
 
-struct ceph_options *
-ceph_parse_options(char *options, const char *dev_name,
-			const char *dev_name_end,
-			int (*parse_extra_token)(char *c, void *private),
-			void *private)
+int ceph_parse_server_specs(struct ceph_options *opt, struct fs_context *fc,
+			    const char *data, size_t size)
 {
-	struct ceph_options *opt;
-	const char *c;
-	int err = -ENOMEM;
-	substring_t argstr[MAX_OPT_ARGS];
-
-	opt = kzalloc(sizeof(*opt), GFP_KERNEL);
-	if (!opt)
-		return ERR_PTR(-ENOMEM);
-	opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr),
-				GFP_KERNEL);
-	if (!opt->mon_addr)
-		goto out;
-
-	dout("parse_options %p options '%s' dev_name '%s'\n", opt, options,
-	     dev_name);
-
-	/* start with defaults */
-	opt->flags = CEPH_OPT_DEFAULT;
-	opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
-	opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT;
-	opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;
-	opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT;
-
 	/* get mon ip(s) */
 	/* ip1[:port1][,ip2[:port2]...] */
-	err = ceph_parse_ips(dev_name, dev_name_end, opt->mon_addr,
-			     CEPH_MAX_MON, &opt->num_mon);
-	if (err < 0)
-		goto out;
+	return ceph_parse_ips(data, data + size,
+			      opt->mon_addr, CEPH_MAX_MON, &opt->num_mon);
+}
+EXPORT_SYMBOL(ceph_parse_server_specs);
 
-	/* parse mount options */
-	while ((c = strsep(&options, ",")) != NULL) {
-		int token, intval;
-		if (!*c)
-			continue;
-		err = -EINVAL;
-		token = match_token((char *)c, opt_tokens, argstr);
-		if (token < 0 && parse_extra_token) {
-			/* extra? */
-			err = parse_extra_token((char *)c, private);
-			if (err < 0) {
-				pr_err("bad option at '%s'\n", c);
-				goto out;
-			}
-			continue;
-		}
-		if (token < Opt_last_int) {
-			err = match_int(&argstr[0], &intval);
-			if (err < 0) {
-				pr_err("bad option arg (not int) at '%s'\n", c);
-				goto out;
-			}
-			dout("got int token %d val %d\n", token, intval);
-		} else if (token > Opt_last_int && token < Opt_last_string) {
-			dout("got string token %d val %s\n", token,
-			     argstr[0].from);
-		} else {
-			dout("got token %d\n", token);
-		}
-		switch (token) {
-		case Opt_ip:
-			err = ceph_parse_ips(argstr[0].from,
-					     argstr[0].to,
-					     &opt->my_addr,
-					     1, NULL);
-			if (err < 0)
-				goto out;
+int ceph_parse_option(struct ceph_options *opt, struct fs_context *fc,
+		      struct fs_parameter *param)
+{
+	struct fs_parse_result result;
+	int token, err;
+
+	dout("parse_option '%s'\n", param->key);
+
+	token = fs_parse(fc, &ceph_options, param, &result);
+	if (token < 0)
+		return token;
+
+	switch (token) {
+	case Opt_ip:
+		err = ceph_parse_ips(param->string,
+				     param->string + param->size,
+				     &opt->my_addr,
+				     1, NULL);
+		if (err == 0)
 			opt->flags |= CEPH_OPT_MYIP;
-			break;
+		break;
 
-		case Opt_fsid:
-			err = parse_fsid(argstr[0].from, &opt->fsid);
-			if (err == 0)
-				opt->flags |= CEPH_OPT_FSID;
-			break;
-		case Opt_name:
-			kfree(opt->name);
-			opt->name = kstrndup(argstr[0].from,
-					      argstr[0].to-argstr[0].from,
-					      GFP_KERNEL);
-			if (!opt->name) {
-				err = -ENOMEM;
-				goto out;
-			}
-			break;
-		case Opt_secret:
-			ceph_crypto_key_destroy(opt->key);
-			kfree(opt->key);
-
-		        opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
-			if (!opt->key) {
-				err = -ENOMEM;
-				goto out;
-			}
-			err = ceph_crypto_key_unarmor(opt->key, argstr[0].from);
-			if (err < 0)
-				goto out;
-			break;
-		case Opt_key:
-			ceph_crypto_key_destroy(opt->key);
-			kfree(opt->key);
-
-		        opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
-			if (!opt->key) {
-				err = -ENOMEM;
-				goto out;
-			}
-			err = get_secret(opt->key, argstr[0].from);
-			if (err < 0)
-				goto out;
-			break;
+	case Opt_fsid:
+		err = parse_fsid(param->string, &opt->fsid);
+		if (err < 0)
+			return invalf(fc, "Invalid fsid");
+		opt->flags |= CEPH_OPT_FSID;
+		break;
+	case Opt_name:
+		kfree(opt->name);
+		opt->name = param->string;
+		param->string = NULL;
+		break;
+	case Opt_secret:
+		ceph_crypto_key_destroy(opt->key);
+		kfree(opt->key);
 
-			/* misc */
-		case Opt_osdtimeout:
-			pr_warn("ignoring deprecated osdtimeout option\n");
-			break;
-		case Opt_osdkeepalivetimeout:
-			/* 0 isn't well defined right now, reject it */
-			if (intval < 1 || intval > INT_MAX / 1000) {
-				pr_err("osdkeepalive out of range\n");
-				err = -EINVAL;
-				goto out;
-			}
-			opt->osd_keepalive_timeout =
-					msecs_to_jiffies(intval * 1000);
-			break;
-		case Opt_osd_idle_ttl:
-			/* 0 isn't well defined right now, reject it */
-			if (intval < 1 || intval > INT_MAX / 1000) {
-				pr_err("osd_idle_ttl out of range\n");
-				err = -EINVAL;
-				goto out;
-			}
-			opt->osd_idle_ttl = msecs_to_jiffies(intval * 1000);
-			break;
-		case Opt_mount_timeout:
-			/* 0 is "wait forever" (i.e. infinite timeout) */
-			if (intval < 0 || intval > INT_MAX / 1000) {
-				pr_err("mount_timeout out of range\n");
-				err = -EINVAL;
-				goto out;
-			}
-			opt->mount_timeout = msecs_to_jiffies(intval * 1000);
-			break;
-		case Opt_osd_request_timeout:
-			/* 0 is "wait forever" (i.e. infinite timeout) */
-			if (intval < 0 || intval > INT_MAX / 1000) {
-				pr_err("osd_request_timeout out of range\n");
-				err = -EINVAL;
-				goto out;
-			}
-			opt->osd_request_timeout = msecs_to_jiffies(intval * 1000);
-			break;
+		opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
+		if (!opt->key)
+			return -ENOMEM;
+		return ceph_crypto_key_unarmor(opt->key, param->string);
+	case Opt_key:
+		ceph_crypto_key_destroy(opt->key);
+		kfree(opt->key);
 
-		case Opt_share:
+		opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
+		if (!opt->key)
+			return -ENOMEM;
+		return get_secret(opt->key, param->string);
+
+		/* misc */
+	case Opt_osdkeepalivetimeout:
+		/* 0 isn't well defined right now, reject it */
+		if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000)
+			goto out_of_range;
+		opt->osd_keepalive_timeout =
+			msecs_to_jiffies(result.uint_32 * 1000);
+		break;
+	case Opt_osd_idle_ttl:
+		/* 0 isn't well defined right now, reject it */
+		if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000)
+			goto out_of_range;
+		opt->osd_idle_ttl = msecs_to_jiffies(result.uint_32 * 1000);
+		break;
+	case Opt_mount_timeout:
+		/* 0 is "wait forever" (i.e. infinite timeout) */
+		if (result.uint_32 > INT_MAX / 1000)
+			goto out_of_range;
+		opt->mount_timeout = msecs_to_jiffies(result.uint_32 * 1000);
+		break;
+	case Opt_osd_request_timeout:
+		/* 0 is "wait forever" (i.e. infinite timeout) */
+		if (result.uint_32 > INT_MAX / 1000)
+			goto out_of_range;
+		opt->osd_request_timeout = msecs_to_jiffies(result.uint_32 * 1000);
+		break;
+
+	case Opt_share:
+		if (!result.negated)
 			opt->flags &= ~CEPH_OPT_NOSHARE;
-			break;
-		case Opt_noshare:
+		else
 			opt->flags |= CEPH_OPT_NOSHARE;
-			break;
+		break;
 
-		case Opt_crc:
+	case Opt_crc:
+		if (!result.negated)
 			opt->flags &= ~CEPH_OPT_NOCRC;
-			break;
-		case Opt_nocrc:
+		else
 			opt->flags |= CEPH_OPT_NOCRC;
-			break;
+		break;
 
-		case Opt_cephx_require_signatures:
+	case Opt_cephx_require_signatures:
+		if (!result.negated)
 			opt->flags &= ~CEPH_OPT_NOMSGAUTH;
-			break;
-		case Opt_nocephx_require_signatures:
+		else
 			opt->flags |= CEPH_OPT_NOMSGAUTH;
-			break;
-		case Opt_cephx_sign_messages:
+		break;
+	case Opt_cephx_sign_messages:
+		if (!result.negated)
 			opt->flags &= ~CEPH_OPT_NOMSGSIGN;
-			break;
-		case Opt_nocephx_sign_messages:
+		else
 			opt->flags |= CEPH_OPT_NOMSGSIGN;
-			break;
+		break;
 
-		case Opt_tcp_nodelay:
+	case Opt_tcp_nodelay:
+		if (!result.negated)
 			opt->flags |= CEPH_OPT_TCP_NODELAY;
-			break;
-		case Opt_notcp_nodelay:
+		else
 			opt->flags &= ~CEPH_OPT_TCP_NODELAY;
-			break;
+		break;
 
-		case Opt_abort_on_full:
-			opt->flags |= CEPH_OPT_ABORT_ON_FULL;
-			break;
+	case Opt_abort_on_full:
+		opt->flags |= CEPH_OPT_ABORT_ON_FULL;
+		break;
 
-		default:
-			BUG_ON(token);
-		}
+	default:
+		BUG();
 	}
 
-	/* success */
-	return opt;
+	return 0;
 
-out:
-	ceph_destroy_options(opt);
-	return ERR_PTR(err);
+out_of_range:
+	return invalf(fc, "ceph: %s out of range", param->key);
 }
-EXPORT_SYMBOL(ceph_parse_options);
+EXPORT_SYMBOL(ceph_parse_option);
 
 int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
 			      bool show_all)


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 53/68] vfs: Convert functionfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (51 preceding siblings ...)
  2019-03-27 23:47 ` [RFC PATCH 52/68] vfs: Convert ceph " David Howells
@ 2019-03-27 23:47 ` David Howells
  2019-03-28 10:43   ` Michał Nazarewicz
  2019-03-27 23:47 ` [RFC PATCH 54/68] vfs: Add a single-or-reconfig keying to vfs_get_super() David Howells
                   ` (14 subsequent siblings)
  67 siblings, 1 reply; 87+ messages in thread
From: David Howells @ 2019-03-27 23:47 UTC (permalink / raw)
  To: viro
  Cc: Felipe Balbi, Michał Nazarewicz, linux-usb, linux-fsdevel,
	linux-kernel, dhowells

Convert the functionfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Felipe Balbi <balbi@kernel.org>
cc: Michał Nazarewicz <mina86@mina86.com>
cc: linux-usb@vger.kernel.org
---

 drivers/usb/gadget/function/f_fs.c |  233 +++++++++++++++++++-----------------
 1 file changed, 120 insertions(+), 113 deletions(-)

diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 20413c276c61..796c8b688477 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -17,6 +17,7 @@
 #include <linux/blkdev.h>
 #include <linux/pagemap.h>
 #include <linux/export.h>
+#include <linux/fs_parser.h>
 #include <linux/hid.h>
 #include <linux/mm.h>
 #include <linux/module.h>
@@ -1447,9 +1448,9 @@ struct ffs_sb_fill_data {
 	struct ffs_data *ffs_data;
 };
 
-static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
 {
-	struct ffs_sb_fill_data *data = _data;
+	struct ffs_sb_fill_data *data = fc->fs_private;
 	struct inode	*inode;
 	struct ffs_data	*ffs = data->ffs_data;
 
@@ -1482,147 +1483,152 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
 	return 0;
 }
 
-static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
-{
-	ENTER();
+enum {
+	Opt_no_disconnect,
+	Opt_rmode,
+	Opt_fmode,
+	Opt_mode,
+	Opt_uid,
+	Opt_gid,
+};
 
-	if (!opts || !*opts)
-		return 0;
+static const struct fs_parameter_spec ffs_fs_param_specs[] = {
+	fsparam_bool	("no_disconnect",	Opt_no_disconnect),
+	fsparam_u32	("rmode",		Opt_rmode),
+	fsparam_u32	("fmode",		Opt_fmode),
+	fsparam_u32	("mode",		Opt_mode),
+	fsparam_u32	("uid",			Opt_uid),
+	fsparam_u32	("gid",			Opt_gid),
+	{}
+};
 
-	for (;;) {
-		unsigned long value;
-		char *eq, *comma;
-
-		/* Option limit */
-		comma = strchr(opts, ',');
-		if (comma)
-			*comma = 0;
-
-		/* Value limit */
-		eq = strchr(opts, '=');
-		if (unlikely(!eq)) {
-			pr_err("'=' missing in %s\n", opts);
-			return -EINVAL;
-		}
-		*eq = 0;
+static const struct fs_parameter_description ffs_fs_fs_parameters = {
+	.name		= "kAFS",
+	.specs		= ffs_fs_param_specs,
+};
 
-		/* Parse value */
-		if (kstrtoul(eq + 1, 0, &value)) {
-			pr_err("%s: invalid value: %s\n", opts, eq + 1);
-			return -EINVAL;
-		}
+static int ffs_fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct ffs_sb_fill_data *data = fc->fs_private;
+	struct fs_parse_result result;
+	int opt;
 
-		/* Interpret option */
-		switch (eq - opts) {
-		case 13:
-			if (!memcmp(opts, "no_disconnect", 13))
-				data->no_disconnect = !!value;
-			else
-				goto invalid;
-			break;
-		case 5:
-			if (!memcmp(opts, "rmode", 5))
-				data->root_mode  = (value & 0555) | S_IFDIR;
-			else if (!memcmp(opts, "fmode", 5))
-				data->perms.mode = (value & 0666) | S_IFREG;
-			else
-				goto invalid;
-			break;
+	ENTER();
 
-		case 4:
-			if (!memcmp(opts, "mode", 4)) {
-				data->root_mode  = (value & 0555) | S_IFDIR;
-				data->perms.mode = (value & 0666) | S_IFREG;
-			} else {
-				goto invalid;
-			}
-			break;
+	opt = fs_parse(fc, &ffs_fs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
 
-		case 3:
-			if (!memcmp(opts, "uid", 3)) {
-				data->perms.uid = make_kuid(current_user_ns(), value);
-				if (!uid_valid(data->perms.uid)) {
-					pr_err("%s: unmapped value: %lu\n", opts, value);
-					return -EINVAL;
-				}
-			} else if (!memcmp(opts, "gid", 3)) {
-				data->perms.gid = make_kgid(current_user_ns(), value);
-				if (!gid_valid(data->perms.gid)) {
-					pr_err("%s: unmapped value: %lu\n", opts, value);
-					return -EINVAL;
-				}
-			} else {
-				goto invalid;
-			}
-			break;
+	switch (opt) {
+	case Opt_no_disconnect:
+		data->no_disconnect = result.boolean;
+		break;
+	case Opt_rmode:
+		data->root_mode  = (result.uint_32 & 0555) | S_IFDIR;
+		break;
+	case Opt_fmode:
+		data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
+		break;
+	case Opt_mode:
+		data->root_mode  = (result.uint_32 & 0555) | S_IFDIR;
+		data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
+		break;
 
-		default:
-invalid:
-			pr_err("%s: invalid option\n", opts);
-			return -EINVAL;
-		}
+	case Opt_uid:
+		data->perms.uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(data->perms.uid))
+			goto unmapped_value;
+		break;
+	case Opt_gid:
+		data->perms.gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(data->perms.gid))
+			goto unmapped_value;
+		break;
 
-		/* Next iteration */
-		if (!comma)
-			break;
-		opts = comma + 1;
+	default:
+		return -ENOPARAM;
 	}
 
 	return 0;
-}
 
-/* "mount -t functionfs dev_name /dev/function" ends up here */
+unmapped_value:
+	return invalf(fc, "%s: unmapped value: %u", param->key, result.uint_32);
+}
 
-static struct dentry *
-ffs_fs_mount(struct file_system_type *t, int flags,
-	      const char *dev_name, void *opts)
-{
-	struct ffs_sb_fill_data data = {
-		.perms = {
-			.mode = S_IFREG | 0600,
-			.uid = GLOBAL_ROOT_UID,
-			.gid = GLOBAL_ROOT_GID,
-		},
-		.root_mode = S_IFDIR | 0500,
-		.no_disconnect = false,
-	};
-	struct dentry *rv;
-	int ret;
+/*
+ * Set up the superblock for a mount.
+ */
+static int ffs_fs_get_tree(struct fs_context *fc)
+{
+	struct ffs_sb_fill_data *ctx = fc->fs_private;
 	void *ffs_dev;
 	struct ffs_data	*ffs;
 
 	ENTER();
 
-	ret = ffs_fs_parse_opts(&data, opts);
-	if (unlikely(ret < 0))
-		return ERR_PTR(ret);
+	if (!fc->source)
+		return invalf(fc, "No source specified");
 
-	ffs = ffs_data_new(dev_name);
+	ffs = ffs_data_new(fc->source);
 	if (unlikely(!ffs))
-		return ERR_PTR(-ENOMEM);
-	ffs->file_perms = data.perms;
-	ffs->no_disconnect = data.no_disconnect;
+		return -ENOMEM;
+	ffs->file_perms = ctx->perms;
+	ffs->no_disconnect = ctx->no_disconnect;
 
-	ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
+	ffs->dev_name = kstrdup(fc->source, GFP_KERNEL);
 	if (unlikely(!ffs->dev_name)) {
 		ffs_data_put(ffs);
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
 	}
 
-	ffs_dev = ffs_acquire_dev(dev_name);
+	ffs_dev = ffs_acquire_dev(ffs->dev_name);
 	if (IS_ERR(ffs_dev)) {
 		ffs_data_put(ffs);
-		return ERR_CAST(ffs_dev);
+		return PTR_ERR(ffs_dev);
 	}
+
 	ffs->private_data = ffs_dev;
-	data.ffs_data = ffs;
+	ctx->ffs_data = ffs;
+	return vfs_get_super(fc, vfs_get_independent_super, ffs_sb_fill);
+}
+
+static void ffs_fs_free_fc(struct fs_context *fc)
+{
+	struct ffs_sb_fill_data *ctx = fc->fs_private;
+
+	if (ctx) {
+		if (ctx->ffs_data) {
+			ffs_release_dev(ctx->ffs_data);
+			ffs_data_put(ctx->ffs_data);
+		}
 
-	rv = mount_nodev(t, flags, &data, ffs_sb_fill);
-	if (IS_ERR(rv) && data.ffs_data) {
-		ffs_release_dev(data.ffs_data);
-		ffs_data_put(data.ffs_data);
+		kfree(ctx);
 	}
-	return rv;
+}
+
+static const struct fs_context_operations ffs_fs_context_ops = {
+	.free		= ffs_fs_free_fc,
+	.parse_param	= ffs_fs_parse_param,
+	.get_tree	= ffs_fs_get_tree,
+};
+
+static int ffs_fs_init_fs_context(struct fs_context *fc)
+{
+	struct ffs_sb_fill_data *ctx;
+
+	ctx = kzalloc(sizeof(struct ffs_sb_fill_data), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->perms.mode = S_IFREG | 0600;
+	ctx->perms.uid = GLOBAL_ROOT_UID;
+	ctx->perms.gid = GLOBAL_ROOT_GID;
+	ctx->root_mode = S_IFDIR | 0500;
+	ctx->no_disconnect = false;
+
+	fc->fs_private = ctx;
+	fc->ops = &ffs_fs_context_ops;
+	return 0;
 }
 
 static void
@@ -1640,7 +1646,8 @@ ffs_fs_kill_sb(struct super_block *sb)
 static struct file_system_type ffs_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "functionfs",
-	.mount		= ffs_fs_mount,
+	.init_fs_context = ffs_fs_init_fs_context,
+	.parameters	= &ffs_fs_fs_parameters,
 	.kill_sb	= ffs_fs_kill_sb,
 };
 MODULE_ALIAS_FS("functionfs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 54/68] vfs: Add a single-or-reconfig keying to vfs_get_super()
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (52 preceding siblings ...)
  2019-03-27 23:47 ` [RFC PATCH 53/68] vfs: Convert functionfs " David Howells
@ 2019-03-27 23:47 ` David Howells
  2019-03-27 23:47 ` [RFC PATCH 55/68] vfs: Convert debugfs to use the new mount API David Howells
                   ` (13 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:47 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Add an additional keying mode to vfs_get_super() to indicate that only a
single superblock should exist in the system, and that, if it does, further
mounts should invoke reconfiguration upon it.

This allows mount_single() to be replaced.

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

 fs/super.c                 |   24 +++++++++++++++++-------
 include/linux/fs_context.h |    1 +
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index 6cb57ad31e6f..60c9caeae3c7 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1175,9 +1175,11 @@ int vfs_get_super(struct fs_context *fc,
 {
 	int (*test)(struct super_block *, struct fs_context *);
 	struct super_block *sb;
+	int err;
 
 	switch (keying) {
 	case vfs_get_single_super:
+	case vfs_get_single_reconf_super:
 		test = test_single_super;
 		break;
 	case vfs_get_keyed_super:
@@ -1195,18 +1197,26 @@ int vfs_get_super(struct fs_context *fc,
 		return PTR_ERR(sb);
 
 	if (!sb->s_root) {
-		int err = fill_super(sb, fc);
-		if (err) {
-			deactivate_locked_super(sb);
-			return err;
-		}
+		err = fill_super(sb, fc);
+		if (err)
+			goto error;
 
 		sb->s_flags |= SB_ACTIVE;
+		fc->root = dget(sb->s_root);
+	} else {
+		fc->root = dget(sb->s_root);
+		if (keying == vfs_get_single_reconf_super) {
+			err = reconfigure_super(fc);
+			if (err < 0)
+				goto error;
+		}
 	}
 
-	BUG_ON(fc->root);
-	fc->root = dget(sb->s_root);
 	return 0;
+
+error:
+	deactivate_locked_super(sb);
+	return err;
 }
 EXPORT_SYMBOL(vfs_get_super);
 
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 7c7eefb35cc3..3b1da9b2db91 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -138,6 +138,7 @@ extern int vfs_init_pseudo_fs_context(struct fs_context *fc,
  */
 enum vfs_get_super_keying {
 	vfs_get_single_super,	/* Only one such superblock may exist */
+	vfs_get_single_reconf_super, /* As above, but reconfigure if it exists */
 	vfs_get_keyed_super,	/* Superblocks with different s_fs_info keys may exist */
 	vfs_get_independent_super, /* Multiple independent superblocks may exist */
 };


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 55/68] vfs: Convert debugfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (53 preceding siblings ...)
  2019-03-27 23:47 ` [RFC PATCH 54/68] vfs: Add a single-or-reconfig keying to vfs_get_super() David Howells
@ 2019-03-27 23:47 ` David Howells
  2019-03-27 23:47 ` [RFC PATCH 56/68] vfs: Convert tracefs " David Howells
                   ` (12 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:47 UTC (permalink / raw)
  To: viro
  Cc: Greg Kroah-Hartman, Rafael J. Wysocki, linux-fsdevel,
	linux-kernel, dhowells

Convert the debugfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
cc: "Rafael J. Wysocki" <rafael@kernel.org>
---

 fs/debugfs/inode.c |  186 ++++++++++++++++++++++++----------------------------
 1 file changed, 85 insertions(+), 101 deletions(-)

diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 95b5e78c22b1..951939bf25ce 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -12,6 +12,8 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/pagemap.h>
 #include <linux/init.h>
 #include <linux/kobject.h>
@@ -20,7 +22,6 @@
 #include <linux/fsnotify.h>
 #include <linux/string.h>
 #include <linux/seq_file.h>
-#include <linux/parser.h>
 #include <linux/magic.h>
 #include <linux/slab.h>
 
@@ -43,73 +44,62 @@ static struct inode *debugfs_get_inode(struct super_block *sb)
 	return inode;
 }
 
-struct debugfs_mount_opts {
-	kuid_t uid;
-	kgid_t gid;
-	umode_t mode;
-};
-
 enum {
-	Opt_uid,
 	Opt_gid,
 	Opt_mode,
-	Opt_err
+	Opt_uid,
+};
+
+static const struct fs_parameter_spec debugfs_param_specs[] = {
+	fsparam_u32	("gid",				Opt_gid),
+	fsparam_u32oct	("mode",			Opt_mode),
+	fsparam_u32	("uid",				Opt_uid),
+	{}
 };
 
-static const match_table_t tokens = {
-	{Opt_uid, "uid=%u"},
-	{Opt_gid, "gid=%u"},
-	{Opt_mode, "mode=%o"},
-	{Opt_err, NULL}
+static const struct fs_parameter_description debugfs_fs_parameters = {
+	.name		= "debugfs",
+	.specs		= debugfs_param_specs,
 };
 
 struct debugfs_fs_info {
-	struct debugfs_mount_opts mount_opts;
+	kuid_t uid;
+	kgid_t gid;
+	umode_t mode;
 };
 
-static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts)
+static int debugfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	substring_t args[MAX_OPT_ARGS];
-	int option;
-	int token;
+	struct debugfs_fs_info *opts = fc->s_fs_info;
+	struct fs_parse_result result;
 	kuid_t uid;
 	kgid_t gid;
-	char *p;
-
-	opts->mode = DEBUGFS_DEFAULT_MODE;
-
-	while ((p = strsep(&data, ",")) != NULL) {
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_uid:
-			if (match_int(&args[0], &option))
-				return -EINVAL;
-			uid = make_kuid(current_user_ns(), option);
-			if (!uid_valid(uid))
-				return -EINVAL;
-			opts->uid = uid;
-			break;
-		case Opt_gid:
-			if (match_int(&args[0], &option))
-				return -EINVAL;
-			gid = make_kgid(current_user_ns(), option);
-			if (!gid_valid(gid))
-				return -EINVAL;
-			opts->gid = gid;
-			break;
-		case Opt_mode:
-			if (match_octal(&args[0], &option))
-				return -EINVAL;
-			opts->mode = option & S_IALLUGO;
-			break;
+	int opt;
+
+	opt = fs_parse(fc, &debugfs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_uid:
+		uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(uid))
+			return invalf(fc, "Unknown uid");
+		opts->uid = uid;
+		break;
+	case Opt_gid:
+		gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(gid))
+			return invalf(fc, "Unknown gid");
+		opts->gid = gid;
+		break;
+	case Opt_mode:
+		opts->mode = result.uint_32 & S_IALLUGO;
+		break;
 		/*
 		 * We might like to report bad mount options here;
 		 * but traditionally debugfs has ignored all mount options
 		 */
-		}
 	}
 
 	return 0;
@@ -119,46 +109,36 @@ static int debugfs_apply_options(struct super_block *sb)
 {
 	struct debugfs_fs_info *fsi = sb->s_fs_info;
 	struct inode *inode = d_inode(sb->s_root);
-	struct debugfs_mount_opts *opts = &fsi->mount_opts;
 
 	inode->i_mode &= ~S_IALLUGO;
-	inode->i_mode |= opts->mode;
+	inode->i_mode |= fsi->mode;
 
-	inode->i_uid = opts->uid;
-	inode->i_gid = opts->gid;
+	inode->i_uid = fsi->uid;
+	inode->i_gid = fsi->gid;
 
 	return 0;
 }
 
-static int debugfs_remount(struct super_block *sb, int *flags, char *data)
+static int debugfs_reconfigure(struct fs_context *fc)
 {
-	int err;
-	struct debugfs_fs_info *fsi = sb->s_fs_info;
+	struct super_block *sb = fc->root->d_sb;
 
 	sync_filesystem(sb);
-	err = debugfs_parse_options(data, &fsi->mount_opts);
-	if (err)
-		goto fail;
-
-	debugfs_apply_options(sb);
-
-fail:
-	return err;
+	return debugfs_apply_options(sb);
 }
 
 static int debugfs_show_options(struct seq_file *m, struct dentry *root)
 {
 	struct debugfs_fs_info *fsi = root->d_sb->s_fs_info;
-	struct debugfs_mount_opts *opts = &fsi->mount_opts;
 
-	if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
+	if (!uid_eq(fsi->uid, GLOBAL_ROOT_UID))
 		seq_printf(m, ",uid=%u",
-			   from_kuid_munged(&init_user_ns, opts->uid));
-	if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
+			   from_kuid_munged(&init_user_ns, fsi->uid));
+	if (!gid_eq(fsi->gid, GLOBAL_ROOT_GID))
 		seq_printf(m, ",gid=%u",
-			   from_kgid_munged(&init_user_ns, opts->gid));
-	if (opts->mode != DEBUGFS_DEFAULT_MODE)
-		seq_printf(m, ",mode=%o", opts->mode);
+			   from_kgid_munged(&init_user_ns, fsi->gid));
+	if (fsi->mode != DEBUGFS_DEFAULT_MODE)
+		seq_printf(m, ",mode=%o", fsi->mode);
 
 	return 0;
 }
@@ -173,7 +153,6 @@ static void debugfs_evict_inode(struct inode *inode)
 
 static const struct super_operations debugfs_super_operations = {
 	.statfs		= simple_statfs,
-	.remount_fs	= debugfs_remount,
 	.show_options	= debugfs_show_options,
 	.evict_inode	= debugfs_evict_inode,
 };
@@ -199,51 +178,57 @@ static const struct dentry_operations debugfs_dops = {
 	.d_automount = debugfs_automount,
 };
 
-static int debug_fill_super(struct super_block *sb, void *data, int silent)
+static int debugfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static const struct tree_descr debug_files[] = {{""}};
-	struct debugfs_fs_info *fsi;
 	int err;
 
-	fsi = kzalloc(sizeof(struct debugfs_fs_info), GFP_KERNEL);
-	sb->s_fs_info = fsi;
-	if (!fsi) {
-		err = -ENOMEM;
-		goto fail;
-	}
-
-	err = debugfs_parse_options(data, &fsi->mount_opts);
+	err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
 	if (err)
-		goto fail;
-
-	err  =  simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
-	if (err)
-		goto fail;
+		return err;
 
 	sb->s_op = &debugfs_super_operations;
 	sb->s_d_op = &debugfs_dops;
 
-	debugfs_apply_options(sb);
+	return debugfs_apply_options(sb);
+}
 
-	return 0;
+static int debugfs_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_single_reconf_super,
+			     debugfs_fill_super);
+}
 
-fail:
-	kfree(fsi);
-	sb->s_fs_info = NULL;
-	return err;
+static void debugfs_free_fc(struct fs_context *fc)
+{
+	kfree(fc->s_fs_info);
 }
 
-static struct dentry *debug_mount(struct file_system_type *fs_type,
-			int flags, const char *dev_name,
-			void *data)
+static const struct fs_context_operations debugfs_context_ops = {
+	.free		= debugfs_free_fc,
+	.parse_param	= debugfs_parse_param,
+	.get_tree	= debugfs_get_tree,
+	.reconfigure	= debugfs_reconfigure,
+};
+
+static int debugfs_init_fs_context(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, debug_fill_super);
+	struct debugfs_fs_info *fsi;
+
+	fsi = kzalloc(sizeof(struct debugfs_fs_info), GFP_KERNEL);
+	if (!fsi)
+		return -ENOMEM;
+
+	fc->s_fs_info = fsi;
+	fc->ops = &debugfs_context_ops;
+	return 0;
 }
 
 static struct file_system_type debug_fs_type = {
 	.owner =	THIS_MODULE,
 	.name =		"debugfs",
-	.mount =	debug_mount,
+	.init_fs_context = debugfs_init_fs_context,
+	.parameters	= &debugfs_fs_parameters,
 	.kill_sb =	kill_litter_super,
 };
 MODULE_ALIAS_FS("debugfs");
@@ -862,4 +847,3 @@ static int __init debugfs_init(void)
 	return retval;
 }
 core_initcall(debugfs_init);
-


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 56/68] vfs: Convert tracefs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (54 preceding siblings ...)
  2019-03-27 23:47 ` [RFC PATCH 55/68] vfs: Convert debugfs to use the new mount API David Howells
@ 2019-03-27 23:47 ` David Howells
  2019-03-27 23:47 ` [RFC PATCH 57/68] vfs: Convert pstore " David Howells
                   ` (11 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:47 UTC (permalink / raw)
  To: viro
  Cc: Steven Rostedt, Greg Kroah-Hartman, linux-fsdevel, linux-kernel,
	dhowells

Convert the tracefs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Steven Rostedt <rostedt@goodmis.org>
cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---

 fs/tracefs/inode.c |  180 ++++++++++++++++++++++++----------------------------
 1 file changed, 83 insertions(+), 97 deletions(-)

diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
index 7098c49f3693..7ba64f38931f 100644
--- a/fs/tracefs/inode.c
+++ b/fs/tracefs/inode.c
@@ -16,12 +16,13 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/kobject.h>
 #include <linux/namei.h>
 #include <linux/tracefs.h>
 #include <linux/fsnotify.h>
 #include <linux/seq_file.h>
-#include <linux/parser.h>
 #include <linux/magic.h>
 #include <linux/slab.h>
 
@@ -138,73 +139,62 @@ static struct inode *tracefs_get_inode(struct super_block *sb)
 	return inode;
 }
 
-struct tracefs_mount_opts {
+struct tracefs_fs_info {
 	kuid_t uid;
 	kgid_t gid;
 	umode_t mode;
 };
 
 enum {
-	Opt_uid,
 	Opt_gid,
 	Opt_mode,
-	Opt_err
+	Opt_uid,
 };
 
-static const match_table_t tokens = {
-	{Opt_uid, "uid=%u"},
-	{Opt_gid, "gid=%u"},
-	{Opt_mode, "mode=%o"},
-	{Opt_err, NULL}
+static const struct fs_parameter_spec tracefs_param_specs[] = {
+	fsparam_u32	("gid",				Opt_gid),
+	fsparam_u32oct	("mode",			Opt_mode),
+	fsparam_u32	("uid",				Opt_uid),
+	{}
 };
 
-struct tracefs_fs_info {
-	struct tracefs_mount_opts mount_opts;
+static const struct fs_parameter_description tracefs_fs_parameters = {
+	.name		= "tracefs",
+	.specs		= tracefs_param_specs,
 };
 
-static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts)
+static int tracefs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	substring_t args[MAX_OPT_ARGS];
-	int option;
-	int token;
+	struct tracefs_fs_info *opts = fc->s_fs_info;
+	struct fs_parse_result result;
 	kuid_t uid;
 	kgid_t gid;
-	char *p;
-
-	opts->mode = TRACEFS_DEFAULT_MODE;
-
-	while ((p = strsep(&data, ",")) != NULL) {
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_uid:
-			if (match_int(&args[0], &option))
-				return -EINVAL;
-			uid = make_kuid(current_user_ns(), option);
-			if (!uid_valid(uid))
-				return -EINVAL;
-			opts->uid = uid;
-			break;
-		case Opt_gid:
-			if (match_int(&args[0], &option))
-				return -EINVAL;
-			gid = make_kgid(current_user_ns(), option);
-			if (!gid_valid(gid))
-				return -EINVAL;
-			opts->gid = gid;
-			break;
-		case Opt_mode:
-			if (match_octal(&args[0], &option))
-				return -EINVAL;
-			opts->mode = option & S_IALLUGO;
-			break;
+	int opt;
+
+	opt = fs_parse(fc, &tracefs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_uid:
+		uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(uid))
+			return invalf(fc, "Unknown uid");
+		opts->uid = uid;
+		break;
+	case Opt_gid:
+		gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(gid))
+			return invalf(fc, "Unknown gid");
+		opts->gid = gid;
+		break;
+	case Opt_mode:
+		opts->mode = result.uint_32 & S_IALLUGO;
+		break;
 		/*
 		 * We might like to report bad mount options here;
 		 * but traditionally tracefs has ignored all mount options
 		 */
-		}
 	}
 
 	return 0;
@@ -214,100 +204,96 @@ static int tracefs_apply_options(struct super_block *sb)
 {
 	struct tracefs_fs_info *fsi = sb->s_fs_info;
 	struct inode *inode = sb->s_root->d_inode;
-	struct tracefs_mount_opts *opts = &fsi->mount_opts;
 
 	inode->i_mode &= ~S_IALLUGO;
-	inode->i_mode |= opts->mode;
+	inode->i_mode |= fsi->mode;
 
-	inode->i_uid = opts->uid;
-	inode->i_gid = opts->gid;
+	inode->i_uid = fsi->uid;
+	inode->i_gid = fsi->gid;
 
 	return 0;
 }
 
-static int tracefs_remount(struct super_block *sb, int *flags, char *data)
+static int tracefs_reconfigure(struct fs_context *fc)
 {
-	int err;
-	struct tracefs_fs_info *fsi = sb->s_fs_info;
+	struct super_block *sb = fc->root->d_sb;
 
 	sync_filesystem(sb);
-	err = tracefs_parse_options(data, &fsi->mount_opts);
-	if (err)
-		goto fail;
-
-	tracefs_apply_options(sb);
-
-fail:
-	return err;
+	return tracefs_apply_options(sb);
 }
 
 static int tracefs_show_options(struct seq_file *m, struct dentry *root)
 {
 	struct tracefs_fs_info *fsi = root->d_sb->s_fs_info;
-	struct tracefs_mount_opts *opts = &fsi->mount_opts;
 
-	if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
+	if (!uid_eq(fsi->uid, GLOBAL_ROOT_UID))
 		seq_printf(m, ",uid=%u",
-			   from_kuid_munged(&init_user_ns, opts->uid));
-	if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
+			   from_kuid_munged(&init_user_ns, fsi->uid));
+	if (!gid_eq(fsi->gid, GLOBAL_ROOT_GID))
 		seq_printf(m, ",gid=%u",
-			   from_kgid_munged(&init_user_ns, opts->gid));
-	if (opts->mode != TRACEFS_DEFAULT_MODE)
-		seq_printf(m, ",mode=%o", opts->mode);
+			   from_kgid_munged(&init_user_ns, fsi->gid));
+	if (fsi->mode != TRACEFS_DEFAULT_MODE)
+		seq_printf(m, ",mode=%o", fsi->mode);
 
 	return 0;
 }
 
 static const struct super_operations tracefs_super_operations = {
 	.statfs		= simple_statfs,
-	.remount_fs	= tracefs_remount,
 	.show_options	= tracefs_show_options,
 };
 
-static int trace_fill_super(struct super_block *sb, void *data, int silent)
+static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static const struct tree_descr trace_files[] = {{""}};
-	struct tracefs_fs_info *fsi;
 	int err;
 
-	fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL);
-	sb->s_fs_info = fsi;
-	if (!fsi) {
-		err = -ENOMEM;
-		goto fail;
-	}
-
-	err = tracefs_parse_options(data, &fsi->mount_opts);
+	err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files);
 	if (err)
-		goto fail;
-
-	err  =  simple_fill_super(sb, TRACEFS_MAGIC, trace_files);
-	if (err)
-		goto fail;
+		return err;
 
 	sb->s_op = &tracefs_super_operations;
+	return tracefs_apply_options(sb);
+}
 
-	tracefs_apply_options(sb);
-
-	return 0;
+static int tracefs_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_single_reconf_super,
+			     tracefs_fill_super);
+}
 
-fail:
-	kfree(fsi);
-	sb->s_fs_info = NULL;
-	return err;
+static void tracefs_free_fc(struct fs_context *fc)
+{
+	kfree(fc->s_fs_info);
 }
 
-static struct dentry *trace_mount(struct file_system_type *fs_type,
-			int flags, const char *dev_name,
-			void *data)
+static const struct fs_context_operations tracefs_context_ops = {
+	.free		= tracefs_free_fc,
+	.parse_param	= tracefs_parse_param,
+	.get_tree	= tracefs_get_tree,
+	.reconfigure	= tracefs_reconfigure,
+};
+
+static int tracefs_init_fs_context(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, trace_fill_super);
+	struct tracefs_fs_info *fsi;
+
+	fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL);
+	if (!fsi)
+		return -ENOMEM;
+
+	fsi->mode = TRACEFS_DEFAULT_MODE;
+
+	fc->s_fs_info = fsi;
+	fc->ops = &tracefs_context_ops;
+	return 0;
 }
 
 static struct file_system_type trace_fs_type = {
 	.owner =	THIS_MODULE,
 	.name =		"tracefs",
-	.mount =	trace_mount,
+	.init_fs_context = tracefs_init_fs_context,
+	.parameters	= &tracefs_fs_parameters,
 	.kill_sb =	kill_litter_super,
 };
 MODULE_ALIAS_FS("tracefs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 57/68] vfs: Convert pstore to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (55 preceding siblings ...)
  2019-03-27 23:47 ` [RFC PATCH 56/68] vfs: Convert tracefs " David Howells
@ 2019-03-27 23:47 ` David Howells
  2019-03-28  0:24   ` Kees Cook
  2019-03-27 23:47 ` [RFC PATCH 58/68] hypfs: Fix error number left in struct pointer member David Howells
                   ` (10 subsequent siblings)
  67 siblings, 1 reply; 87+ messages in thread
From: David Howells @ 2019-03-27 23:47 UTC (permalink / raw)
  To: viro
  Cc: Kees Cook, Anton Vorontsov, Colin Cross, Tony Luck,
	linux-fsdevel, linux-kernel, dhowells

Convert the pstore filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Kees Cook <keescook@chromium.org>
cc: Anton Vorontsov <anton@enomsg.org>
cc: Colin Cross <ccross@android.com>
cc: Tony Luck <tony.luck@intel.com>
---

 fs/pstore/inode.c |  109 +++++++++++++++++++++++++++++++++++------------------
 1 file changed, 71 insertions(+), 38 deletions(-)

diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index c60ee46f3e39..bb4e4cabd11f 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -19,6 +19,8 @@
 
 #include <linux/module.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/fsnotify.h>
 #include <linux/pagemap.h>
 #include <linux/highmem.h>
@@ -26,10 +28,8 @@
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/string.h>
-#include <linux/mount.h>
 #include <linux/seq_file.h>
 #include <linux/ramfs.h>
-#include <linux/parser.h>
 #include <linux/sched.h>
 #include <linux/magic.h>
 #include <linux/pstore.h>
@@ -227,38 +227,48 @@ static struct inode *pstore_get_inode(struct super_block *sb)
 	return inode;
 }
 
+struct pstore_fs_context {
+	unsigned int	kmsg_bytes;
+};
+
 enum {
-	Opt_kmsg_bytes, Opt_err
+	Opt_kmsg_bytes,
 };
 
-static const match_table_t tokens = {
-	{Opt_kmsg_bytes, "kmsg_bytes=%u"},
-	{Opt_err, NULL}
+static const struct fs_parameter_spec pstore_param_specs[] = {
+	fsparam_u32	("kmsg_bytes",		Opt_kmsg_bytes),
+	{}
 };
 
-static void parse_options(char *options)
-{
-	char		*p;
-	substring_t	args[MAX_OPT_ARGS];
-	int		option;
+static const struct fs_parameter_description pstore_fs_parameters = {
+	.name		= "pstore",
+	.specs		= pstore_param_specs,
+};
 
-	if (!options)
-		return;
+static int pstore_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct pstore_fs_context *ctx = fc->fs_private;
+	struct fs_parse_result result;
+	int opt;
+
+	opt = fs_parse(fc, &pstore_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_kmsg_bytes:
+		ctx->kmsg_bytes = result.uint_32;
+		break;
+	}
 
-	while ((p = strsep(&options, ",")) != NULL) {
-		int token;
+	return 0;
+}
 
-		if (!*p)
-			continue;
+static void pstore_apply_param(struct fs_context *fc)
+{
+	struct pstore_fs_context *ctx = fc->fs_private;
 
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_kmsg_bytes:
-			if (!match_int(&args[0], &option))
-				pstore_set_kmsg_bytes(option);
-			break;
-		}
-	}
+	pstore_set_kmsg_bytes(ctx->kmsg_bytes);
 }
 
 /*
@@ -271,11 +281,10 @@ static int pstore_show_options(struct seq_file *m, struct dentry *root)
 	return 0;
 }
 
-static int pstore_remount(struct super_block *sb, int *flags, char *data)
+static int pstore_reconfigure(struct fs_context *fc)
 {
-	sync_filesystem(sb);
-	parse_options(data);
-
+	sync_filesystem(fc->root->d_sb);
+	pstore_apply_param(fc);
 	return 0;
 }
 
@@ -283,7 +292,6 @@ static const struct super_operations pstore_ops = {
 	.statfs		= simple_statfs,
 	.drop_inode	= generic_delete_inode,
 	.evict_inode	= pstore_evict_inode,
-	.remount_fs	= pstore_remount,
 	.show_options	= pstore_show_options,
 };
 
@@ -389,12 +397,10 @@ void pstore_get_records(int quiet)
 	inode_unlock(d_inode(root));
 }
 
-static int pstore_fill_super(struct super_block *sb, void *data, int silent)
+static int pstore_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct inode *inode;
 
-	pstore_sb = sb;
-
 	sb->s_maxbytes		= MAX_LFS_FILESIZE;
 	sb->s_blocksize		= PAGE_SIZE;
 	sb->s_blocksize_bits	= PAGE_SHIFT;
@@ -402,7 +408,8 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
 	sb->s_op		= &pstore_ops;
 	sb->s_time_gran		= 1;
 
-	parse_options(data);
+	pstore_sb = sb;
+	pstore_apply_param(fc);
 
 	inode = pstore_get_inode(sb);
 	if (inode) {
@@ -420,10 +427,35 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
-static struct dentry *pstore_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int pstore_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_single_reconf_super,
+			     pstore_fill_super);
+}
+
+static void pstore_free_fc(struct fs_context *fc)
 {
-	return mount_single(fs_type, flags, data, pstore_fill_super);
+	kfree(fc->fs_private);
+}
+
+static const struct fs_context_operations pstore_context_ops = {
+	.free		= pstore_free_fc,
+	.parse_param	= pstore_parse_param,
+	.get_tree	= pstore_get_tree,
+	.reconfigure	= pstore_reconfigure,
+};
+
+static int pstore_init_fs_context(struct fs_context *fc)
+{
+	struct pstore_fs_context *ctx;
+
+	ctx = kzalloc(sizeof(struct pstore_fs_context), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	fc->fs_private = ctx;
+	fc->ops = &pstore_context_ops;
+	return 0;
 }
 
 static void pstore_kill_sb(struct super_block *sb)
@@ -435,7 +467,8 @@ static void pstore_kill_sb(struct super_block *sb)
 static struct file_system_type pstore_fs_type = {
 	.owner          = THIS_MODULE,
 	.name		= "pstore",
-	.mount		= pstore_mount,
+	.init_fs_context = pstore_init_fs_context,
+	.parameters	= &pstore_fs_parameters,
 	.kill_sb	= pstore_kill_sb,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 58/68] hypfs: Fix error number left in struct pointer member
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (56 preceding siblings ...)
  2019-03-27 23:47 ` [RFC PATCH 57/68] vfs: Convert pstore " David Howells
@ 2019-03-27 23:47 ` David Howells
  2019-03-27 23:48 ` [RFC PATCH 59/68] vfs: Convert hypfs to use the new mount API David Howells
                   ` (9 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:47 UTC (permalink / raw)
  To: viro
  Cc: Martin Schwidefsky, Heiko Carstens, linux-s390, linux-fsdevel,
	linux-kernel, dhowells

In hypfs_fill_super(), if hypfs_create_update_file() fails,
sbi->update_file is left holding an error number.  This is passed to
hypfs_kill_super() which doesn't check for this.

Fix this by not setting sbi->update_value until after we've checked for
error.

Fixes: 24bbb1faf3f0 ("[PATCH] s390_hypfs filesystem")
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
cc: Heiko Carstens <heiko.carstens@de.ibm.com>
cc: linux-s390@vger.kernel.org
---

 arch/s390/hypfs/inode.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index ccad1398abd4..b5cfcad953c2 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -269,7 +269,7 @@ static int hypfs_show_options(struct seq_file *s, struct dentry *root)
 static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
 {
 	struct inode *root_inode;
-	struct dentry *root_dentry;
+	struct dentry *root_dentry, *update_file;
 	int rc = 0;
 	struct hypfs_sb_info *sbi;
 
@@ -300,9 +300,10 @@ static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
 		rc = hypfs_diag_create_files(root_dentry);
 	if (rc)
 		return rc;
-	sbi->update_file = hypfs_create_update_file(root_dentry);
-	if (IS_ERR(sbi->update_file))
-		return PTR_ERR(sbi->update_file);
+	update_file = hypfs_create_update_file(root_dentry);
+	if (IS_ERR(update_file))
+		return PTR_ERR(update_file);
+	sbi->update_file = update_file;
 	hypfs_update_update(sb);
 	pr_info("Hypervisor filesystem mounted\n");
 	return 0;


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 59/68] vfs: Convert hypfs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (57 preceding siblings ...)
  2019-03-27 23:47 ` [RFC PATCH 58/68] hypfs: Fix error number left in struct pointer member David Howells
@ 2019-03-27 23:48 ` David Howells
  2019-03-27 23:48 ` [RFC PATCH 60/68] vfs: Convert spufs " David Howells
                   ` (8 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:48 UTC (permalink / raw)
  To: viro
  Cc: Martin Schwidefsky, Heiko Carstens, linux-s390, linux-fsdevel,
	linux-kernel, dhowells

Convert the hypfs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
cc: Heiko Carstens <heiko.carstens@de.ibm.com>
cc: linux-s390@vger.kernel.org
---

 arch/s390/hypfs/inode.c |  128 ++++++++++++++++++++++++++---------------------
 1 file changed, 70 insertions(+), 58 deletions(-)

diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index b5cfcad953c2..d1b5f75334af 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -12,17 +12,17 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/namei.h>
 #include <linux/vfs.h>
 #include <linux/slab.h>
 #include <linux/pagemap.h>
 #include <linux/time.h>
-#include <linux/parser.h>
 #include <linux/sysfs.h>
 #include <linux/init.h>
 #include <linux/kobject.h>
 #include <linux/seq_file.h>
-#include <linux/mount.h>
 #include <linux/uio.h>
 #include <asm/ebcdic.h>
 #include "hypfs.h"
@@ -207,52 +207,44 @@ static int hypfs_release(struct inode *inode, struct file *filp)
 	return 0;
 }
 
-enum { opt_uid, opt_gid, opt_err };
+enum { Opt_uid, Opt_gid, };
 
-static const match_table_t hypfs_tokens = {
-	{opt_uid, "uid=%u"},
-	{opt_gid, "gid=%u"},
-	{opt_err, NULL}
+static const struct fs_parameter_spec hypfs_param_specs[] = {
+	fsparam_u32("gid", Opt_gid),
+	fsparam_u32("uid", Opt_uid),
+	{}
 };
 
-static int hypfs_parse_options(char *options, struct super_block *sb)
+static const struct fs_parameter_description hypfs_fs_parameters = {
+	.name		= "hypfs",
+	.specs		= hypfs_param_specs,
+};
+
+static int hypfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	char *str;
-	substring_t args[MAX_OPT_ARGS];
+	struct hypfs_sb_info *hypfs_info = fc->s_fs_info;
+	struct fs_parse_result result;
 	kuid_t uid;
 	kgid_t gid;
-
-	if (!options)
-		return 0;
-	while ((str = strsep(&options, ",")) != NULL) {
-		int token, option;
-		struct hypfs_sb_info *hypfs_info = sb->s_fs_info;
-
-		if (!*str)
-			continue;
-		token = match_token(str, hypfs_tokens, args);
-		switch (token) {
-		case opt_uid:
-			if (match_int(&args[0], &option))
-				return -EINVAL;
-			uid = make_kuid(current_user_ns(), option);
-			if (!uid_valid(uid))
-				return -EINVAL;
-			hypfs_info->uid = uid;
-			break;
-		case opt_gid:
-			if (match_int(&args[0], &option))
-				return -EINVAL;
-			gid = make_kgid(current_user_ns(), option);
-			if (!gid_valid(gid))
-				return -EINVAL;
-			hypfs_info->gid = gid;
-			break;
-		case opt_err:
-		default:
-			pr_err("%s is not a valid mount option\n", str);
-			return -EINVAL;
-		}
+	int opt;
+
+	opt = fs_parse(fc, &hypfs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_uid:
+		uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(uid))
+			return invalf(fc, "Unknown uid");
+		hypfs_info->uid = uid;
+		break;
+	case Opt_gid:
+		gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(gid))
+			return invalf(fc, "Unknown gid");
+		hypfs_info->gid = gid;
+		break;
 	}
 	return 0;
 }
@@ -266,26 +258,18 @@ static int hypfs_show_options(struct seq_file *s, struct dentry *root)
 	return 0;
 }
 
-static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
+static int hypfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
+	struct hypfs_sb_info *sbi = sb->s_fs_info;
 	struct inode *root_inode;
 	struct dentry *root_dentry, *update_file;
-	int rc = 0;
-	struct hypfs_sb_info *sbi;
+	int rc;
 
-	sbi = kzalloc(sizeof(struct hypfs_sb_info), GFP_KERNEL);
-	if (!sbi)
-		return -ENOMEM;
-	mutex_init(&sbi->lock);
-	sbi->uid = current_uid();
-	sbi->gid = current_gid();
-	sb->s_fs_info = sbi;
 	sb->s_blocksize = PAGE_SIZE;
 	sb->s_blocksize_bits = PAGE_SHIFT;
 	sb->s_magic = HYPFS_MAGIC;
 	sb->s_op = &hypfs_s_ops;
-	if (hypfs_parse_options(data, sb))
-		return -EINVAL;
+
 	root_inode = hypfs_make_inode(sb, S_IFDIR | 0755);
 	if (!root_inode)
 		return -ENOMEM;
@@ -309,10 +293,37 @@ static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
 	return 0;
 }
 
-static struct dentry *hypfs_mount(struct file_system_type *fst, int flags,
-			const char *devname, void *data)
+static int hypfs_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_single_super, hypfs_fill_super);
+}
+
+static void hypfs_free_fc(struct fs_context *fc)
 {
-	return mount_single(fst, flags, data, hypfs_fill_super);
+	kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations hypfs_context_ops = {
+	.free		= hypfs_free_fc,
+	.parse_param	= hypfs_parse_param,
+	.get_tree	= hypfs_get_tree,
+};
+
+static int hypfs_init_fs_context(struct fs_context *fc)
+{
+	struct hypfs_sb_info *sbi;
+
+	sbi = kzalloc(sizeof(struct hypfs_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+
+	mutex_init(&sbi->lock);
+	sbi->uid = current_uid();
+	sbi->gid = current_gid();
+
+	fc->s_fs_info = sbi;
+	fc->ops = &hypfs_context_ops;
+	return 0;
 }
 
 static void hypfs_kill_super(struct super_block *sb)
@@ -443,7 +454,8 @@ static const struct file_operations hypfs_file_ops = {
 static struct file_system_type hypfs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "s390_hypfs",
-	.mount		= hypfs_mount,
+	.init_fs_context = hypfs_init_fs_context,
+	.parameters	= &hypfs_fs_parameters,
 	.kill_sb	= hypfs_kill_super
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 60/68] vfs: Convert spufs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (58 preceding siblings ...)
  2019-03-27 23:48 ` [RFC PATCH 59/68] vfs: Convert hypfs to use the new mount API David Howells
@ 2019-03-27 23:48 ` David Howells
  2019-03-27 23:48 ` [RFC PATCH 61/68] vfs: Kill mount_single() David Howells
                   ` (7 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:48 UTC (permalink / raw)
  To: viro
  Cc: Jeremy Kerr, Arnd Bergmann, linuxppc-dev, linux-fsdevel,
	linux-kernel, dhowells

Convert the spufs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jeremy Kerr <jk@ozlabs.org>
cc: Arnd Bergmann <arnd@arndb.de>
cc: linuxppc-dev@lists.ozlabs.org
---

 arch/powerpc/platforms/cell/spufs/inode.c |  207 ++++++++++++++++-------------
 1 file changed, 116 insertions(+), 91 deletions(-)

diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index db329d4bf1c3..f951a7fe4e3c 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -23,6 +23,8 @@
 
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/fsnotify.h>
 #include <linux/backing-dev.h>
 #include <linux/init.h>
@@ -33,7 +35,6 @@
 #include <linux/pagemap.h>
 #include <linux/poll.h>
 #include <linux/slab.h>
-#include <linux/parser.h>
 
 #include <asm/prom.h>
 #include <asm/spu.h>
@@ -43,7 +44,7 @@
 #include "spufs.h"
 
 struct spufs_sb_info {
-	int debug;
+	bool debug;
 };
 
 static struct kmem_cache *spufs_inode_cache;
@@ -593,16 +594,27 @@ long spufs_create(struct path *path, struct dentry *dentry,
 }
 
 /* File system initialization */
+struct spufs_fs_context {
+	kuid_t	uid;
+	kgid_t	gid;
+	umode_t	mode;
+};
+
 enum {
-	Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err,
+	Opt_uid, Opt_gid, Opt_mode, Opt_debug,
+};
+
+static const struct fs_parameter_spec spufs_param_specs[] = {
+	fsparam_u32	("gid",				Opt_gid),
+	fsparam_u32oct	("mode",			Opt_mode),
+	fsparam_u32	("uid",				Opt_uid),
+	fsparam_flag	("debug",			Opt_debug),
+	{}
 };
 
-static const match_table_t spufs_tokens = {
-	{ Opt_uid,   "uid=%d" },
-	{ Opt_gid,   "gid=%d" },
-	{ Opt_mode,  "mode=%o" },
-	{ Opt_debug, "debug" },
-	{ Opt_err,    NULL  },
+static const struct fs_parameter_description spufs_fs_parameters = {
+	.name		= "spufs",
+	.specs		= spufs_param_specs,
 };
 
 static int spufs_show_options(struct seq_file *m, struct dentry *root)
@@ -623,47 +635,41 @@ static int spufs_show_options(struct seq_file *m, struct dentry *root)
 	return 0;
 }
 
-static int
-spufs_parse_options(struct super_block *sb, char *options, struct inode *root)
-{
-	char *p;
-	substring_t args[MAX_OPT_ARGS];
-
-	while ((p = strsep(&options, ",")) != NULL) {
-		int token, option;
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, spufs_tokens, args);
-		switch (token) {
-		case Opt_uid:
-			if (match_int(&args[0], &option))
-				return 0;
-			root->i_uid = make_kuid(current_user_ns(), option);
-			if (!uid_valid(root->i_uid))
-				return 0;
-			break;
-		case Opt_gid:
-			if (match_int(&args[0], &option))
-				return 0;
-			root->i_gid = make_kgid(current_user_ns(), option);
-			if (!gid_valid(root->i_gid))
-				return 0;
-			break;
-		case Opt_mode:
-			if (match_octal(&args[0], &option))
-				return 0;
-			root->i_mode = option | S_IFDIR;
-			break;
-		case Opt_debug:
-			spufs_get_sb_info(sb)->debug = 1;
-			break;
-		default:
-			return 0;
-		}
+static int spufs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct spufs_fs_context *ctx = fc->fs_private;
+	struct spufs_sb_info *sbi = fc->s_fs_info;
+	struct fs_parse_result result;
+	kuid_t uid;
+	kgid_t gid;
+	int opt;
+
+	opt = fs_parse(fc, &spufs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_uid:
+		uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(uid))
+			return invalf(fc, "Unknown uid");
+		ctx->uid = uid;
+		break;
+	case Opt_gid:
+		gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(gid))
+			return invalf(fc, "Unknown gid");
+		ctx->gid = gid;
+		break;
+	case Opt_mode:
+		ctx->mode = result.uint_32 & S_IALLUGO;
+		break;
+	case Opt_debug:
+		sbi->debug = true;
+		break;
 	}
-	return 1;
+
+	return 0;
 }
 
 static void spufs_exit_isolated_loader(void)
@@ -697,79 +703,98 @@ spufs_init_isolated_loader(void)
 	printk(KERN_INFO "spufs: SPU isolation mode enabled\n");
 }
 
-static int
-spufs_create_root(struct super_block *sb, void *data)
+static int spufs_create_root(struct super_block *sb, struct fs_context *fc)
 {
+	struct spufs_fs_context *ctx = fc->fs_private;
 	struct inode *inode;
-	int ret;
 
-	ret = -ENODEV;
 	if (!spu_management_ops)
-		goto out;
+		return -ENODEV;
 
-	ret = -ENOMEM;
-	inode = spufs_new_inode(sb, S_IFDIR | 0775);
+	inode = spufs_new_inode(sb, S_IFDIR | ctx->mode);
 	if (!inode)
-		goto out;
+		return -ENOMEM;
 
+	inode->i_uid = ctx->uid;
+	inode->i_gid = ctx->gid;
 	inode->i_op = &simple_dir_inode_operations;
 	inode->i_fop = &simple_dir_operations;
 	SPUFS_I(inode)->i_ctx = NULL;
 	inc_nlink(inode);
 
-	ret = -EINVAL;
-	if (!spufs_parse_options(sb, data, inode))
-		goto out_iput;
-
-	ret = -ENOMEM;
 	sb->s_root = d_make_root(inode);
 	if (!sb->s_root)
-		goto out;
-
+		return -ENOMEM;
 	return 0;
-out_iput:
-	iput(inode);
-out:
-	return ret;
 }
 
-static int
-spufs_fill_super(struct super_block *sb, void *data, int silent)
-{
-	struct spufs_sb_info *info;
-	static const struct super_operations s_ops = {
-		.alloc_inode = spufs_alloc_inode,
-		.destroy_inode = spufs_destroy_inode,
-		.statfs = simple_statfs,
-		.evict_inode = spufs_evict_inode,
-		.show_options = spufs_show_options,
-	};
-
-	info = kzalloc(sizeof(*info), GFP_KERNEL);
-	if (!info)
-		return -ENOMEM;
+static const struct super_operations spufs_ops = {
+	.alloc_inode	= spufs_alloc_inode,
+	.destroy_inode	= spufs_destroy_inode,
+	.statfs		= simple_statfs,
+	.evict_inode	= spufs_evict_inode,
+	.show_options	= spufs_show_options,
+};
 
+static int spufs_fill_super(struct super_block *sb, struct fs_context *fc)
+{
 	sb->s_maxbytes = MAX_LFS_FILESIZE;
 	sb->s_blocksize = PAGE_SIZE;
 	sb->s_blocksize_bits = PAGE_SHIFT;
 	sb->s_magic = SPUFS_MAGIC;
-	sb->s_op = &s_ops;
-	sb->s_fs_info = info;
+	sb->s_op = &spufs_ops;
 
-	return spufs_create_root(sb, data);
+	return spufs_create_root(sb, fc);
+}
+
+static int spufs_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_single_super, spufs_fill_super);
 }
 
-static struct dentry *
-spufs_mount(struct file_system_type *fstype, int flags,
-		const char *name, void *data)
+static void spufs_free_fc(struct fs_context *fc)
 {
-	return mount_single(fstype, flags, data, spufs_fill_super);
+	kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations spufs_context_ops = {
+	.free		= spufs_free_fc,
+	.parse_param	= spufs_parse_param,
+	.get_tree	= spufs_get_tree,
+};
+
+static int spufs_init_fs_context(struct fs_context *fc)
+{
+	struct spufs_fs_context *ctx;
+	struct spufs_sb_info *sbi;
+
+	ctx = kzalloc(sizeof(struct spufs_fs_context), GFP_KERNEL);
+	if (!ctx)
+		goto nomem;
+
+	sbi = kzalloc(sizeof(struct spufs_sb_info), GFP_KERNEL);
+	if (!sbi)
+		goto nomem_ctx;
+
+	ctx->uid = current_uid();
+	ctx->gid = current_gid();
+	ctx->mode = 0755;
+
+	fc->s_fs_info = sbi;
+	fc->ops = &spufs_context_ops;
+	return 0;
+
+nomem_ctx:
+	kfree(ctx);
+nomem:
+	return -ENOMEM;
 }
 
 static struct file_system_type spufs_type = {
 	.owner = THIS_MODULE,
 	.name = "spufs",
-	.mount = spufs_mount,
+	.init_fs_context = spufs_init_fs_context,
+	.parameters	= &spufs_fs_parameters,
 	.kill_sb = kill_litter_super,
 };
 MODULE_ALIAS_FS("spufs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 61/68] vfs: Kill mount_single()
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (59 preceding siblings ...)
  2019-03-27 23:48 ` [RFC PATCH 60/68] vfs: Convert spufs " David Howells
@ 2019-03-27 23:48 ` David Howells
  2019-03-27 23:48 ` [RFC PATCH 62/68] vfs: Convert coda to use the new mount API David Howells
                   ` (6 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:48 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Kill mount_single() as nothing now uses it.  It has been replaced by
vfs_get_super() keyed with vfs_get_single_super or
vfs_get_single_reconf_super.

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

 Documentation/filesystems/vfs.txt |    4 +--
 fs/super.c                        |   55 -------------------------------------
 include/linux/fs.h                |    3 --
 3 files changed, 2 insertions(+), 60 deletions(-)

diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 761c6fd24a53..6ef83fdfffdc 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -181,8 +181,8 @@ and provides a fill_super() callback instead. The generic variants are:
 
   mount_nodev: mount a filesystem that is not backed by a device
 
-  mount_single: mount a filesystem which shares the instance between
-  	all mounts
+  vfs_get_super: mount a filesystem with one of a number of superblock
+		 sharing options.
 
 A fill_super() callback implementation has the following arguments:
 
diff --git a/fs/super.c b/fs/super.c
index 60c9caeae3c7..b94c50288636 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1449,61 +1449,6 @@ struct dentry *mount_nodev(struct file_system_type *fs_type,
 }
 EXPORT_SYMBOL(mount_nodev);
 
-static int reconfigure_single(struct super_block *s,
-			      int flags, void *data)
-{
-	struct fs_context *fc;
-	int ret;
-
-	/* The caller really need to be passing fc down into mount_single(),
-	 * then a chunk of this can be removed.  [Bollocks -- AV]
-	 * Better yet, reconfiguration shouldn't happen, but rather the second
-	 * mount should be rejected if the parameters are not compatible.
-	 */
-	fc = fs_context_for_reconfigure(s->s_root, flags, MS_RMT_MASK);
-	if (IS_ERR(fc))
-		return PTR_ERR(fc);
-
-	ret = parse_monolithic_mount_data(fc, data);
-	if (ret < 0)
-		goto out;
-
-	ret = reconfigure_super(fc);
-out:
-	put_fs_context(fc);
-	return ret;
-}
-
-static int compare_single(struct super_block *s, void *p)
-{
-	return 1;
-}
-
-struct dentry *mount_single(struct file_system_type *fs_type,
-	int flags, void *data,
-	int (*fill_super)(struct super_block *, void *, int))
-{
-	struct super_block *s;
-	int error;
-
-	s = sget(fs_type, compare_single, set_anon_super, flags, NULL);
-	if (IS_ERR(s))
-		return ERR_CAST(s);
-	if (!s->s_root) {
-		error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
-		if (!error)
-			s->s_flags |= SB_ACTIVE;
-	} else {
-		error = reconfigure_single(s, flags, data);
-	}
-	if (unlikely(error)) {
-		deactivate_locked_super(s);
-		return ERR_PTR(error);
-	}
-	return dget(s->s_root);
-}
-EXPORT_SYMBOL(mount_single);
-
 /**
  * vfs_get_tree - Get the mountable root
  * @fc: The superblock configuration context.
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 646f4ccaeee9..5ca14600dcf6 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2206,9 +2206,6 @@ static inline struct dentry *mount_bdev(struct file_system_type *fs_type,
 	return ERR_PTR(-ENODEV);
 }
 #endif
-extern struct dentry *mount_single(struct file_system_type *fs_type,
-	int flags, void *data,
-	int (*fill_super)(struct super_block *, void *, int));
 extern struct dentry *mount_nodev(struct file_system_type *fs_type,
 	int flags, void *data,
 	int (*fill_super)(struct super_block *, void *, int));


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 62/68] vfs: Convert coda to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (60 preceding siblings ...)
  2019-03-27 23:48 ` [RFC PATCH 61/68] vfs: Kill mount_single() David Howells
@ 2019-03-27 23:48 ` David Howells
  2019-03-27 23:48 ` [RFC PATCH 63/68] vfs: Convert autofs " David Howells
                   ` (5 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:48 UTC (permalink / raw)
  To: viro; +Cc: Jan Harkes, coda, linux-fsdevel, linux-kernel, dhowells

Convert the coda filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Note this is slightly tricky as coda currently only has a binary mount data
interface.  This is handled through the parse_monolithic hook.

Also add a more conventional interface with a parameter named "fd" that
takes an fd that refers to a coda psdev, thereby specifying the index to
use.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jan Harkes <jaharkes@cs.cmu.edu>
cc: coda@cs.cmu.edu
---

 fs/coda/inode.c |  171 +++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 116 insertions(+), 55 deletions(-)

diff --git a/fs/coda/inode.c b/fs/coda/inode.c
index 97424cf206c0..aaed68ba960b 100644
--- a/fs/coda/inode.c
+++ b/fs/coda/inode.c
@@ -2,9 +2,9 @@
 /*
  * Super block/filesystem wide operations
  *
- * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and 
- * Michael Callahan <callahan@maths.ox.ac.uk> 
- * 
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and
+ * Michael Callahan <callahan@maths.ox.ac.uk>
+ *
  * Rewritten for Linux 2.1.  Peter Braam <braam@cs.cmu.edu>
  * Copyright (C) Carnegie Mellon University
  */
@@ -24,6 +24,8 @@
 #include <linux/pid_namespace.h>
 #include <linux/uaccess.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/vmalloc.h>
 
 #include <linux/coda.h>
@@ -93,10 +95,10 @@ void coda_destroy_inodecache(void)
 	kmem_cache_destroy(coda_inode_cachep);
 }
 
-static int coda_remount(struct super_block *sb, int *flags, char *data)
+static int coda_reconfigure(struct fs_context *fc)
 {
-	sync_filesystem(sb);
-	*flags |= SB_NOATIME;
+	sync_filesystem(fc->root->d_sb);
+	fc->sb_flags |= SB_NOATIME;
 	return 0;
 }
 
@@ -108,78 +110,111 @@ static const struct super_operations coda_super_operations =
 	.evict_inode	= coda_evict_inode,
 	.put_super	= coda_put_super,
 	.statfs		= coda_statfs,
-	.remount_fs	= coda_remount,
 };
 
-static int get_device_index(struct coda_mount_data *data)
+struct coda_fs_context {
+	int	idx;
+};
+
+enum {
+	Opt_fd,
+};
+
+static const struct fs_parameter_spec coda_param_specs[] = {
+	fsparam_fd	("fd",				Opt_fd),
+	{}
+};
+
+static const struct fs_parameter_description coda_fs_parameters = {
+	.name		= "coda",
+	.specs		= coda_param_specs,
+};
+
+static int coda_parse_fd(struct fs_context *fc, int fd)
 {
+	struct coda_fs_context *ctx = fc->fs_private;
 	struct fd f;
 	struct inode *inode;
 	int idx;
 
-	if (data == NULL) {
-		pr_warn("%s: Bad mount data\n", __func__);
-		return -1;
-	}
-
-	if (data->version != CODA_MOUNT_VERSION) {
-		pr_warn("%s: Bad mount version\n", __func__);
-		return -1;
-	}
-
-	f = fdget(data->fd);
+	f = fdget(fd);
 	if (!f.file)
-		goto Ebadf;
+		return -EBADF;
+
 	inode = file_inode(f.file);
 	if (!S_ISCHR(inode->i_mode) || imajor(inode) != CODA_PSDEV_MAJOR) {
 		fdput(f);
-		goto Ebadf;
+		return invalf(fc, "code: Not coda psdev");
 	}
 
 	idx = iminor(inode);
 	fdput(f);
 
-	if (idx < 0 || idx >= MAX_CODADEVS) {
-		pr_warn("%s: Bad minor number\n", __func__);
-		return -1;
+	if (idx < 0 || idx >= MAX_CODADEVS)
+		return invalf(fc, "coda: Bad minor number");
+	ctx->idx = idx;
+	return 0;
+}
+
+static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct fs_parse_result result;
+	int opt;
+
+	opt = fs_parse(fc, &coda_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_fd:
+		return coda_parse_fd(fc, result.uint_32);
+	}
+
+	return 0;
+}
+
+/*
+ * Parse coda's binary mount data form.  We ignore any errors and go with index
+ * 0 if we get one for backward compatibility.
+ */
+static int coda_parse_monolithic(struct fs_context *fc, void *_data)
+{
+	struct coda_mount_data *data = _data;
+
+	if (!data) {
+		invalf(fc, "coda: Bad mount data");
+		return 0;
+	}
+	if (data->version != CODA_MOUNT_VERSION) {
+		invalf(fc, "coda: Bad mount version");
+		return 0;
 	}
 
-	return idx;
-Ebadf:
-	pr_warn("%s: Bad file\n", __func__);
-	return -1;
+	coda_parse_fd(fc, data->fd);
+	return 0;
 }
 
-static int coda_fill_super(struct super_block *sb, void *data, int silent)
+static int coda_fill_super(struct super_block *sb, struct fs_context *fc)
 {
+	struct coda_fs_context *ctx = fc->fs_private;
 	struct inode *root = NULL;
 	struct venus_comm *vc;
 	struct CodaFid fid;
 	int error;
-	int idx;
 
-	if (task_active_pid_ns(current) != &init_pid_ns)
-		return -EINVAL;
+	infof(fc, "coda: device index: %i\n", ctx->idx);
 
-	idx = get_device_index((struct coda_mount_data *) data);
-
-	/* Ignore errors in data, for backward compatibility */
-	if(idx == -1)
-		idx = 0;
-	
-	pr_info("%s: device index: %i\n", __func__,  idx);
-
-	vc = &coda_comms[idx];
+	vc = &coda_comms[ctx->idx];
 	mutex_lock(&vc->vc_mutex);
 
 	if (!vc->vc_inuse) {
-		pr_warn("%s: No pseudo device\n", __func__);
+		errorf(fc, "coda: No pseudo device");
 		error = -EINVAL;
 		goto unlock_out;
 	}
 
 	if (vc->vc_sb) {
-		pr_warn("%s: Device already mounted\n", __func__);
+		errorf(fc, "coda: Device already mounted");
 		error = -EBUSY;
 		goto unlock_out;
 	}
@@ -207,7 +242,7 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent)
 		goto error;
 	}
 	pr_info("%s: rootfid is %s\n", __func__, coda_f2s(&fid));
-	
+
 	/* make root inode */
         root = coda_cnode_make(&fid, sb);
         if (IS_ERR(root)) {
@@ -215,7 +250,7 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent)
 		pr_warn("Failure of coda_cnode_make for root: error %d\n",
 			error);
 		goto error;
-	} 
+	}
 
 	pr_info("%s: rootinode is %ld dev %s\n",
 		__func__, root->i_ino, root->i_sb->s_id);
@@ -268,7 +303,7 @@ int coda_setattr(struct dentry *de, struct iattr *iattr)
 	struct coda_vattr vattr;
 	int error;
 
-	memset(&vattr, 0, sizeof(vattr)); 
+	memset(&vattr, 0, sizeof(vattr));
 
 	inode->i_ctime = current_time(inode);
 	coda_iattr_to_vattr(iattr, &vattr);
@@ -278,7 +313,7 @@ int coda_setattr(struct dentry *de, struct iattr *iattr)
 	error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr);
 
 	if (!error) {
-	        coda_vattr_to_iattr(inode, &vattr); 
+	        coda_vattr_to_iattr(inode, &vattr);
 		coda_cache_clear_inode(inode);
 	}
 	return error;
@@ -293,7 +328,7 @@ const struct inode_operations coda_file_inode_operations = {
 static int coda_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
 	int error;
-	
+
 	error = venus_statfs(dentry, buf);
 
 	if (error) {
@@ -310,23 +345,49 @@ static int coda_statfs(struct dentry *dentry, struct kstatfs *buf)
 	buf->f_bsize = 4096;
 	buf->f_namelen = CODA_MAXNAMLEN;
 
-	return 0; 
+	return 0;
 }
 
-/* init_coda: used by filesystems.c to register coda */
+static int coda_get_tree(struct fs_context *fc)
+{
+	if (task_active_pid_ns(current) != &init_pid_ns)
+		return -EINVAL;
+
+	return vfs_get_super(fc, vfs_get_independent_super, coda_fill_super);
+}
+
+static void coda_free_fc(struct fs_context *fc)
+{
+	kfree(fc->fs_private);
+}
 
-static struct dentry *coda_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static const struct fs_context_operations coda_context_ops = {
+	.free		= coda_free_fc,
+	.parse_param	= coda_parse_param,
+	.parse_monolithic = coda_parse_monolithic,
+	.get_tree	= coda_get_tree,
+	.reconfigure	= coda_reconfigure,
+};
+
+static int coda_init_fs_context(struct fs_context *fc)
 {
-	return mount_nodev(fs_type, flags, data, coda_fill_super);
+	struct coda_fs_context *ctx;
+
+	ctx = kzalloc(sizeof(struct coda_fs_context), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	fc->fs_private = ctx;
+	fc->ops = &coda_context_ops;
+	return 0;
 }
 
 struct file_system_type coda_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "coda",
-	.mount		= coda_mount,
+	.init_fs_context = coda_init_fs_context,
+	.parameters	= &coda_fs_parameters,
 	.kill_sb	= kill_anon_super,
 	.fs_flags	= FS_BINARY_MOUNTDATA,
 };
 MODULE_ALIAS_FS("coda");
-


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 63/68] vfs: Convert autofs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (61 preceding siblings ...)
  2019-03-27 23:48 ` [RFC PATCH 62/68] vfs: Convert coda to use the new mount API David Howells
@ 2019-03-27 23:48 ` David Howells
  2019-03-27 23:48 ` [RFC PATCH 64/68] vfs: Convert devpts " David Howells
                   ` (4 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:48 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, linux-kernel, dhowells

Convert the autofs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

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

 fs/autofs/autofs_i.h |   13 +-
 fs/autofs/init.c     |    9 -
 fs/autofs/inode.c    |  429 +++++++++++++++++++++++++++-----------------------
 3 files changed, 247 insertions(+), 204 deletions(-)

diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
index 70c132acdab1..bedbb004baf8 100644
--- a/fs/autofs/autofs_i.h
+++ b/fs/autofs/autofs_i.h
@@ -204,16 +204,25 @@ static inline void managed_dentry_clear_managed(struct dentry *dentry)
 
 /* Initializing function */
 
-int autofs_fill_super(struct super_block *, void *, int);
+extern const struct fs_parameter_description autofs_fs_parameters;
+int autofs_init_fs_context(struct fs_context *fc);
 struct autofs_info *autofs_new_ino(struct autofs_sb_info *);
 void autofs_clean_ino(struct autofs_info *);
 
-static inline int autofs_prepare_pipe(struct file *pipe)
+static inline int autofs_check_pipe(struct file *pipe)
 {
 	if (!(pipe->f_mode & FMODE_CAN_WRITE))
 		return -EINVAL;
 	if (!S_ISFIFO(file_inode(pipe)->i_mode))
 		return -EINVAL;
+	return 0;
+}
+
+static inline int autofs_prepare_pipe(struct file *pipe)
+{
+	int ret = autofs_check_pipe(pipe);
+	if (ret < 0)
+		return ret;
 	/* We want a packet pipe */
 	pipe->f_flags |= O_DIRECT;
 	/* We don't expect -EAGAIN */
diff --git a/fs/autofs/init.c b/fs/autofs/init.c
index c0c1db2cc6ea..075ecdad7496 100644
--- a/fs/autofs/init.c
+++ b/fs/autofs/init.c
@@ -10,16 +10,11 @@
 #include <linux/init.h>
 #include "autofs_i.h"
 
-static struct dentry *autofs_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
-{
-	return mount_nodev(fs_type, flags, data, autofs_fill_super);
-}
-
 struct file_system_type autofs_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "autofs",
-	.mount		= autofs_mount,
+	.init_fs_context = autofs_init_fs_context,
+	.parameters	= &autofs_fs_parameters,
 	.kill_sb	= autofs_kill_sb,
 };
 MODULE_ALIAS_FS("autofs");
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index 80597b88718b..ba9aa35e8259 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -9,7 +9,8 @@
 
 #include <linux/seq_file.h>
 #include <linux/pagemap.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 
 #include "autofs_i.h"
 
@@ -112,150 +113,148 @@ static const struct super_operations autofs_sops = {
 	.evict_inode	= autofs_evict_inode,
 };
 
-enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto,
-	Opt_indirect, Opt_direct, Opt_offset, Opt_strictexpire,
-	Opt_ignore};
-
-static const match_table_t tokens = {
-	{Opt_fd, "fd=%u"},
-	{Opt_uid, "uid=%u"},
-	{Opt_gid, "gid=%u"},
-	{Opt_pgrp, "pgrp=%u"},
-	{Opt_minproto, "minproto=%u"},
-	{Opt_maxproto, "maxproto=%u"},
-	{Opt_indirect, "indirect"},
-	{Opt_direct, "direct"},
-	{Opt_offset, "offset"},
-	{Opt_strictexpire, "strictexpire"},
-	{Opt_ignore, "ignore"},
-	{Opt_err, NULL}
+struct autofs_fs_context {
+	kuid_t	uid;
+	kgid_t	gid;
+	};
+
+enum {
+	Opt_direct,
+	Opt_fd,
+	Opt_gid,
+	Opt_ignore,
+	Opt_indirect,
+	Opt_maxproto,
+	Opt_minproto,
+	Opt_offset,
+	Opt_pgrp,
+	Opt_strictexpire,
+	Opt_uid,
 };
 
-static int parse_options(char *options,
-			 struct inode *root, int *pgrp, bool *pgrp_set,
-			 struct autofs_sb_info *sbi)
+static const struct fs_parameter_spec autofs_param_specs[] = {
+	fsparam_flag	("direct",			Opt_direct),
+	fsparam_fd	("fd",				Opt_fd),
+	fsparam_u32	("gid",				Opt_gid),
+	fsparam_flag	("ignore",			Opt_ignore),
+	fsparam_flag	("indirect",			Opt_indirect),
+	fsparam_u32	("maxproto",			Opt_maxproto),
+	fsparam_u32	("minproto",			Opt_minproto),
+	fsparam_flag	("offset",			Opt_offset),
+	fsparam_u32	("pgrp",			Opt_pgrp),
+	fsparam_flag	("strictexpire",		Opt_strictexpire),
+	fsparam_u32	("uid",				Opt_uid),
+	{}
+};
+
+const struct fs_parameter_description autofs_fs_parameters = {
+	.name		= "autofs",
+	.specs		= autofs_param_specs,
+};
+
+/*
+ * Open the fd.  We do it here rather than in get_tree so that it's done in the
+ * context of the system call that passed the data and not the one that
+ * triggered the superblock creation, lest the fd gets reassigned.
+ */
+static int autofs_parse_fd(struct fs_context *fc, int pipefd)
 {
-	char *p;
-	substring_t args[MAX_OPT_ARGS];
-	int option;
-	int pipefd = -1;
-	kuid_t uid;
-	kgid_t gid;
+	struct autofs_sb_info *sbi = fc->s_fs_info;
+	struct file *pipe;
+	int ret;
 
-	root->i_uid = current_uid();
-	root->i_gid = current_gid();
+	pipe = fget(pipefd);
+	if (!pipe) {
+		errorf(fc, "Pipe file descriptor not open");
+		return -EBADF;
+	}
 
-	sbi->min_proto = AUTOFS_MIN_PROTO_VERSION;
-	sbi->max_proto = AUTOFS_MAX_PROTO_VERSION;
+	ret = autofs_check_pipe(pipe);
+	if (ret < 0) {
+		fput(pipe);
+		return invalf(fc, "Invalid/unusable pipe");
+	}
 
-	sbi->pipefd = -1;
+	sbi->pipefd = pipefd;
+	sbi->pipe = pipe;
+
+	return 0;
+}
 
-	if (!options)
-		return 1;
-
-	while ((p = strsep(&options, ",")) != NULL) {
-		int token;
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_fd:
-			if (match_int(args, &pipefd))
-				return 1;
-			sbi->pipefd = pipefd;
-			break;
-		case Opt_uid:
-			if (match_int(args, &option))
-				return 1;
-			uid = make_kuid(current_user_ns(), option);
-			if (!uid_valid(uid))
-				return 1;
-			root->i_uid = uid;
-			break;
-		case Opt_gid:
-			if (match_int(args, &option))
-				return 1;
-			gid = make_kgid(current_user_ns(), option);
-			if (!gid_valid(gid))
-				return 1;
-			root->i_gid = gid;
-			break;
-		case Opt_pgrp:
-			if (match_int(args, &option))
-				return 1;
-			*pgrp = option;
-			*pgrp_set = true;
-			break;
-		case Opt_minproto:
-			if (match_int(args, &option))
-				return 1;
-			sbi->min_proto = option;
-			break;
-		case Opt_maxproto:
-			if (match_int(args, &option))
-				return 1;
-			sbi->max_proto = option;
-			break;
-		case Opt_indirect:
-			set_autofs_type_indirect(&sbi->type);
-			break;
-		case Opt_direct:
-			set_autofs_type_direct(&sbi->type);
-			break;
-		case Opt_offset:
-			set_autofs_type_offset(&sbi->type);
-			break;
-		case Opt_strictexpire:
-			sbi->flags |= AUTOFS_SBI_STRICTEXPIRE;
-			break;
-		case Opt_ignore:
-			sbi->flags |= AUTOFS_SBI_IGNORE;
-			break;
-		default:
+static int autofs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+	struct autofs_fs_context *ctx = fc->fs_private;
+	struct autofs_sb_info *sbi = fc->s_fs_info;
+	struct fs_parse_result result;
+	struct pid *pgrp;
+	kuid_t uid;
+	kgid_t gid;
+	int opt;
+
+	opt = fs_parse(fc, &autofs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_fd:
+		return autofs_parse_fd(fc, result.int_32);
+	case Opt_uid:
+		uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(uid))
+			return 1;
+		ctx->uid = uid;
+		break;
+	case Opt_gid:
+		gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(gid))
 			return 1;
-		}
+		ctx->gid = gid;
+		break;
+	case Opt_pgrp:
+		pgrp = find_get_pid(result.uint_32);
+		if (!pgrp)
+			return invalf(fc, "Could not find process group %u",
+				      result.uint_32);
+		put_pid(sbi->oz_pgrp);
+		sbi->oz_pgrp = pgrp;
+		break;
+	case Opt_minproto:
+		sbi->min_proto = result.uint_32;
+		break;
+	case Opt_maxproto:
+		sbi->max_proto = result.uint_32;
+		break;
+	case Opt_indirect:
+		set_autofs_type_indirect(&sbi->type);
+		break;
+	case Opt_direct:
+		set_autofs_type_direct(&sbi->type);
+		break;
+	case Opt_offset:
+		set_autofs_type_offset(&sbi->type);
+		break;
+	case Opt_strictexpire:
+		sbi->flags |= AUTOFS_SBI_STRICTEXPIRE;
+		break;
+	case Opt_ignore:
+		sbi->flags |= AUTOFS_SBI_IGNORE;
+		break;
 	}
-	return (sbi->pipefd < 0);
+
+	return 0;
 }
 
-int autofs_fill_super(struct super_block *s, void *data, int silent)
+static int autofs_fill_super(struct super_block *s, struct fs_context *fc)
 {
+	struct autofs_fs_context *ctx = fc->fs_private;
+	struct autofs_sb_info *sbi = s->s_fs_info;
+	struct autofs_info *ino;
 	struct inode *root_inode;
 	struct dentry *root;
-	struct file *pipe;
-	struct autofs_sb_info *sbi;
-	struct autofs_info *ino;
-	int pgrp = 0;
-	bool pgrp_set = false;
-	int ret = -EINVAL;
 
-	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
-	if (!sbi)
-		return -ENOMEM;
 	pr_debug("starting up, sbi = %p\n", sbi);
 
-	s->s_fs_info = sbi;
-	sbi->magic = AUTOFS_SBI_MAGIC;
-	sbi->pipefd = -1;
-	sbi->pipe = NULL;
-	sbi->exp_timeout = 0;
-	sbi->oz_pgrp = NULL;
 	sbi->sb = s;
-	sbi->version = 0;
-	sbi->sub_version = 0;
-	sbi->flags = AUTOFS_SBI_CATATONIC;
-	set_autofs_type_indirect(&sbi->type);
-	sbi->min_proto = 0;
-	sbi->max_proto = 0;
-	mutex_init(&sbi->wq_mutex);
-	mutex_init(&sbi->pipe_mutex);
-	spin_lock_init(&sbi->fs_lock);
-	sbi->queues = NULL;
-	spin_lock_init(&sbi->lookup_lock);
-	INIT_LIST_HEAD(&sbi->active_list);
-	INIT_LIST_HEAD(&sbi->expiring_list);
 	s->s_blocksize = 1024;
 	s->s_blocksize_bits = 10;
 	s->s_magic = AUTOFS_SUPER_MAGIC;
@@ -264,38 +263,63 @@ int autofs_fill_super(struct super_block *s, void *data, int silent)
 	s->s_time_gran = 1;
 
 	/*
-	 * Get the root inode and dentry, but defer checking for errors.
+	 * Get the root inode and dentry.
 	 */
 	ino = autofs_new_ino(sbi);
-	if (!ino) {
-		ret = -ENOMEM;
-		goto fail_free;
-	}
+	if (!ino)
+		goto nomem;
+
 	root_inode = autofs_get_inode(s, S_IFDIR | 0755);
+	root_inode->i_uid = ctx->uid;
+	root_inode->i_gid = ctx->gid;
+	root_inode->i_fop = &autofs_root_operations;
+	root_inode->i_op = &autofs_dir_inode_operations;
+
 	root = d_make_root(root_inode);
-	if (!root) {
-		ret = -ENOMEM;
-		goto fail_ino;
-	}
-	pipe = NULL;
+	if (!root)
+		goto nomem_ino;
 
 	root->d_fsdata = ino;
 
-	/* Can this call block? */
-	if (parse_options(data, root_inode, &pgrp, &pgrp_set, sbi)) {
-		pr_err("called with bogus options\n");
-		goto fail_dput;
-	}
+	if (autofs_type_trigger(sbi->type))
+		__managed_dentry_set_managed(root);
+
+	pr_debug("pipe fd = %d, pgrp = %u\n",
+		 sbi->pipefd, pid_nr(sbi->oz_pgrp));
+
+	autofs_prepare_pipe(sbi->pipe);
+
+	sbi->flags &= ~AUTOFS_SBI_CATATONIC;
+
+	/*
+	 * Success! Install the root dentry now to indicate completion.
+	 */
+	s->s_root = root;
+	return 0;
+
+	/*
+	 * Failure ... clean up.
+	 */
+nomem_ino:
+	autofs_free_ino(ino);
+nomem:
+	return -ENOMEM;
+}
+
+/*
+ * Validate the parameters and then request a superblock.
+ */
+static int autofs_get_tree(struct fs_context *fc)
+{
+	struct autofs_sb_info *sbi = fc->s_fs_info;
 
 	/* Test versions first */
 	if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
-	    sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) {
-		pr_err("kernel does not match daemon version "
-		       "daemon (%d, %d) kernel (%d, %d)\n",
-		       sbi->min_proto, sbi->max_proto,
-		       AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
-		goto fail_dput;
-	}
+	    sbi->min_proto > AUTOFS_MAX_PROTO_VERSION)
+		return invalf(fc, "kernel does not match daemon version "
+			      "daemon (%d, %d) kernel (%d, %d)\n",
+			      sbi->min_proto, sbi->max_proto,
+			      AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
 
 	/* Establish highest kernel protocol version */
 	if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION)
@@ -304,60 +328,75 @@ int autofs_fill_super(struct super_block *s, void *data, int silent)
 		sbi->version = sbi->max_proto;
 	sbi->sub_version = AUTOFS_PROTO_SUBVERSION;
 
-	if (pgrp_set) {
-		sbi->oz_pgrp = find_get_pid(pgrp);
-		if (!sbi->oz_pgrp) {
-			pr_err("could not find process group %d\n",
-				pgrp);
-			goto fail_dput;
-		}
-	} else {
-		sbi->oz_pgrp = get_task_pid(current, PIDTYPE_PGID);
+	if (!sbi->pipe)
+		return invalf(fc, "No control pipe specified");
+
+	return vfs_get_super(fc, vfs_get_independent_super, autofs_fill_super);
+}
+
+static void autofs_free_fc(struct fs_context *fc)
+{
+	struct autofs_fs_context *ctx = fc->fs_private;
+	struct autofs_sb_info *sbi = fc->s_fs_info;
+
+	if (sbi) {
+		if (sbi->pipe)
+			fput(sbi->pipe);
+		put_pid(sbi->oz_pgrp);
+		kfree(sbi);
 	}
+	kfree(ctx);
+}
 
-	if (autofs_type_trigger(sbi->type))
-		__managed_dentry_set_managed(root);
+static const struct fs_context_operations autofs_context_ops = {
+	.free		= autofs_free_fc,
+	.parse_param	= autofs_parse_param,
+	.get_tree	= autofs_get_tree,
+};
 
-	root_inode->i_fop = &autofs_root_operations;
-	root_inode->i_op = &autofs_dir_inode_operations;
+/*
+ * Set up the filesystem mount context.
+ */
+int autofs_init_fs_context(struct fs_context *fc)
+{
+	struct autofs_fs_context *ctx;
+	struct autofs_sb_info *sbi;
 
-	pr_debug("pipe fd = %d, pgrp = %u\n",
-		 sbi->pipefd, pid_nr(sbi->oz_pgrp));
-	pipe = fget(sbi->pipefd);
+	ctx = kzalloc(sizeof(struct autofs_fs_context), GFP_KERNEL);
+	if (!ctx)
+		goto nomem;
 
-	if (!pipe) {
-		pr_err("could not open pipe file descriptor\n");
-		goto fail_put_pid;
-	}
-	ret = autofs_prepare_pipe(pipe);
-	if (ret < 0)
-		goto fail_fput;
-	sbi->pipe = pipe;
-	sbi->flags &= ~AUTOFS_SBI_CATATONIC;
+	ctx->uid = current_uid();
+	ctx->gid = current_gid();
 
-	/*
-	 * Success! Install the root dentry now to indicate completion.
-	 */
-	s->s_root = root;
+	sbi = kzalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
+	if (!sbi)
+		goto nomem_ctx;
+
+	sbi->magic = AUTOFS_SBI_MAGIC;
+	sbi->flags = AUTOFS_SBI_CATATONIC;
+	sbi->min_proto = AUTOFS_MIN_PROTO_VERSION;
+	sbi->max_proto = AUTOFS_MAX_PROTO_VERSION;
+	sbi->pipefd = -1;
+	sbi->oz_pgrp = get_task_pid(current, PIDTYPE_PGID);
+
+	set_autofs_type_indirect(&sbi->type);
+	mutex_init(&sbi->wq_mutex);
+	mutex_init(&sbi->pipe_mutex);
+	spin_lock_init(&sbi->fs_lock);
+	spin_lock_init(&sbi->lookup_lock);
+	INIT_LIST_HEAD(&sbi->active_list);
+	INIT_LIST_HEAD(&sbi->expiring_list);
+
+	fc->fs_private = ctx;
+	fc->s_fs_info = sbi;
+	fc->ops = &autofs_context_ops;
 	return 0;
 
-	/*
-	 * Failure ... clean up.
-	 */
-fail_fput:
-	pr_err("pipe file descriptor does not contain proper ops\n");
-	fput(pipe);
-fail_put_pid:
-	put_pid(sbi->oz_pgrp);
-fail_dput:
-	dput(root);
-	goto fail_free;
-fail_ino:
-	autofs_free_ino(ino);
-fail_free:
-	kfree(sbi);
-	s->s_fs_info = NULL;
-	return ret;
+nomem_ctx:
+	kfree(ctx);
+nomem:
+	return -ENOMEM;
 }
 
 struct inode *autofs_get_inode(struct super_block *sb, umode_t mode)


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 64/68] vfs: Convert devpts to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (62 preceding siblings ...)
  2019-03-27 23:48 ` [RFC PATCH 63/68] vfs: Convert autofs " David Howells
@ 2019-03-27 23:48 ` David Howells
  2019-04-05 12:52   ` Christian Brauner
  2019-03-27 23:48 ` [RFC PATCH 65/68] vfs: Convert bpf " David Howells
                   ` (3 subsequent siblings)
  67 siblings, 1 reply; 87+ messages in thread
From: David Howells @ 2019-03-27 23:48 UTC (permalink / raw)
  To: viro
  Cc: Eric W. Biederman, Christian Brauner, linux-fsdevel,
	linux-kernel, dhowells

Convert the devpts filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: "Eric W. Biederman" <ebiederm@xmission.com>
cc: Christian Brauner <christian.brauner@ubuntu.com>
---

 fs/devpts/inode.c |  265 +++++++++++++++++++++++++----------------------------
 1 file changed, 124 insertions(+), 141 deletions(-)

diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 553a3f3300ae..ed4a49012570 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -15,6 +15,8 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/sched.h>
 #include <linux/namei.h>
 #include <linux/slab.h>
@@ -24,7 +26,6 @@
 #include <linux/magic.h>
 #include <linux/idr.h>
 #include <linux/devpts_fs.h>
-#include <linux/parser.h>
 #include <linux/fsnotify.h>
 #include <linux/seq_file.h>
 
@@ -109,14 +110,19 @@ enum {
 	Opt_err
 };
 
-static const match_table_t tokens = {
-	{Opt_uid, "uid=%u"},
-	{Opt_gid, "gid=%u"},
-	{Opt_mode, "mode=%o"},
-	{Opt_ptmxmode, "ptmxmode=%o"},
-	{Opt_newinstance, "newinstance"},
-	{Opt_max, "max=%d"},
-	{Opt_err, NULL}
+static const struct fs_parameter_spec devpts_param_specs[] = {
+	fsparam_u32	("gid",		Opt_gid),
+	fsparam_s32	("max",		Opt_max),
+	fsparam_u32oct	("mode",	Opt_mode),
+	fsparam_flag	("newinstance",	Opt_newinstance),
+	fsparam_u32oct	("ptmxmode",	Opt_ptmxmode),
+	fsparam_u32	("uid",		Opt_uid),
+	{}
+};
+
+static const struct fs_parameter_description devpts_fs_parameters = {
+	.name		= "devpts",
+	.specs		= devpts_param_specs,
 };
 
 struct pts_fs_info {
@@ -236,93 +242,56 @@ void devpts_release(struct pts_fs_info *fsi)
 	deactivate_super(fsi->sb);
 }
 
-#define PARSE_MOUNT	0
-#define PARSE_REMOUNT	1
-
 /*
- * parse_mount_options():
- *	Set @opts to mount options specified in @data. If an option is not
- *	specified in @data, set it to its default value.
- *
- * Note: @data may be NULL (in which case all options are set to default).
+ * devpts_parse_param - Parse mount parameters
  */
-static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
+static int devpts_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	char *p;
+	struct pts_fs_info *fsi = fc->s_fs_info;
+	struct pts_mount_opts *opts = &fsi->mount_opts;
+	struct fs_parse_result result;
 	kuid_t uid;
 	kgid_t gid;
-
-	opts->setuid  = 0;
-	opts->setgid  = 0;
-	opts->uid     = GLOBAL_ROOT_UID;
-	opts->gid     = GLOBAL_ROOT_GID;
-	opts->mode    = DEVPTS_DEFAULT_MODE;
-	opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
-	opts->max     = NR_UNIX98_PTY_MAX;
-
-	/* Only allow instances mounted from the initial mount
-	 * namespace to tap the reserve pool of ptys.
-	 */
-	if (op == PARSE_MOUNT)
-		opts->reserve =
-			(current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns);
-
-	while ((p = strsep(&data, ",")) != NULL) {
-		substring_t args[MAX_OPT_ARGS];
-		int token;
-		int option;
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_uid:
-			if (match_int(&args[0], &option))
-				return -EINVAL;
-			uid = make_kuid(current_user_ns(), option);
-			if (!uid_valid(uid))
-				return -EINVAL;
-			opts->uid = uid;
-			opts->setuid = 1;
-			break;
-		case Opt_gid:
-			if (match_int(&args[0], &option))
-				return -EINVAL;
-			gid = make_kgid(current_user_ns(), option);
-			if (!gid_valid(gid))
-				return -EINVAL;
-			opts->gid = gid;
-			opts->setgid = 1;
-			break;
-		case Opt_mode:
-			if (match_octal(&args[0], &option))
-				return -EINVAL;
-			opts->mode = option & S_IALLUGO;
-			break;
-		case Opt_ptmxmode:
-			if (match_octal(&args[0], &option))
-				return -EINVAL;
-			opts->ptmxmode = option & S_IALLUGO;
-			break;
-		case Opt_newinstance:
-			break;
-		case Opt_max:
-			if (match_int(&args[0], &option) ||
-			    option < 0 || option > NR_UNIX98_PTY_MAX)
-				return -EINVAL;
-			opts->max = option;
-			break;
-		default:
-			pr_err("called with bogus options\n");
-			return -EINVAL;
-		}
+	int opt;
+
+	opt = fs_parse(fc, &devpts_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_uid:
+		uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(uid))
+			return invalf(fc, "Unknown uid");
+		opts->uid = uid;
+		opts->setuid = 1;
+		break;
+	case Opt_gid:
+		gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(gid))
+			return invalf(fc, "Unknown gid");
+		opts->gid = gid;
+		opts->setgid = 1;
+		break;
+	case Opt_mode:
+		opts->mode = result.uint_32 & S_IALLUGO;
+		break;
+	case Opt_ptmxmode:
+		opts->ptmxmode = result.uint_32 & S_IALLUGO;
+		break;
+	case Opt_newinstance:
+		break;
+	case Opt_max:
+		if (result.uint_32 > NR_UNIX98_PTY_MAX)
+			return invalf(fc, "max out of range");
+		opts->max = result.uint_32;
+		break;
 	}
 
 	return 0;
 }
 
-static int mknod_ptmx(struct super_block *sb)
+static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
 {
 	int mode;
 	int rc = -ENOMEM;
@@ -344,7 +313,7 @@ static int mknod_ptmx(struct super_block *sb)
 
 	dentry = d_alloc_name(root, "ptmx");
 	if (!dentry) {
-		pr_err("Unable to alloc dentry for ptmx node\n");
+		errorf(fc, "Unable to alloc dentry for ptmx node\n");
 		goto out;
 	}
 
@@ -353,7 +322,7 @@ static int mknod_ptmx(struct super_block *sb)
 	 */
 	inode = new_inode(sb);
 	if (!inode) {
-		pr_err("Unable to alloc inode for ptmx node\n");
+		errorf(fc, "Unable to alloc inode for ptmx node\n");
 		dput(dentry);
 		goto out;
 	}
@@ -384,13 +353,23 @@ static void update_ptmx_mode(struct pts_fs_info *fsi)
 	}
 }
 
-static int devpts_remount(struct super_block *sb, int *flags, char *data)
+static int devpts_reconfigure(struct fs_context *fc)
 {
-	int err;
-	struct pts_fs_info *fsi = DEVPTS_SB(sb);
-	struct pts_mount_opts *opts = &fsi->mount_opts;
+	struct pts_fs_info *fsi = DEVPTS_SB(fc->root->d_sb);
+	struct pts_fs_info *new = fc->s_fs_info;
 
-	err = parse_mount_options(data, PARSE_REMOUNT, opts);
+	/* Apply the revised options.  We don't want to change ->reserve.
+	 * Ideally, we'd update each option conditionally on it having being
+	 * explicitly changed, but the default is to reset everything so that
+	 * would break UAPI...
+	 */
+	fsi->mount_opts.setuid		= new->mount_opts.setuid;
+	fsi->mount_opts.setgid		= new->mount_opts.setgid;
+	fsi->mount_opts.uid		= new->mount_opts.uid;
+	fsi->mount_opts.gid		= new->mount_opts.gid;
+	fsi->mount_opts.mode		= new->mount_opts.mode;
+	fsi->mount_opts.ptmxmode	= new->mount_opts.ptmxmode;
+	fsi->mount_opts.max		= new->mount_opts.max;
 
 	/*
 	 * parse_mount_options() restores options to default values
@@ -400,7 +379,7 @@ static int devpts_remount(struct super_block *sb, int *flags, char *data)
 	 */
 	update_ptmx_mode(fsi);
 
-	return err;
+	return 0;
 }
 
 static int devpts_show_options(struct seq_file *seq, struct dentry *root)
@@ -424,31 +403,13 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root)
 
 static const struct super_operations devpts_sops = {
 	.statfs		= simple_statfs,
-	.remount_fs	= devpts_remount,
 	.show_options	= devpts_show_options,
 };
 
-static void *new_pts_fs_info(struct super_block *sb)
-{
-	struct pts_fs_info *fsi;
-
-	fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
-	if (!fsi)
-		return NULL;
-
-	ida_init(&fsi->allocated_ptys);
-	fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
-	fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
-	fsi->sb = sb;
-
-	return fsi;
-}
-
-static int
-devpts_fill_super(struct super_block *s, void *data, int silent)
+static int devpts_fill_super(struct super_block *s, struct fs_context *fc)
 {
+	struct pts_fs_info *fsi = DEVPTS_SB(s);
 	struct inode *inode;
-	int error;
 
 	s->s_iflags &= ~SB_I_NODEV;
 	s->s_blocksize = 1024;
@@ -457,20 +418,12 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
 	s->s_op = &devpts_sops;
 	s->s_d_op = &simple_dentry_operations;
 	s->s_time_gran = 1;
+	fsi->sb = s;
 
-	error = -ENOMEM;
-	s->s_fs_info = new_pts_fs_info(s);
-	if (!s->s_fs_info)
-		goto fail;
-
-	error = parse_mount_options(data, PARSE_MOUNT, &DEVPTS_SB(s)->mount_opts);
-	if (error)
-		goto fail;
-
-	error = -ENOMEM;
 	inode = new_inode(s);
 	if (!inode)
-		goto fail;
+		return -ENOMEM;
+
 	inode->i_ino = 1;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
 	inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
@@ -480,20 +433,11 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
 
 	s->s_root = d_make_root(inode);
 	if (!s->s_root) {
-		pr_err("get root dentry failed\n");
-		goto fail;
+		errorf(fc, "get root dentry failed");
+		return -ENOMEM;
 	}
 
-	error = mknod_ptmx(s);
-	if (error)
-		goto fail_dput;
-
-	return 0;
-fail_dput:
-	dput(s->s_root);
-	s->s_root = NULL;
-fail:
-	return error;
+	return mknod_ptmx(s, fc);
 }
 
 /*
@@ -502,10 +446,48 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
  *     Mount a new (private) instance of devpts.  PTYs created in this
  *     instance are independent of the PTYs in other devpts instances.
  */
-static struct dentry *devpts_mount(struct file_system_type *fs_type,
-	int flags, const char *dev_name, void *data)
+static int devpts_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_independent_super, devpts_fill_super);
+}
+
+static void devpts_free_fc(struct fs_context *fc)
+{
+	kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations devpts_context_ops = {
+	.free		= devpts_free_fc,
+	.parse_param	= devpts_parse_param,
+	.get_tree	= devpts_get_tree,
+	.reconfigure	= devpts_reconfigure,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+static int devpts_init_fs_context(struct fs_context *fc)
 {
-	return mount_nodev(fs_type, flags, data, devpts_fill_super);
+	struct pts_fs_info *fsi;
+
+	fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
+	if (!fsi)
+		return -ENOMEM;
+
+	ida_init(&fsi->allocated_ptys);
+	fsi->mount_opts.uid     = GLOBAL_ROOT_UID;
+	fsi->mount_opts.gid     = GLOBAL_ROOT_GID;
+	fsi->mount_opts.mode    = DEVPTS_DEFAULT_MODE;
+	fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
+	fsi->mount_opts.max     = NR_UNIX98_PTY_MAX;
+
+	if (fc->purpose == FS_CONTEXT_FOR_MOUNT &&
+	    current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns)
+		fsi->mount_opts.reserve = true;
+
+	fc->s_fs_info = fsi;
+	fc->ops = &devpts_context_ops;
+	return 0;
 }
 
 static void devpts_kill_sb(struct super_block *sb)
@@ -520,7 +502,8 @@ static void devpts_kill_sb(struct super_block *sb)
 
 static struct file_system_type devpts_fs_type = {
 	.name		= "devpts",
-	.mount		= devpts_mount,
+	.init_fs_context = devpts_init_fs_context,
+	.parameters	= &devpts_fs_parameters,
 	.kill_sb	= devpts_kill_sb,
 	.fs_flags	= FS_USERNS_MOUNT,
 };


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 65/68] vfs: Convert bpf to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (63 preceding siblings ...)
  2019-03-27 23:48 ` [RFC PATCH 64/68] vfs: Convert devpts " David Howells
@ 2019-03-27 23:48 ` David Howells
  2019-03-27 23:48 ` [RFC PATCH 66/68] vfs: Convert ubifs " David Howells
                   ` (2 subsequent siblings)
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:48 UTC (permalink / raw)
  To: viro
  Cc: Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
	Yonghong Song, netdev, bpf, linux-fsdevel, linux-kernel,
	dhowells

Convert the bpf filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Alexei Starovoitov <ast@kernel.org>
cc: Daniel Borkmann <daniel@iogearbox.net>
cc: Martin KaFai Lau <kafai@fb.com>
cc: Song Liu <songliubraving@fb.com>
cc: Yonghong Song <yhs@fb.com>
cc: netdev@vger.kernel.org
cc: bpf@vger.kernel.org
---

 kernel/bpf/inode.c |   92 +++++++++++++++++++++++++++++++++-------------------
 1 file changed, 58 insertions(+), 34 deletions(-)

diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 2ada5e21dfa6..8ea3a1f11e10 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -17,8 +17,9 @@
 #include <linux/mount.h>
 #include <linux/namei.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/kdev_t.h>
-#include <linux/parser.h>
 #include <linux/filter.h>
 #include <linux/bpf.h>
 #include <linux/bpf_trace.h>
@@ -588,58 +589,52 @@ static const struct super_operations bpf_super_ops = {
 
 enum {
 	OPT_MODE,
-	OPT_ERR,
 };
 
-static const match_table_t bpf_mount_tokens = {
-	{ OPT_MODE, "mode=%o" },
-	{ OPT_ERR, NULL },
+static const struct fs_parameter_spec bpf_param_specs[] = {
+	fsparam_u32oct	("mode",			OPT_MODE),
+	{}
+};
+
+static const struct fs_parameter_description bpf_fs_parameters = {
+	.name		= "bpf",
+	.specs		= bpf_param_specs,
 };
 
 struct bpf_mount_opts {
 	umode_t mode;
 };
 
-static int bpf_parse_options(char *data, struct bpf_mount_opts *opts)
+static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	substring_t args[MAX_OPT_ARGS];
-	int option, token;
-	char *ptr;
+	struct bpf_mount_opts *opts = fc->fs_private;
+	struct fs_parse_result result;
+	int opt;
 
-	opts->mode = S_IRWXUGO;
-
-	while ((ptr = strsep(&data, ",")) != NULL) {
-		if (!*ptr)
-			continue;
-
-		token = match_token(ptr, bpf_mount_tokens, args);
-		switch (token) {
-		case OPT_MODE:
-			if (match_octal(&args[0], &option))
-				return -EINVAL;
-			opts->mode = option & S_IALLUGO;
-			break;
+	opt = fs_parse(fc, &bpf_fs_parameters, param, &result);
+	if (opt < 0)
 		/* We might like to report bad mount options here, but
 		 * traditionally we've ignored all mount options, so we'd
 		 * better continue to ignore non-existing options for bpf.
 		 */
-		}
+		return opt == -ENOPARAM ? 0 : opt;
+
+	switch (opt) {
+	case OPT_MODE:
+		opts->mode = result.uint_32 & S_IALLUGO;
+		break;
 	}
 
 	return 0;
 }
 
-static int bpf_fill_super(struct super_block *sb, void *data, int silent)
+static int bpf_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static const struct tree_descr bpf_rfiles[] = { { "" } };
-	struct bpf_mount_opts opts;
+	struct bpf_mount_opts *opts = fc->fs_private;
 	struct inode *inode;
 	int ret;
 
-	ret = bpf_parse_options(data, &opts);
-	if (ret)
-		return ret;
-
 	ret = simple_fill_super(sb, BPF_FS_MAGIC, bpf_rfiles);
 	if (ret)
 		return ret;
@@ -649,21 +644,50 @@ static int bpf_fill_super(struct super_block *sb, void *data, int silent)
 	inode = sb->s_root->d_inode;
 	inode->i_op = &bpf_dir_iops;
 	inode->i_mode &= ~S_IALLUGO;
-	inode->i_mode |= S_ISVTX | opts.mode;
+	inode->i_mode |= S_ISVTX | opts->mode;
 
 	return 0;
 }
 
-static struct dentry *bpf_mount(struct file_system_type *type, int flags,
-				const char *dev_name, void *data)
+static int bpf_get_tree(struct fs_context *fc)
+{
+	return vfs_get_super(fc, vfs_get_independent_super, bpf_fill_super);
+}
+
+static void bpf_free_fc(struct fs_context *fc)
 {
-	return mount_nodev(type, flags, data, bpf_fill_super);
+	kfree(fc->fs_private);
+}
+
+static const struct fs_context_operations bpf_context_ops = {
+	.free		= bpf_free_fc,
+	.parse_param	= bpf_parse_param,
+	.get_tree	= bpf_get_tree,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+static int bpf_init_fs_context(struct fs_context *fc)
+{
+	struct bpf_mount_opts *opts;
+
+	opts = kzalloc(sizeof(struct bpf_mount_opts), GFP_KERNEL);
+	if (!opts)
+		return -ENOMEM;
+
+	opts->mode = S_IRWXUGO;
+
+	fc->fs_private = opts;
+	fc->ops = &bpf_context_ops;
+	return 0;
 }
 
 static struct file_system_type bpf_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "bpf",
-	.mount		= bpf_mount,
+	.init_fs_context = bpf_init_fs_context,
+	.parameters	= &bpf_fs_parameters,
 	.kill_sb	= kill_litter_super,
 };
 


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 66/68] vfs: Convert ubifs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (64 preceding siblings ...)
  2019-03-27 23:48 ` [RFC PATCH 65/68] vfs: Convert bpf " David Howells
@ 2019-03-27 23:48 ` David Howells
  2019-03-27 23:49 ` [RFC PATCH 67/68] vfs: Convert orangefs " David Howells
  2019-03-27 23:49 ` [RFC PATCH 68/68] gfs2: Convert gfs2 to fs_context David Howells
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:48 UTC (permalink / raw)
  To: viro
  Cc: Richard Weinberger, Artem Bityutskiy, Adrian Hunter, linux-mtd,
	linux-fsdevel, linux-kernel, dhowells

Convert the ubifs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Richard Weinberger <richard@nod.at>
cc: Artem Bityutskiy <dedekind1@gmail.com>
cc: Adrian Hunter <adrian.hunter@intel.com>
cc: linux-mtd@lists.infradead.org
---

 fs/ubifs/super.c |  447 ++++++++++++++++++++++++------------------------------
 1 file changed, 200 insertions(+), 247 deletions(-)

diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 8dc2818fdd84..e77ad4dd2665 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -31,9 +31,9 @@
 #include <linux/module.h>
 #include <linux/ctype.h>
 #include <linux/kthread.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/seq_file.h>
-#include <linux/mount.h>
 #include <linux/math64.h>
 #include <linux/writeback.h>
 #include "ubifs.h"
@@ -935,7 +935,7 @@ static int check_volume_empty(struct ubifs_info *c)
  * Opt_no_bulk_read: disable bulk-reads
  * Opt_chk_data_crc: check CRCs when reading data nodes
  * Opt_no_chk_data_crc: do not check CRCs when reading data nodes
- * Opt_override_compr: override default compressor
+ * Opt_compr: override default compressor
  * Opt_assert: set ubifs_assert() action
  * Opt_auth_key: The key name used for authentication
  * Opt_auth_hash_name: The hash type used for authentication
@@ -948,54 +948,47 @@ enum {
 	Opt_no_bulk_read,
 	Opt_chk_data_crc,
 	Opt_no_chk_data_crc,
-	Opt_override_compr,
+	Opt_compr,
 	Opt_assert,
 	Opt_auth_key,
 	Opt_auth_hash_name,
 	Opt_ignore,
-	Opt_err,
 };
 
-static const match_table_t tokens = {
-	{Opt_fast_unmount, "fast_unmount"},
-	{Opt_norm_unmount, "norm_unmount"},
-	{Opt_bulk_read, "bulk_read"},
-	{Opt_no_bulk_read, "no_bulk_read"},
-	{Opt_chk_data_crc, "chk_data_crc"},
-	{Opt_no_chk_data_crc, "no_chk_data_crc"},
-	{Opt_override_compr, "compr=%s"},
-	{Opt_auth_key, "auth_key=%s"},
-	{Opt_auth_hash_name, "auth_hash_name=%s"},
-	{Opt_ignore, "ubi=%s"},
-	{Opt_ignore, "vol=%s"},
-	{Opt_assert, "assert=%s"},
-	{Opt_err, NULL},
+static const struct fs_parameter_spec ubifs_param_specs[] = {
+	fsparam_flag	("fast_unmount",		Opt_fast_unmount),
+	fsparam_flag	("norm_unmount",		Opt_norm_unmount),
+	fsparam_flag	("bulk_read",			Opt_bulk_read),
+	fsparam_flag	("no_bulk_read",		Opt_no_bulk_read),
+	fsparam_flag	("chk_data_crc",		Opt_chk_data_crc),
+	fsparam_flag	("no_chk_data_crc",		Opt_no_chk_data_crc),
+	fsparam_enum	("compr",			Opt_compr),
+	fsparam_enum	("assert",			Opt_assert),
+	fsparam_string	("auth_key",			Opt_auth_key),
+	fsparam_string	("auth_hash_name",		Opt_auth_hash_name),
+	fsparam_string	("ubi",				Opt_ignore),
+	fsparam_string	("vol",				Opt_ignore),
+	{}
 };
 
-/**
- * parse_standard_option - parse a standard mount option.
- * @option: the option to parse
- *
- * Normally, standard mount options like "sync" are passed to file-systems as
- * flags. However, when a "rootflags=" kernel boot parameter is used, they may
- * be present in the options string. This function tries to deal with this
- * situation and parse standard options. Returns 0 if the option was not
- * recognized, and the corresponding integer flag if it was.
- *
- * UBIFS is only interested in the "sync" option, so do not check for anything
- * else.
- */
-static int parse_standard_option(const char *option)
-{
+static const struct fs_parameter_enum ubifs_param_enums[] = {
+	{ Opt_compr,	"none",		UBIFS_COMPR_NONE },
+	{ Opt_compr,	"lzo",		UBIFS_COMPR_LZO },
+	{ Opt_compr,	"zlib",		UBIFS_COMPR_ZLIB },
+	{ Opt_assert,	"report",	ASSACT_REPORT },
+	{ Opt_assert,	"read-only",	ASSACT_RO },
+	{ Opt_assert,	"panic",	ASSACT_PANIC },
+	{}
+};
 
-	pr_notice("UBIFS: parse %s\n", option);
-	if (!strcmp(option, "sync"))
-		return SB_SYNCHRONOUS;
-	return 0;
-}
+static const struct fs_parameter_description ubifs_fs_parameters = {
+	.name		= "ubifs",
+	.specs		= ubifs_param_specs,
+	.enums		= ubifs_param_enums,
+};
 
 /**
- * ubifs_parse_options - parse mount parameters.
+ * ubifs_parse_param - parse a parameter.
  * @c: UBIFS file-system description object
  * @options: parameters to parse
  * @is_remount: non-zero if this is FS re-mount
@@ -1003,119 +996,64 @@ static int parse_standard_option(const char *option)
  * This function parses UBIFS mount options and returns zero in case success
  * and a negative error code in case of failure.
  */
-static int ubifs_parse_options(struct ubifs_info *c, char *options,
-			       int is_remount)
+static int ubifs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	char *p;
-	substring_t args[MAX_OPT_ARGS];
-
-	if (!options)
-		return 0;
-
-	while ((p = strsep(&options, ","))) {
-		int token;
+	struct ubifs_info *c = fc->s_fs_info;
+	struct fs_parse_result result;
+	int opt;
 
-		if (!*p)
-			continue;
+	opt = fs_parse(fc, &ubifs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
 
-		token = match_token(p, tokens, args);
-		switch (token) {
+	switch (opt) {
 		/*
 		 * %Opt_fast_unmount and %Opt_norm_unmount options are ignored.
 		 * We accept them in order to be backward-compatible. But this
 		 * should be removed at some point.
 		 */
-		case Opt_fast_unmount:
-			c->mount_opts.unmount_mode = 2;
-			break;
-		case Opt_norm_unmount:
-			c->mount_opts.unmount_mode = 1;
-			break;
-		case Opt_bulk_read:
-			c->mount_opts.bulk_read = 2;
-			c->bulk_read = 1;
-			break;
-		case Opt_no_bulk_read:
-			c->mount_opts.bulk_read = 1;
-			c->bulk_read = 0;
-			break;
-		case Opt_chk_data_crc:
-			c->mount_opts.chk_data_crc = 2;
-			c->no_chk_data_crc = 0;
-			break;
-		case Opt_no_chk_data_crc:
-			c->mount_opts.chk_data_crc = 1;
-			c->no_chk_data_crc = 1;
-			break;
-		case Opt_override_compr:
-		{
-			char *name = match_strdup(&args[0]);
-
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "none"))
-				c->mount_opts.compr_type = UBIFS_COMPR_NONE;
-			else if (!strcmp(name, "lzo"))
-				c->mount_opts.compr_type = UBIFS_COMPR_LZO;
-			else if (!strcmp(name, "zlib"))
-				c->mount_opts.compr_type = UBIFS_COMPR_ZLIB;
-			else {
-				ubifs_err(c, "unknown compressor \"%s\"", name); //FIXME: is c ready?
-				kfree(name);
-				return -EINVAL;
-			}
-			kfree(name);
-			c->mount_opts.override_compr = 1;
-			c->default_compr = c->mount_opts.compr_type;
-			break;
-		}
-		case Opt_assert:
-		{
-			char *act = match_strdup(&args[0]);
-
-			if (!act)
-				return -ENOMEM;
-			if (!strcmp(act, "report"))
-				c->assert_action = ASSACT_REPORT;
-			else if (!strcmp(act, "read-only"))
-				c->assert_action = ASSACT_RO;
-			else if (!strcmp(act, "panic"))
-				c->assert_action = ASSACT_PANIC;
-			else {
-				ubifs_err(c, "unknown assert action \"%s\"", act);
-				kfree(act);
-				return -EINVAL;
-			}
-			kfree(act);
-			break;
-		}
-		case Opt_auth_key:
-			c->auth_key_name = kstrdup(args[0].from, GFP_KERNEL);
-			if (!c->auth_key_name)
-				return -ENOMEM;
-			break;
-		case Opt_auth_hash_name:
-			c->auth_hash_name = kstrdup(args[0].from, GFP_KERNEL);
-			if (!c->auth_hash_name)
-				return -ENOMEM;
-			break;
-		case Opt_ignore:
-			break;
-		default:
-		{
-			unsigned long flag;
-			struct super_block *sb = c->vfs_sb;
-
-			flag = parse_standard_option(p);
-			if (!flag) {
-				ubifs_err(c, "unrecognized mount option \"%s\" or missing value",
-					  p);
-				return -EINVAL;
-			}
-			sb->s_flags |= flag;
-			break;
-		}
-		}
+	case Opt_fast_unmount:
+		c->mount_opts.unmount_mode = 2;
+		break;
+	case Opt_norm_unmount:
+		c->mount_opts.unmount_mode = 1;
+		break;
+	case Opt_bulk_read:
+		c->mount_opts.bulk_read = 2;
+		c->bulk_read = 1;
+		break;
+	case Opt_no_bulk_read:
+		c->mount_opts.bulk_read = 1;
+		c->bulk_read = 0;
+		break;
+	case Opt_chk_data_crc:
+		c->mount_opts.chk_data_crc = 2;
+		c->no_chk_data_crc = 0;
+		break;
+	case Opt_no_chk_data_crc:
+		c->mount_opts.chk_data_crc = 1;
+		c->no_chk_data_crc = 1;
+		break;
+	case Opt_compr:
+		c->mount_opts.compr_type = result.uint_32;
+		c->mount_opts.override_compr = 1;
+		c->default_compr = c->mount_opts.compr_type;
+		break;
+	case Opt_assert:
+		c->assert_action = result.uint_32;
+		break;
+	case Opt_auth_key:
+		kfree(c->auth_key_name);
+		c->auth_key_name = param->string;
+		param->string = NULL;
+		break;
+	case Opt_auth_hash_name:
+		kfree(c->auth_hash_name);
+		c->auth_hash_name = param->string;
+		param->string = NULL;
+		break;
+	case Opt_ignore:
+		break;
 	}
 
 	return 0;
@@ -1927,21 +1865,30 @@ static void ubifs_put_super(struct super_block *sb)
 	mutex_unlock(&c->umount_mutex);
 }
 
-static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
+static int ubifs_reconfigure(struct fs_context *fc)
 {
+	struct super_block *sb = fc->root->d_sb;
 	int err;
 	struct ubifs_info *c = sb->s_fs_info;
+	struct ubifs_info *reconf = fc->s_fs_info;
 
 	sync_filesystem(sb);
-	dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags);
+	dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, fc->sb_flags);
 
-	err = ubifs_parse_options(c, data, 1);
-	if (err) {
-		ubifs_err(c, "invalid or unknown remount parameter");
-		return err;
-	}
+	/* Apply the mount option changes.
+	 *
+	 * [!] NOTE Replacing the auth_key_name and auth_hash_name is
+	 *     very dodgy without locking.  Previously it just leaked
+	 *     the old strings.  The strings only seem to be used
+	 *     during mounting, so don't reconfigure those.
+	 */
+	c->mount_opts		= reconf->mount_opts;
+	c->bulk_read		= reconf->bulk_read;
+	c->no_chk_data_crc	= reconf->no_chk_data_crc;
+	c->default_compr	= reconf->default_compr;
+	c->assert_action	= reconf->assert_action;
 
-	if (c->ro_mount && !(*flags & SB_RDONLY)) {
+	if (c->ro_mount && !(fc->sb_flags & SB_RDONLY)) {
 		if (c->ro_error) {
 			ubifs_msg(c, "cannot re-mount R/W due to prior errors");
 			return -EROFS;
@@ -1953,7 +1900,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
 		err = ubifs_remount_rw(c);
 		if (err)
 			return err;
-	} else if (!c->ro_mount && (*flags & SB_RDONLY)) {
+	} else if (!c->ro_mount && (fc->sb_flags & SB_RDONLY)) {
 		if (c->ro_error) {
 			ubifs_msg(c, "cannot re-mount R/O due to prior errors");
 			return -EROFS;
@@ -1985,14 +1932,13 @@ const struct super_operations ubifs_super_operations = {
 	.evict_inode   = ubifs_evict_inode,
 	.statfs        = ubifs_statfs,
 	.dirty_inode   = ubifs_dirty_inode,
-	.remount_fs    = ubifs_remount_fs,
 	.show_options  = ubifs_show_options,
 	.sync_fs       = ubifs_sync_fs,
 };
 
 /**
  * open_ubi - parse UBI device name string and open the UBI device.
- * @name: UBI volume name
+ * @fc: The filesystem parameters
  * @mode: UBI volume open mode
  *
  * The primary method of mounting UBIFS is by specifying the UBI volume
@@ -2009,15 +1955,13 @@ const struct super_operations ubifs_super_operations = {
  * returns UBI volume description object in case of success and a negative
  * error code in case of failure.
  */
-static struct ubi_volume_desc *open_ubi(const char *name, int mode)
+static struct ubi_volume_desc *open_ubi(struct fs_context *fc, int mode)
 {
 	struct ubi_volume_desc *ubi;
+	const char *name = fc->source;
 	int dev, vol;
 	char *endptr;
 
-	if (!name || !*name)
-		return ERR_PTR(-EINVAL);
-
 	/* First, try to open using the device node path method */
 	ubi = ubi_open_volume_path(name, mode);
 	if (!IS_ERR(ubi))
@@ -2025,14 +1969,14 @@ static struct ubi_volume_desc *open_ubi(const char *name, int mode)
 
 	/* Try the "nodev" method */
 	if (name[0] != 'u' || name[1] != 'b' || name[2] != 'i')
-		return ERR_PTR(-EINVAL);
+		goto invalid_source;
 
 	/* ubi:NAME method */
 	if ((name[3] == ':' || name[3] == '!') && name[4] != '\0')
 		return ubi_open_volume_nm(0, name + 4, mode);
 
 	if (!isdigit(name[3]))
-		return ERR_PTR(-EINVAL);
+		goto invalid_source;
 
 	dev = simple_strtoul(name + 3, &endptr, 0);
 
@@ -2044,7 +1988,7 @@ static struct ubi_volume_desc *open_ubi(const char *name, int mode)
 	if (*endptr == '_' && isdigit(endptr[1])) {
 		vol = simple_strtoul(endptr + 1, &endptr, 0);
 		if (*endptr != '\0')
-			return ERR_PTR(-EINVAL);
+			goto invalid_source;
 		return ubi_open_volume(dev, vol, mode);
 	}
 
@@ -2052,57 +1996,11 @@ static struct ubi_volume_desc *open_ubi(const char *name, int mode)
 	if ((*endptr == ':' || *endptr == '!') && endptr[1] != '\0')
 		return ubi_open_volume_nm(dev, ++endptr, mode);
 
-	return ERR_PTR(-EINVAL);
-}
-
-static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi)
-{
-	struct ubifs_info *c;
-
-	c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL);
-	if (c) {
-		spin_lock_init(&c->cnt_lock);
-		spin_lock_init(&c->cs_lock);
-		spin_lock_init(&c->buds_lock);
-		spin_lock_init(&c->space_lock);
-		spin_lock_init(&c->orphan_lock);
-		init_rwsem(&c->commit_sem);
-		mutex_init(&c->lp_mutex);
-		mutex_init(&c->tnc_mutex);
-		mutex_init(&c->log_mutex);
-		mutex_init(&c->umount_mutex);
-		mutex_init(&c->bu_mutex);
-		mutex_init(&c->write_reserve_mutex);
-		init_waitqueue_head(&c->cmt_wq);
-		c->buds = RB_ROOT;
-		c->old_idx = RB_ROOT;
-		c->size_tree = RB_ROOT;
-		c->orph_tree = RB_ROOT;
-		INIT_LIST_HEAD(&c->infos_list);
-		INIT_LIST_HEAD(&c->idx_gc);
-		INIT_LIST_HEAD(&c->replay_list);
-		INIT_LIST_HEAD(&c->replay_buds);
-		INIT_LIST_HEAD(&c->uncat_list);
-		INIT_LIST_HEAD(&c->empty_list);
-		INIT_LIST_HEAD(&c->freeable_list);
-		INIT_LIST_HEAD(&c->frdi_idx_list);
-		INIT_LIST_HEAD(&c->unclean_leb_list);
-		INIT_LIST_HEAD(&c->old_buds);
-		INIT_LIST_HEAD(&c->orph_list);
-		INIT_LIST_HEAD(&c->orph_new);
-		c->no_chk_data_crc = 1;
-		c->assert_action = ASSACT_RO;
-
-		c->highest_inum = UBIFS_FIRST_INO;
-		c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
-
-		ubi_get_volume_info(ubi, &c->vi);
-		ubi_get_device_info(c->vi.ubi_num, &c->di);
-	}
-	return c;
+invalid_source:
+	return ERR_PTR(invalf(fc, "Invalid source name"));
 }
 
-static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
+static int ubifs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct ubifs_info *c = sb->s_fs_info;
 	struct inode *root;
@@ -2116,10 +2014,6 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
 		goto out;
 	}
 
-	err = ubifs_parse_options(c, data, 0);
-	if (err)
-		goto out_close;
-
 	/*
 	 * UBIFS provides 'backing_dev_info' in order to disable read-ahead. For
 	 * UBIFS, I/O is not deferred, it is done immediately in readpage,
@@ -2183,69 +2077,61 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
 	return err;
 }
 
-static int sb_test(struct super_block *sb, void *data)
+static int sb_test(struct super_block *sb, struct fs_context *fc)
 {
-	struct ubifs_info *c1 = data;
+	struct ubifs_info *c1 = fc->s_fs_info;
 	struct ubifs_info *c = sb->s_fs_info;
 
 	return c->vi.cdev == c1->vi.cdev;
 }
 
-static int sb_set(struct super_block *sb, void *data)
-{
-	sb->s_fs_info = data;
-	return set_anon_super(sb, NULL);
-}
-
-static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
-			const char *name, void *data)
+static int ubifs_get_tree(struct fs_context *fc)
 {
 	struct ubi_volume_desc *ubi;
-	struct ubifs_info *c;
+	struct ubifs_info *c = fc->s_fs_info;
 	struct super_block *sb;
 	int err;
 
-	dbg_gen("name %s, flags %#x", name, flags);
+	if (!fc->source || !*fc->source)
+		return invalf(fc, "No source specified");
+
+	dbg_gen("name %s, flags %#x", fc->source, fc->sb_flags);
 
 	/*
 	 * Get UBI device number and volume ID. Mount it read-only so far
 	 * because this might be a new mount point, and UBI allows only one
 	 * read-write user at a time.
 	 */
-	ubi = open_ubi(name, UBI_READONLY);
+	ubi = open_ubi(fc, UBI_READONLY);
 	if (IS_ERR(ubi)) {
-		if (!(flags & SB_SILENT))
-			pr_err("UBIFS error (pid: %d): cannot open \"%s\", error %d",
-			       current->pid, name, (int)PTR_ERR(ubi));
-		return ERR_CAST(ubi);
+		err = PTR_ERR(ubi);
+		if (!(fc->sb_flags & SB_SILENT))
+			errorf(fc, "UBIFS error (pid: %d): cannot open \"%s\", error %d",
+			       current->pid, fc->source, err);
+		return err;
 	}
 
-	c = alloc_ubifs_info(ubi);
-	if (!c) {
-		err = -ENOMEM;
-		goto out_close;
-	}
+	ubi_get_volume_info(ubi, &c->vi);
+	ubi_get_device_info(c->vi.ubi_num, &c->di);
 
 	dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
 
-	sb = sget(fs_type, sb_test, sb_set, flags, c);
+	sb = sget_fc(fc, sb_test, set_anon_super_fc);
 	if (IS_ERR(sb)) {
 		err = PTR_ERR(sb);
-		kfree(c);
 		goto out_close;
 	}
 
 	if (sb->s_root) {
-		struct ubifs_info *c1 = sb->s_fs_info;
-		kfree(c);
 		/* A new mount point for already mounted UBIFS */
+		c = sb->s_fs_info;
 		dbg_gen("this ubi volume is already mounted");
-		if (!!(flags & SB_RDONLY) != c1->ro_mount) {
+		if (!!(fc->sb_flags & SB_RDONLY) != c->ro_mount) {
 			err = -EBUSY;
 			goto out_deact;
 		}
 	} else {
-		err = ubifs_fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
+		err = ubifs_fill_super(sb, fc);
 		if (err)
 			goto out_deact;
 		/* We do not support atime */
@@ -2260,13 +2146,79 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
 	/* 'fill_super()' opens ubi again so we must close it here */
 	ubi_close_volume(ubi);
 
-	return dget(sb->s_root);
+	fc->root = dget(sb->s_root);
+	return 0;
 
 out_deact:
 	deactivate_locked_super(sb);
 out_close:
 	ubi_close_volume(ubi);
-	return ERR_PTR(err);
+	return err;
+}
+
+static void ubifs_free_fc(struct fs_context *fc)
+{
+	struct ubifs_info *c = fc->s_fs_info;
+
+	if (c) {
+		kfree(c->auth_key_name);
+		kfree(c->auth_hash_name);
+		kfree(c);
+	}
+}
+
+static const struct fs_context_operations ubifs_context_ops = {
+	.free		= ubifs_free_fc,
+	.parse_param	= ubifs_parse_param,
+	.get_tree	= ubifs_get_tree,
+	.reconfigure	= ubifs_reconfigure,
+};
+
+static int ubifs_init_fs_context(struct fs_context *fc)
+{
+	struct ubifs_info *c;
+
+	c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL);
+	if (!c)
+		return -ENOMEM;
+
+	spin_lock_init(&c->cnt_lock);
+	spin_lock_init(&c->cs_lock);
+	spin_lock_init(&c->buds_lock);
+	spin_lock_init(&c->space_lock);
+	spin_lock_init(&c->orphan_lock);
+	init_rwsem(&c->commit_sem);
+	mutex_init(&c->lp_mutex);
+	mutex_init(&c->tnc_mutex);
+	mutex_init(&c->log_mutex);
+	mutex_init(&c->umount_mutex);
+	mutex_init(&c->bu_mutex);
+	mutex_init(&c->write_reserve_mutex);
+	init_waitqueue_head(&c->cmt_wq);
+	c->buds = RB_ROOT;
+	c->old_idx = RB_ROOT;
+	c->size_tree = RB_ROOT;
+	c->orph_tree = RB_ROOT;
+	INIT_LIST_HEAD(&c->infos_list);
+	INIT_LIST_HEAD(&c->idx_gc);
+	INIT_LIST_HEAD(&c->replay_list);
+	INIT_LIST_HEAD(&c->replay_buds);
+	INIT_LIST_HEAD(&c->uncat_list);
+	INIT_LIST_HEAD(&c->empty_list);
+	INIT_LIST_HEAD(&c->freeable_list);
+	INIT_LIST_HEAD(&c->frdi_idx_list);
+	INIT_LIST_HEAD(&c->unclean_leb_list);
+	INIT_LIST_HEAD(&c->old_buds);
+	INIT_LIST_HEAD(&c->orph_list);
+	INIT_LIST_HEAD(&c->orph_new);
+	c->no_chk_data_crc = 1;
+	c->assert_action = ASSACT_RO;
+	c->highest_inum = UBIFS_FIRST_INO;
+	c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
+
+	fc->s_fs_info = c;
+	fc->ops = &ubifs_context_ops;
+	return 0;
 }
 
 static void kill_ubifs_super(struct super_block *s)
@@ -2279,7 +2231,8 @@ static void kill_ubifs_super(struct super_block *s)
 static struct file_system_type ubifs_fs_type = {
 	.name    = "ubifs",
 	.owner   = THIS_MODULE,
-	.mount   = ubifs_mount,
+	.init_fs_context = ubifs_init_fs_context,
+	.parameters	= &ubifs_fs_parameters,
 	.kill_sb = kill_ubifs_super,
 };
 MODULE_ALIAS_FS("ubifs");


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 67/68] vfs: Convert orangefs to use the new mount API
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (65 preceding siblings ...)
  2019-03-27 23:48 ` [RFC PATCH 66/68] vfs: Convert ubifs " David Howells
@ 2019-03-27 23:49 ` David Howells
  2019-03-27 23:49 ` [RFC PATCH 68/68] gfs2: Convert gfs2 to fs_context David Howells
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:49 UTC (permalink / raw)
  To: viro
  Cc: Mike Marshall, Martin Brandenburg, devel, linux-fsdevel,
	linux-kernel, dhowells

Convert the orangefs filesystem to the new internal mount API as the old
one will be obsoleted and removed.  This allows greater flexibility in
communication of mount parameters between userspace, the VFS and the
filesystem.

See Documentation/filesystems/mount_api.txt for more information.
cc: Mike Marshall <hubcap@omnibond.com>
cc: Martin Brandenburg <martin@omnibond.com>
cc: devel@lists.orangefs.org
---

 fs/orangefs/orangefs-kernel.h |    8 +-
 fs/orangefs/orangefs-mod.c    |    3 -
 fs/orangefs/super.c           |  186 ++++++++++++++++++++++-------------------
 3 files changed, 105 insertions(+), 92 deletions(-)

diff --git a/fs/orangefs/orangefs-kernel.h b/fs/orangefs/orangefs-kernel.h
index 17b24ad6b264..5b8379a6c815 100644
--- a/fs/orangefs/orangefs-kernel.h
+++ b/fs/orangefs/orangefs-kernel.h
@@ -32,6 +32,8 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/vmalloc.h>
 
 #include <linux/aio.h>
@@ -314,11 +316,9 @@ void purge_waiting_ops(void);
  * defined in super.c
  */
 extern uint64_t orangefs_features;
+extern const struct fs_parameter_description orangefs_fs_parameters;
 
-struct dentry *orangefs_mount(struct file_system_type *fst,
-			   int flags,
-			   const char *devname,
-			   void *data);
+int orangefs_init_fs_context(struct fs_context *fc);
 
 void orangefs_kill_sb(struct super_block *sb);
 int orangefs_remount(struct orangefs_sb_info_s *);
diff --git a/fs/orangefs/orangefs-mod.c b/fs/orangefs/orangefs-mod.c
index 85ef87245a87..f65cf0dd0f7e 100644
--- a/fs/orangefs/orangefs-mod.c
+++ b/fs/orangefs/orangefs-mod.c
@@ -44,7 +44,8 @@ MODULE_PARM_DESC(hash_table_size,
 
 static struct file_system_type orangefs_fs_type = {
 	.name = "pvfs2",
-	.mount = orangefs_mount,
+	.init_fs_context	= orangefs_init_fs_context,
+	.parameters		= &orangefs_fs_parameters,
 	.kill_sb = orangefs_kill_sb,
 	.owner = THIS_MODULE,
 };
diff --git a/fs/orangefs/super.c b/fs/orangefs/super.c
index dfaee90d30bd..f69d568818ce 100644
--- a/fs/orangefs/super.c
+++ b/fs/orangefs/super.c
@@ -9,8 +9,6 @@
 #include "orangefs-kernel.h"
 #include "orangefs-bufmap.h"
 
-#include <linux/parser.h>
-
 /* a cache for orangefs-inode objects (i.e. orangefs inode private data) */
 static struct kmem_cache *orangefs_inode_cache;
 
@@ -19,19 +17,22 @@ LIST_HEAD(orangefs_superblocks);
 
 DEFINE_SPINLOCK(orangefs_superblocks_lock);
 
-enum {
-	Opt_intr,
+enum orangefs_param {
 	Opt_acl,
+	Opt_intr,
 	Opt_local_lock,
+};
 
-	Opt_err
+static const struct fs_parameter_spec orangefs_param_specs[] = {
+	fsparam_flag	("acl",			Opt_acl),
+	fsparam_flag	("intr",		Opt_intr),
+	fsparam_flag	("local_lock",		Opt_local_lock),
+	{}
 };
 
-static const match_table_t tokens = {
-	{ Opt_acl,		"acl" },
-	{ Opt_intr,		"intr" },
-	{ Opt_local_lock,	"local_lock" },
-	{ Opt_err,	NULL }
+const struct fs_parameter_description orangefs_fs_parameters = {
+	.name		= "orangefs",
+	.specs		= orangefs_param_specs,
 };
 
 uint64_t orangefs_features;
@@ -49,48 +50,29 @@ static int orangefs_show_options(struct seq_file *m, struct dentry *root)
 	return 0;
 }
 
-static int parse_mount_options(struct super_block *sb, char *options,
-		int silent)
+static int orangefs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	struct orangefs_sb_info_s *orangefs_sb = ORANGEFS_SB(sb);
-	substring_t args[MAX_OPT_ARGS];
-	char *p;
-
-	/*
-	 * Force any potential flags that might be set from the mount
-	 * to zero, ie, initialize to unset.
-	 */
-	sb->s_flags &= ~SB_POSIXACL;
-	orangefs_sb->flags &= ~ORANGEFS_OPT_INTR;
-	orangefs_sb->flags &= ~ORANGEFS_OPT_LOCAL_LOCK;
-
-	while ((p = strsep(&options, ",")) != NULL) {
-		int token;
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_acl:
-			sb->s_flags |= SB_POSIXACL;
-			break;
-		case Opt_intr:
-			orangefs_sb->flags |= ORANGEFS_OPT_INTR;
-			break;
-		case Opt_local_lock:
-			orangefs_sb->flags |= ORANGEFS_OPT_LOCAL_LOCK;
-			break;
-		default:
-			goto fail;
-		}
+	struct orangefs_sb_info_s *orangefs_sb = fc->s_fs_info;
+	struct fs_parse_result result;
+	int opt;
+
+	opt = fs_parse(fc, &orangefs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+
+	switch (opt) {
+	case Opt_acl:
+		fc->sb_flags |= SB_POSIXACL;
+		break;
+	case Opt_intr:
+		orangefs_sb->flags |= ORANGEFS_OPT_INTR;
+		break;
+	case Opt_local_lock:
+		orangefs_sb->flags |= ORANGEFS_OPT_LOCAL_LOCK;
+		break;
 	}
 
 	return 0;
-fail:
-	if (!silent)
-		gossip_err("Error: mount option [%s] is not supported.\n", p);
-	return -EINVAL;
 }
 
 static void orangefs_inode_cache_ctor(void *req)
@@ -207,10 +189,20 @@ static int orangefs_statfs(struct dentry *dentry, struct kstatfs *buf)
  * Remount as initiated by VFS layer.  We just need to reparse the mount
  * options, no need to signal pvfs2-client-core about it.
  */
-static int orangefs_remount_fs(struct super_block *sb, int *flags, char *data)
+static int orangefs_reconfigure(struct fs_context *fc)
 {
-	gossip_debug(GOSSIP_SUPER_DEBUG, "orangefs_remount_fs: called\n");
-	return parse_mount_options(sb, data, 1);
+	struct super_block *sb = fc->root->d_sb;
+	struct orangefs_sb_info_s *orangefs_sb = ORANGEFS_SB(sb);
+	struct orangefs_sb_info_s *revised = fc->s_fs_info;
+	unsigned int flags;
+
+	flags = orangefs_sb->flags;
+	flags &= ~(ORANGEFS_OPT_INTR | ORANGEFS_OPT_LOCAL_LOCK);
+	flags |= revised->flags;
+	WRITE_ONCE(orangefs_sb->flags, flags);
+
+	gossip_debug(GOSSIP_SUPER_DEBUG, "orangefs_reconfigure: called\n");
+	return 0;
 }
 
 /*
@@ -302,7 +294,6 @@ static const struct super_operations orangefs_s_ops = {
 	.destroy_inode = orangefs_destroy_inode,
 	.drop_inode = generic_delete_inode,
 	.statfs = orangefs_statfs,
-	.remount_fs = orangefs_remount_fs,
 	.show_options = orangefs_show_options,
 };
 
@@ -394,10 +385,9 @@ static int orangefs_unmount(int id, __s32 fs_id, const char *devname)
 }
 
 static int orangefs_fill_sb(struct super_block *sb,
-		struct orangefs_fs_mount_response *fs_mount,
-		void *data, int silent)
+			    struct fs_context *fc,
+			    struct orangefs_fs_mount_response *fs_mount)
 {
-	int ret = -EINVAL;
 	struct inode *root = NULL;
 	struct dentry *root_dentry = NULL;
 	struct orangefs_object_kref root_object;
@@ -412,12 +402,6 @@ static int orangefs_fill_sb(struct super_block *sb,
 	ORANGEFS_SB(sb)->fs_id = fs_mount->fs_id;
 	ORANGEFS_SB(sb)->id = fs_mount->id;
 
-	if (data) {
-		ret = parse_mount_options(sb, data, silent);
-		if (ret)
-			return ret;
-	}
-
 	/* Hang the xattr handlers off the superblock */
 	sb->s_xattr = orangefs_xattr_handlers;
 	sb->s_magic = ORANGEFS_SUPER_MAGIC;
@@ -454,31 +438,25 @@ static int orangefs_fill_sb(struct super_block *sb,
 	return 0;
 }
 
-struct dentry *orangefs_mount(struct file_system_type *fst,
-			   int flags,
-			   const char *devname,
-			   void *data)
+static int orangefs_get_tree(struct fs_context *fc)
 {
 	int ret = -EINVAL;
 	struct super_block *sb = ERR_PTR(-EINVAL);
 	struct orangefs_kernel_op_s *new_op;
-	struct dentry *d = ERR_PTR(-EINVAL);
+
+	if (!fc->source)
+		return invalf(fc, "Device name not specified.\n");
 
 	gossip_debug(GOSSIP_SUPER_DEBUG,
 		     "orangefs_mount: called with devname %s\n",
-		     devname);
-
-	if (!devname) {
-		gossip_err("ERROR: device name not specified.\n");
-		return ERR_PTR(-EINVAL);
-	}
+		     fc->source);
 
 	new_op = op_alloc(ORANGEFS_VFS_OP_FS_MOUNT);
 	if (!new_op)
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
 
 	strncpy(new_op->upcall.req.fs_mount.orangefs_config_server,
-		devname,
+		fc->source,
 		ORANGEFS_MAX_SERVER_ADDR_LEN - 1);
 
 	gossip_debug(GOSSIP_SUPER_DEBUG,
@@ -497,30 +475,27 @@ struct dentry *orangefs_mount(struct file_system_type *fst,
 		goto free_op;
 	}
 
-	sb = sget(fst, NULL, set_anon_super, flags, NULL);
+	sb = sget_fc(fc, NULL, set_anon_super_fc);
 
 	if (IS_ERR(sb)) {
-		d = ERR_CAST(sb);
+		ret = PTR_ERR(sb);
 		orangefs_unmount(new_op->downcall.resp.fs_mount.id,
-		    new_op->downcall.resp.fs_mount.fs_id, devname);
+				 new_op->downcall.resp.fs_mount.fs_id,
+				 fc->source);
 		goto free_op;
 	}
 
-	ret = orangefs_fill_sb(sb,
-	      &new_op->downcall.resp.fs_mount, data,
-	      flags & SB_SILENT ? 1 : 0);
+	ret = orangefs_fill_sb(sb, fc, &new_op->downcall.resp.fs_mount);
 
-	if (ret) {
-		d = ERR_PTR(ret);
+	if (ret)
 		goto free_sb_and_op;
-	}
 
 	/*
 	 * on successful mount, store the devname and data
 	 * used
 	 */
 	strncpy(ORANGEFS_SB(sb)->devname,
-		devname,
+		fc->source,
 		ORANGEFS_MAX_SERVER_ADDR_LEN - 1);
 
 	/* mount_pending must be cleared */
@@ -544,7 +519,7 @@ struct dentry *orangefs_mount(struct file_system_type *fst,
 	if (orangefs_userspace_version >= 20906) {
 		new_op = op_alloc(ORANGEFS_VFS_OP_FEATURES);
 		if (!new_op)
-			return ERR_PTR(-ENOMEM);
+			return -ENOMEM;
 		new_op->upcall.req.features.features = 0;
 		ret = service_operation(new_op, "orangefs_features", 0);
 		orangefs_features = new_op->downcall.resp.features.features;
@@ -553,7 +528,8 @@ struct dentry *orangefs_mount(struct file_system_type *fst,
 		orangefs_features = 0;
 	}
 
-	return dget(sb->s_root);
+	fc->root = dget(sb->s_root);
+	return 0;
 
 free_sb_and_op:
 	/* Will call orangefs_kill_sb with sb not in list. */
@@ -569,7 +545,43 @@ struct dentry *orangefs_mount(struct file_system_type *fst,
 
 	op_release(new_op);
 
-	return d;
+	return ret;
+}
+
+static void orangefs_free_fc(struct fs_context *fc)
+{
+	kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations orangefs_context_ops = {
+	.free		= orangefs_free_fc,
+	.parse_param	= orangefs_parse_param,
+	.get_tree	= orangefs_get_tree,
+	.reconfigure	= orangefs_reconfigure,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+int orangefs_init_fs_context(struct fs_context *fc)
+{
+	struct orangefs_sb_info_s *osi;
+
+	osi = kzalloc(sizeof(struct orangefs_sb_info_s), GFP_KERNEL);
+	if (!osi)
+		return -ENOMEM;
+
+	/*
+	 * Force any potential flags that might be set from the mount
+	 * to zero, ie, initialize to unset.
+	 */
+	fc->sb_flags_mask |= SB_POSIXACL;
+	osi->flags &= ~ORANGEFS_OPT_INTR;
+	osi->flags &= ~ORANGEFS_OPT_LOCAL_LOCK;
+
+	fc->s_fs_info = osi;
+	fc->ops = &orangefs_context_ops;
+	return 0;
 }
 
 void orangefs_kill_sb(struct super_block *sb)


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* [RFC PATCH 68/68] gfs2: Convert gfs2 to fs_context
       [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
                   ` (66 preceding siblings ...)
  2019-03-27 23:49 ` [RFC PATCH 67/68] vfs: Convert orangefs " David Howells
@ 2019-03-27 23:49 ` David Howells
  67 siblings, 0 replies; 87+ messages in thread
From: David Howells @ 2019-03-27 23:49 UTC (permalink / raw)
  To: viro; +Cc: Andrew Price, cluster-devel, linux-fsdevel, linux-kernel, dhowells

From: Andrew Price <anprice@redhat.com>

Convert gfs2 and gfs2meta to fs_context. Removes the duplicated vfs code
from gfs2_mount and instead uses the new vfs_get_block_super() before
switching the ->root to the appropriate dentry.

The mount option parsing has been converted to the new API and error
reporting for invalid options has been made more precise at the same
time.

All of the mount/remount code has been moved into ops_fstype.c

Signed-off-by: Andrew Price <anprice@redhat.com>
Signed-off-by: David Howells <dhowells@redhat.com>
cc: cluster-devel@redhat.com
---

 fs/gfs2/incore.h     |    8 -
 fs/gfs2/ops_fstype.c |  495 ++++++++++++++++++++++++++++++++++++++------------
 fs/gfs2/super.c      |  335 ----------------------------------
 fs/gfs2/super.h      |    3 
 4 files changed, 382 insertions(+), 459 deletions(-)

diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index cdf07b408f54..10febb298da5 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -587,10 +587,10 @@ struct gfs2_args {
 	unsigned int ar_rgrplvb:1;		/* use lvbs for rgrp info */
 	unsigned int ar_loccookie:1;		/* use location based readdir
 						   cookies */
-	int ar_commit;				/* Commit interval */
-	int ar_statfs_quantum;			/* The fast statfs interval */
-	int ar_quota_quantum;			/* The quota interval */
-	int ar_statfs_percent;			/* The % change to force sync */
+	s32 ar_commit;				/* Commit interval */
+	s32 ar_statfs_quantum;			/* The fast statfs interval */
+	s32 ar_quota_quantum;			/* The quota interval */
+	s32 ar_statfs_percent;			/* The % change to force sync */
 };
 
 struct gfs2_tune {
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index b041cb8ae383..8e8d624170a1 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -24,6 +24,7 @@
 #include <linux/lockdep.h>
 #include <linux/module.h>
 #include <linux/backing-dev.h>
+#include <linux/fs_parser.h>
 
 #include "gfs2.h"
 #include "incore.h"
@@ -1024,16 +1025,17 @@ void gfs2_online_uevent(struct gfs2_sbd *sdp)
 }
 
 /**
- * fill_super - Read in superblock
+ * gfs2_fill_super - Read in superblock
  * @sb: The VFS superblock
- * @data: Mount options
+ * @args: Mount options
  * @silent: Don't complain if it's not a GFS2 filesystem
  *
- * Returns: errno
+ * Returns: -errno
  */
-
-static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent)
+static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
 {
+	struct gfs2_args *args = fc->fs_private;
+	int silent = fc->sb_flags & SB_SILENT;
 	struct gfs2_sbd *sdp;
 	struct gfs2_holder mount_gh;
 	int error;
@@ -1200,161 +1202,415 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent
 	return error;
 }
 
-static int set_gfs2_super(struct super_block *s, void *data)
+/**
+ * gfs2_get_tree - Get the GFS2 superblock and root directory
+ * @fc: The filesystem context
+ *
+ * Returns: 0 or -errno on error
+ */
+static int gfs2_get_tree(struct fs_context *fc)
 {
-	s->s_bdev = data;
-	s->s_dev = s->s_bdev->bd_dev;
-	s->s_bdi = bdi_get(s->s_bdev->bd_bdi);
+	struct gfs2_args *args = fc->fs_private;
+	struct gfs2_sbd *sdp;
+	int error;
+
+	error = vfs_get_block_super(fc, gfs2_fill_super);
+	if (error)
+		return error;
+
+	sdp = fc->root->d_sb->s_fs_info;
+	dput(fc->root);
+	if (args->ar_meta)
+		fc->root = dget(sdp->sd_master_dir);
+	else
+		fc->root = dget(sdp->sd_root_dir);
 	return 0;
 }
 
-static int test_gfs2_super(struct super_block *s, void *ptr)
+static void gfs2_fc_free(struct fs_context *fc)
 {
-	struct block_device *bdev = ptr;
-	return (bdev == s->s_bdev);
+	struct gfs2_args *args = fc->fs_private;
+
+	kfree(args);
 }
 
-/**
- * gfs2_mount - Get the GFS2 superblock
- * @fs_type: The GFS2 filesystem type
- * @flags: Mount flags
- * @dev_name: The name of the device
- * @data: The mount arguments
- *
- * Q. Why not use get_sb_bdev() ?
- * A. We need to select one of two root directories to mount, independent
- *    of whether this is the initial, or subsequent, mount of this sb
- *
- * Returns: 0 or -ve on error
- */
+enum gfs2_param {
+	Opt_lockproto,
+	Opt_locktable,
+	Opt_hostdata,
+	Opt_spectator,
+	Opt_ignore_local_fs,
+	Opt_localflocks,
+	Opt_localcaching,
+	Opt_debug,
+	Opt_upgrade,
+	Opt_acl,
+	Opt_quota,
+	Opt_suiddir,
+	Opt_data,
+	Opt_meta,
+	Opt_discard,
+	Opt_commit,
+	Opt_errors,
+	Opt_statfs_quantum,
+	Opt_statfs_percent,
+	Opt_quota_quantum,
+	Opt_barrier,
+	Opt_rgrplvb,
+	Opt_loccookie,
+};
+
+enum opt_quota {
+	Opt_quota_unset = 0,
+	Opt_quota_off,
+	Opt_quota_account,
+	Opt_quota_on,
+};
+
+static const unsigned int opt_quota_values[] = {
+	[Opt_quota_off]     = GFS2_QUOTA_OFF,
+	[Opt_quota_account] = GFS2_QUOTA_ACCOUNT,
+	[Opt_quota_on]      = GFS2_QUOTA_ON,
+};
+
+enum opt_data {
+	Opt_data_writeback = GFS2_DATA_WRITEBACK,
+	Opt_data_ordered   = GFS2_DATA_ORDERED,
+};
+
+enum opt_errors {
+	Opt_errors_withdraw = GFS2_ERRORS_WITHDRAW,
+	Opt_errors_panic    = GFS2_ERRORS_PANIC,
+};
 
-static struct dentry *gfs2_mount(struct file_system_type *fs_type, int flags,
-		       const char *dev_name, void *data)
+static const struct fs_parameter_spec gfs2_param_specs[] = {
+	fsparam_string ("lockproto",          Opt_lockproto),
+	fsparam_string ("locktable",          Opt_locktable),
+	fsparam_string ("hostdata",           Opt_hostdata),
+	fsparam_flag   ("spectator",          Opt_spectator),
+	fsparam_flag   ("norecovery",         Opt_spectator),
+	fsparam_flag   ("ignore_local_fs",    Opt_ignore_local_fs),
+	fsparam_flag   ("localflocks",        Opt_localflocks),
+	fsparam_flag   ("localcaching",       Opt_localcaching),
+	fsparam_flag_no("debug",              Opt_debug),
+	fsparam_flag   ("upgrade",            Opt_upgrade),
+	fsparam_flag_no("acl",                Opt_acl),
+	fsparam_flag_no("suiddir",            Opt_suiddir),
+	fsparam_enum   ("data",               Opt_data),
+	fsparam_flag   ("meta",               Opt_meta),
+	fsparam_flag_no("discard",            Opt_discard),
+	fsparam_s32    ("commit",             Opt_commit),
+	fsparam_enum   ("errors",             Opt_errors),
+	fsparam_s32    ("statfs_quantum",     Opt_statfs_quantum),
+	fsparam_s32    ("statfs_percent",     Opt_statfs_percent),
+	fsparam_s32    ("quota_quantum",      Opt_quota_quantum),
+	fsparam_flag_no("barrier",            Opt_barrier),
+	fsparam_flag_no("rgrplvb",            Opt_rgrplvb),
+	fsparam_flag_no("loccookie",          Opt_loccookie),
+	/* quota can be a flag or an enum so it gets special treatment */
+	__fsparam(fs_param_is_enum, "quota", Opt_quota, fs_param_neg_with_no|fs_param_v_optional),
+	{}
+};
+
+static const struct fs_parameter_enum gfs2_param_enums[] = {
+	{ Opt_quota,    "off",        Opt_quota_off },
+	{ Opt_quota,    "account",    Opt_quota_account },
+	{ Opt_quota,    "on",         Opt_quota_on },
+	{ Opt_data,     "writeback",  Opt_data_writeback },
+	{ Opt_data,     "ordered",    Opt_data_ordered },
+	{ Opt_errors,   "withdraw",   Opt_errors_withdraw },
+	{ Opt_errors,   "panic",      Opt_errors_panic },
+	{}
+};
+
+const struct fs_parameter_description gfs2_fs_parameters = {
+	.name = "gfs2",
+	.specs = gfs2_param_specs,
+	.enums = gfs2_param_enums,
+};
+
+/* Parse a single mount parameter */
+static int gfs2_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	struct block_device *bdev;
-	struct super_block *s;
-	fmode_t mode = FMODE_READ | FMODE_EXCL;
+	struct gfs2_args *args = fc->fs_private;
+	struct fs_parse_result result;
+	int o;
+
+	o = fs_parse(fc, &gfs2_fs_parameters, param, &result);
+	if (o < 0)
+		return o;
+
+	switch (o) {
+	case Opt_lockproto:
+		strlcpy(args->ar_lockproto, param->string, GFS2_LOCKNAME_LEN);
+		break;
+	case Opt_locktable:
+		strlcpy(args->ar_locktable, param->string, GFS2_LOCKNAME_LEN);
+		break;
+	case Opt_hostdata:
+		strlcpy(args->ar_hostdata, param->string, GFS2_LOCKNAME_LEN);
+		break;
+	case Opt_spectator:
+		args->ar_spectator = 1;
+		break;
+	case Opt_ignore_local_fs:
+		/* Retained for backwards compat only */
+		break;
+	case Opt_localflocks:
+		args->ar_localflocks = 1;
+		break;
+	case Opt_localcaching:
+		/* Retained for backwards compat only */
+		break;
+	case Opt_debug:
+		if (result.boolean && args->ar_errors == GFS2_ERRORS_PANIC)
+			return invalf(fc, "gfs2: -o debug and -o errors=panic are mutually exclusive");
+		args->ar_debug = result.boolean;
+		break;
+	case Opt_upgrade:
+		/* Retained for backwards compat only */
+		break;
+	case Opt_acl:
+		args->ar_posix_acl = result.boolean;
+		break;
+	case Opt_quota:
+		/* The quota option can be a flag or an enum. A non-zero int_32
+		   result means that we have an enum index. Otherwise we have
+		   to rely on the 'negated' flag to tell us whether 'quota' or
+		   'noquota' was specified. */
+		if (result.negated)
+			args->ar_quota = GFS2_QUOTA_OFF;
+		else if (result.int_32 > 0)
+			args->ar_quota = opt_quota_values[result.int_32];
+		else
+			args->ar_quota = GFS2_QUOTA_ON;
+		break;
+	case Opt_suiddir:
+		args->ar_suiddir = result.boolean;
+		break;
+	case Opt_data:
+		/* The uint_32 result maps directly to GFS2_DATA_* */
+		args->ar_data = result.uint_32;
+		break;
+	case Opt_meta:
+		args->ar_meta = 1;
+		break;
+	case Opt_discard:
+		args->ar_discard = result.boolean;
+		break;
+	case Opt_commit:
+		if (result.int_32 <= 0)
+			return invalf(fc, "gfs2: commit mount option requires a positive numeric argument");
+		args->ar_commit = result.int_32;
+		break;
+	case Opt_statfs_quantum:
+		if (result.int_32 < 0)
+			return invalf(fc, "gfs2: statfs_quantum mount option requires a non-negative numeric argument");
+		args->ar_statfs_quantum = result.int_32;
+		break;
+	case Opt_quota_quantum:
+		if (result.int_32 <= 0)
+			return invalf(fc, "gfs2: quota_quantum mount option requires a positive numeric argument");
+		args->ar_quota_quantum = result.int_32;
+		break;
+	case Opt_statfs_percent:
+		if (result.int_32 < 0 || result.int_32 > 100)
+			return invalf(fc, "gfs2: statfs_percent mount option requires a numeric argument between 0 and 100");
+		args->ar_statfs_percent = result.int_32;
+		break;
+	case Opt_errors:
+		if (args->ar_debug && result.uint_32 == GFS2_ERRORS_PANIC)
+			return invalf(fc, "gfs2: -o debug and -o errors=panic are mutually exclusive");
+		args->ar_errors = result.uint_32;
+		break;
+	case Opt_barrier:
+		args->ar_nobarrier = result.boolean;
+		break;
+	case Opt_rgrplvb:
+		args->ar_rgrplvb = result.boolean;
+		break;
+	case Opt_loccookie:
+		args->ar_loccookie = result.boolean;
+		break;
+	default:
+		return invalf(fc, "gfs2: invalid mount option: %s", param->key);
+	}
+	return 0;
+}
+
+static int gfs2_reconfigure(struct fs_context *fc)
+{
+	struct super_block *sb = fc->root->d_sb;
+	struct gfs2_sbd *sdp = sb->s_fs_info;
+	struct gfs2_args *oldargs = &sdp->sd_args;
+	struct gfs2_args *newargs = fc->fs_private;
+	struct gfs2_tune *gt = &sdp->sd_tune;
 	int error;
-	struct gfs2_args args;
-	struct gfs2_sbd *sdp;
 
-	if (!(flags & SB_RDONLY))
-		mode |= FMODE_WRITE;
+	sync_filesystem(sb);
 
-	bdev = blkdev_get_by_path(dev_name, mode, fs_type);
-	if (IS_ERR(bdev))
-		return ERR_CAST(bdev);
+	spin_lock(&gt->gt_spin);
+	oldargs->ar_commit = gt->gt_logd_secs;
+	oldargs->ar_quota_quantum = gt->gt_quota_quantum;
+	if (gt->gt_statfs_slow)
+		oldargs->ar_statfs_quantum = 0;
+	else
+		oldargs->ar_statfs_quantum = gt->gt_statfs_quantum;
+	spin_unlock(&gt->gt_spin);
 
-	/*
-	 * once the super is inserted into the list by sget, s_umount
-	 * will protect the lockfs code from trying to start a snapshot
-	 * while we are mounting
-	 */
-	mutex_lock(&bdev->bd_fsfreeze_mutex);
-	if (bdev->bd_fsfreeze_count > 0) {
-		mutex_unlock(&bdev->bd_fsfreeze_mutex);
-		error = -EBUSY;
-		goto error_bdev;
+	if (strcmp(newargs->ar_lockproto, oldargs->ar_lockproto)) {
+		errorf(fc, "gfs2: reconfiguration of locking protocol not allowed");
+		return -EINVAL;
 	}
-	s = sget(fs_type, test_gfs2_super, set_gfs2_super, flags, bdev);
-	mutex_unlock(&bdev->bd_fsfreeze_mutex);
-	error = PTR_ERR(s);
-	if (IS_ERR(s))
-		goto error_bdev;
-
-	if (s->s_root) {
-		/*
-		 * s_umount nests inside bd_mutex during
-		 * __invalidate_device().  blkdev_put() acquires
-		 * bd_mutex and can't be called under s_umount.  Drop
-		 * s_umount temporarily.  This is safe as we're
-		 * holding an active reference.
-		 */
-		up_write(&s->s_umount);
-		blkdev_put(bdev, mode);
-		down_write(&s->s_umount);
-	} else {
-		/* s_mode must be set before deactivate_locked_super calls */
-		s->s_mode = mode;
+	if (strcmp(newargs->ar_locktable, oldargs->ar_locktable)) {
+		errorf(fc, "gfs2: reconfiguration of lock table not allowed");
+		return -EINVAL;
 	}
-
-	memset(&args, 0, sizeof(args));
-	args.ar_quota = GFS2_QUOTA_DEFAULT;
-	args.ar_data = GFS2_DATA_DEFAULT;
-	args.ar_commit = 30;
-	args.ar_statfs_quantum = 30;
-	args.ar_quota_quantum = 60;
-	args.ar_errors = GFS2_ERRORS_DEFAULT;
-
-	error = gfs2_mount_args(&args, data);
-	if (error) {
-		pr_warn("can't parse mount arguments\n");
-		goto error_super;
+	if (strcmp(newargs->ar_hostdata, oldargs->ar_hostdata)) {
+		errorf(fc, "gfs2: reconfiguration of host data not allowed");
+		return -EINVAL;
 	}
+	if (newargs->ar_spectator != oldargs->ar_spectator) {
+		errorf(fc, "gfs2: reconfiguration of spectator mode not allowed");
+		return -EINVAL;
+	}
+	if (newargs->ar_localflocks != oldargs->ar_localflocks) {
+		errorf(fc, "gfs2: reconfiguration of localflocks not allowed");
+		return -EINVAL;
+	}
+	if (newargs->ar_meta != oldargs->ar_meta) {
+		errorf(fc, "gfs2: switching between gfs2 and gfs2meta not allowed");
+		return -EINVAL;
+	}
+	if (oldargs->ar_spectator)
+		fc->sb_flags |= SB_RDONLY;
 
-	if (s->s_root) {
-		error = -EBUSY;
-		if ((flags ^ s->s_flags) & SB_RDONLY)
-			goto error_super;
-	} else {
-		snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
-		sb_set_blocksize(s, block_size(bdev));
-		error = fill_super(s, &args, flags & SB_SILENT ? 1 : 0);
-		if (error)
-			goto error_super;
-		s->s_flags |= SB_ACTIVE;
-		bdev->bd_super = s;
+	if ((sb->s_flags ^ fc->sb_flags) & SB_RDONLY) {
+		if (fc->sb_flags & SB_RDONLY) {
+			error = gfs2_make_fs_ro(sdp);
+			if (error) {
+				errorf(fc, "gfs2: unable to remount read-only");
+				return error;
+			}
+		} else {
+			error = gfs2_make_fs_rw(sdp);
+			if (error) {
+				errorf(fc, "gfs2: unable to remount read-write");
+				return error;
+			}
+		}
 	}
+	sdp->sd_args = *newargs;
 
-	sdp = s->s_fs_info;
-	if (args.ar_meta)
-		return dget(sdp->sd_master_dir);
+	if (sdp->sd_args.ar_posix_acl)
+		sb->s_flags |= SB_POSIXACL;
+	else
+		sb->s_flags &= ~SB_POSIXACL;
+	if (sdp->sd_args.ar_nobarrier)
+		set_bit(SDF_NOBARRIERS, &sdp->sd_flags);
 	else
-		return dget(sdp->sd_root_dir);
-
-error_super:
-	deactivate_locked_super(s);
-	return ERR_PTR(error);
-error_bdev:
-	blkdev_put(bdev, mode);
-	return ERR_PTR(error);
+		clear_bit(SDF_NOBARRIERS, &sdp->sd_flags);
+	spin_lock(&gt->gt_spin);
+	gt->gt_logd_secs = newargs->ar_commit;
+	gt->gt_quota_quantum = newargs->ar_quota_quantum;
+	if (newargs->ar_statfs_quantum) {
+		gt->gt_statfs_slow = 0;
+		gt->gt_statfs_quantum = newargs->ar_statfs_quantum;
+	}
+	else {
+		gt->gt_statfs_slow = 1;
+		gt->gt_statfs_quantum = 30;
+	}
+	spin_unlock(&gt->gt_spin);
+
+	gfs2_online_uevent(sdp);
+	return 0;
+}
+
+static const struct fs_context_operations gfs2_context_ops = {
+	.free        = gfs2_fc_free,
+	.parse_param = gfs2_parse_param,
+	.get_tree    = gfs2_get_tree,
+	.reconfigure = gfs2_reconfigure,
+};
+
+/* Set up the filesystem mount context */
+static int gfs2_init_fs_context(struct fs_context *fc)
+{
+	struct gfs2_args *args;
+
+	args = kzalloc(sizeof(*args), GFP_KERNEL);
+	if (args == NULL)
+		return -ENOMEM;
+
+	args->ar_quota = GFS2_QUOTA_DEFAULT;
+	args->ar_data = GFS2_DATA_DEFAULT;
+	args->ar_commit = 30;
+	args->ar_statfs_quantum = 30;
+	args->ar_quota_quantum = 60;
+	args->ar_errors = GFS2_ERRORS_DEFAULT;
+
+	fc->fs_private = args;
+	fc->ops = &gfs2_context_ops;
+	return 0;
 }
 
-static int set_meta_super(struct super_block *s, void *ptr)
+static int set_meta_super(struct super_block *s, struct fs_context *fc)
 {
 	return -EINVAL;
 }
 
-static struct dentry *gfs2_mount_meta(struct file_system_type *fs_type,
-			int flags, const char *dev_name, void *data)
+static int test_meta_super(struct super_block *s, struct fs_context *fc)
+{
+	return (fc->bdev == s->s_bdev);
+}
+
+static int gfs2_meta_get_tree(struct fs_context *fc)
 {
 	struct super_block *s;
 	struct gfs2_sbd *sdp;
 	struct path path;
 	int error;
 
-	if (!dev_name || !*dev_name)
-		return ERR_PTR(-EINVAL);
+	if (!fc->source || !*fc->source)
+		return -EINVAL;
 
-	error = kern_path(dev_name, LOOKUP_FOLLOW, &path);
+	error = kern_path(fc->source, LOOKUP_FOLLOW, &path);
 	if (error) {
 		pr_warn("path_lookup on %s returned error %d\n",
-			dev_name, error);
-		return ERR_PTR(error);
+		        fc->source, error);
+		return error;
 	}
-	s = sget(&gfs2_fs_type, test_gfs2_super, set_meta_super, flags,
-		 path.dentry->d_sb->s_bdev);
+	fc->fs_type = &gfs2_fs_type;
+	fc->bdev = path.dentry->d_sb->s_bdev;
+	s = sget_fc(fc, test_meta_super, set_meta_super);
 	path_put(&path);
 	if (IS_ERR(s)) {
 		pr_warn("gfs2 mount does not exist\n");
-		return ERR_CAST(s);
+		return PTR_ERR(s);
 	}
-	if ((flags ^ s->s_flags) & SB_RDONLY) {
+	if ((fc->sb_flags ^ s->s_flags) & SB_RDONLY) {
 		deactivate_locked_super(s);
-		return ERR_PTR(-EBUSY);
+		return -EBUSY;
 	}
 	sdp = s->s_fs_info;
-	return dget(sdp->sd_master_dir);
+	fc->root = dget(sdp->sd_master_dir);
+	return 0;
+}
+
+static const struct fs_context_operations gfs2_meta_context_ops = {
+	.get_tree    = gfs2_meta_get_tree,
+};
+
+static int gfs2_meta_init_fs_context(struct fs_context *fc)
+{
+	int ret = gfs2_init_fs_context(fc);
+
+	if (ret)
+		return ret;
+
+	fc->ops = &gfs2_meta_context_ops;
+	return 0;
 }
 
 static void gfs2_kill_sb(struct super_block *sb)
@@ -1379,7 +1635,8 @@ static void gfs2_kill_sb(struct super_block *sb)
 struct file_system_type gfs2_fs_type = {
 	.name = "gfs2",
 	.fs_flags = FS_REQUIRES_DEV,
-	.mount = gfs2_mount,
+	.init_fs_context = gfs2_init_fs_context,
+	.parameters = &gfs2_fs_parameters,
 	.kill_sb = gfs2_kill_sb,
 	.owner = THIS_MODULE,
 };
@@ -1388,7 +1645,7 @@ MODULE_ALIAS_FS("gfs2");
 struct file_system_type gfs2meta_fs_type = {
 	.name = "gfs2meta",
 	.fs_flags = FS_REQUIRES_DEV,
-	.mount = gfs2_mount_meta,
+	.init_fs_context = gfs2_meta_init_fs_context,
 	.owner = THIS_MODULE,
 };
 MODULE_ALIAS_FS("gfs2meta");
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index ca71163ff7cf..18917f8ae7ea 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -46,258 +46,6 @@
 #include "sys.h"
 #include "xattr.h"
 
-#define args_neq(a1, a2, x) ((a1)->ar_##x != (a2)->ar_##x)
-
-enum {
-	Opt_lockproto,
-	Opt_locktable,
-	Opt_hostdata,
-	Opt_spectator,
-	Opt_ignore_local_fs,
-	Opt_localflocks,
-	Opt_localcaching,
-	Opt_debug,
-	Opt_nodebug,
-	Opt_upgrade,
-	Opt_acl,
-	Opt_noacl,
-	Opt_quota_off,
-	Opt_quota_account,
-	Opt_quota_on,
-	Opt_quota,
-	Opt_noquota,
-	Opt_suiddir,
-	Opt_nosuiddir,
-	Opt_data_writeback,
-	Opt_data_ordered,
-	Opt_meta,
-	Opt_discard,
-	Opt_nodiscard,
-	Opt_commit,
-	Opt_err_withdraw,
-	Opt_err_panic,
-	Opt_statfs_quantum,
-	Opt_statfs_percent,
-	Opt_quota_quantum,
-	Opt_barrier,
-	Opt_nobarrier,
-	Opt_rgrplvb,
-	Opt_norgrplvb,
-	Opt_loccookie,
-	Opt_noloccookie,
-	Opt_error,
-};
-
-static const match_table_t tokens = {
-	{Opt_lockproto, "lockproto=%s"},
-	{Opt_locktable, "locktable=%s"},
-	{Opt_hostdata, "hostdata=%s"},
-	{Opt_spectator, "spectator"},
-	{Opt_spectator, "norecovery"},
-	{Opt_ignore_local_fs, "ignore_local_fs"},
-	{Opt_localflocks, "localflocks"},
-	{Opt_localcaching, "localcaching"},
-	{Opt_debug, "debug"},
-	{Opt_nodebug, "nodebug"},
-	{Opt_upgrade, "upgrade"},
-	{Opt_acl, "acl"},
-	{Opt_noacl, "noacl"},
-	{Opt_quota_off, "quota=off"},
-	{Opt_quota_account, "quota=account"},
-	{Opt_quota_on, "quota=on"},
-	{Opt_quota, "quota"},
-	{Opt_noquota, "noquota"},
-	{Opt_suiddir, "suiddir"},
-	{Opt_nosuiddir, "nosuiddir"},
-	{Opt_data_writeback, "data=writeback"},
-	{Opt_data_ordered, "data=ordered"},
-	{Opt_meta, "meta"},
-	{Opt_discard, "discard"},
-	{Opt_nodiscard, "nodiscard"},
-	{Opt_commit, "commit=%d"},
-	{Opt_err_withdraw, "errors=withdraw"},
-	{Opt_err_panic, "errors=panic"},
-	{Opt_statfs_quantum, "statfs_quantum=%d"},
-	{Opt_statfs_percent, "statfs_percent=%d"},
-	{Opt_quota_quantum, "quota_quantum=%d"},
-	{Opt_barrier, "barrier"},
-	{Opt_nobarrier, "nobarrier"},
-	{Opt_rgrplvb, "rgrplvb"},
-	{Opt_norgrplvb, "norgrplvb"},
-	{Opt_loccookie, "loccookie"},
-	{Opt_noloccookie, "noloccookie"},
-	{Opt_error, NULL}
-};
-
-/**
- * gfs2_mount_args - Parse mount options
- * @args: The structure into which the parsed options will be written
- * @options: The options to parse
- *
- * Return: errno
- */
-
-int gfs2_mount_args(struct gfs2_args *args, char *options)
-{
-	char *o;
-	int token;
-	substring_t tmp[MAX_OPT_ARGS];
-	int rv;
-
-	/* Split the options into tokens with the "," character and
-	   process them */
-
-	while (1) {
-		o = strsep(&options, ",");
-		if (o == NULL)
-			break;
-		if (*o == '\0')
-			continue;
-
-		token = match_token(o, tokens, tmp);
-		switch (token) {
-		case Opt_lockproto:
-			match_strlcpy(args->ar_lockproto, &tmp[0],
-				      GFS2_LOCKNAME_LEN);
-			break;
-		case Opt_locktable:
-			match_strlcpy(args->ar_locktable, &tmp[0],
-				      GFS2_LOCKNAME_LEN);
-			break;
-		case Opt_hostdata:
-			match_strlcpy(args->ar_hostdata, &tmp[0],
-				      GFS2_LOCKNAME_LEN);
-			break;
-		case Opt_spectator:
-			args->ar_spectator = 1;
-			break;
-		case Opt_ignore_local_fs:
-			/* Retained for backwards compat only */
-			break;
-		case Opt_localflocks:
-			args->ar_localflocks = 1;
-			break;
-		case Opt_localcaching:
-			/* Retained for backwards compat only */
-			break;
-		case Opt_debug:
-			if (args->ar_errors == GFS2_ERRORS_PANIC) {
-				pr_warn("-o debug and -o errors=panic are mutually exclusive\n");
-				return -EINVAL;
-			}
-			args->ar_debug = 1;
-			break;
-		case Opt_nodebug:
-			args->ar_debug = 0;
-			break;
-		case Opt_upgrade:
-			/* Retained for backwards compat only */
-			break;
-		case Opt_acl:
-			args->ar_posix_acl = 1;
-			break;
-		case Opt_noacl:
-			args->ar_posix_acl = 0;
-			break;
-		case Opt_quota_off:
-		case Opt_noquota:
-			args->ar_quota = GFS2_QUOTA_OFF;
-			break;
-		case Opt_quota_account:
-			args->ar_quota = GFS2_QUOTA_ACCOUNT;
-			break;
-		case Opt_quota_on:
-		case Opt_quota:
-			args->ar_quota = GFS2_QUOTA_ON;
-			break;
-		case Opt_suiddir:
-			args->ar_suiddir = 1;
-			break;
-		case Opt_nosuiddir:
-			args->ar_suiddir = 0;
-			break;
-		case Opt_data_writeback:
-			args->ar_data = GFS2_DATA_WRITEBACK;
-			break;
-		case Opt_data_ordered:
-			args->ar_data = GFS2_DATA_ORDERED;
-			break;
-		case Opt_meta:
-			args->ar_meta = 1;
-			break;
-		case Opt_discard:
-			args->ar_discard = 1;
-			break;
-		case Opt_nodiscard:
-			args->ar_discard = 0;
-			break;
-		case Opt_commit:
-			rv = match_int(&tmp[0], &args->ar_commit);
-			if (rv || args->ar_commit <= 0) {
-				pr_warn("commit mount option requires a positive numeric argument\n");
-				return rv ? rv : -EINVAL;
-			}
-			break;
-		case Opt_statfs_quantum:
-			rv = match_int(&tmp[0], &args->ar_statfs_quantum);
-			if (rv || args->ar_statfs_quantum < 0) {
-				pr_warn("statfs_quantum mount option requires a non-negative numeric argument\n");
-				return rv ? rv : -EINVAL;
-			}
-			break;
-		case Opt_quota_quantum:
-			rv = match_int(&tmp[0], &args->ar_quota_quantum);
-			if (rv || args->ar_quota_quantum <= 0) {
-				pr_warn("quota_quantum mount option requires a positive numeric argument\n");
-				return rv ? rv : -EINVAL;
-			}
-			break;
-		case Opt_statfs_percent:
-			rv = match_int(&tmp[0], &args->ar_statfs_percent);
-			if (rv || args->ar_statfs_percent < 0 ||
-			    args->ar_statfs_percent > 100) {
-				pr_warn("statfs_percent mount option requires a numeric argument between 0 and 100\n");
-				return rv ? rv : -EINVAL;
-			}
-			break;
-		case Opt_err_withdraw:
-			args->ar_errors = GFS2_ERRORS_WITHDRAW;
-			break;
-		case Opt_err_panic:
-			if (args->ar_debug) {
-				pr_warn("-o debug and -o errors=panic are mutually exclusive\n");
-				return -EINVAL;
-			}
-			args->ar_errors = GFS2_ERRORS_PANIC;
-			break;
-		case Opt_barrier:
-			args->ar_nobarrier = 0;
-			break;
-		case Opt_nobarrier:
-			args->ar_nobarrier = 1;
-			break;
-		case Opt_rgrplvb:
-			args->ar_rgrplvb = 1;
-			break;
-		case Opt_norgrplvb:
-			args->ar_rgrplvb = 0;
-			break;
-		case Opt_loccookie:
-			args->ar_loccookie = 1;
-			break;
-		case Opt_noloccookie:
-			args->ar_loccookie = 0;
-			break;
-		case Opt_error:
-		default:
-			pr_warn("invalid mount option: %s\n", o);
-			return -EINVAL;
-		}
-	}
-
-	return 0;
-}
-
 /**
  * gfs2_jindex_free - Clear all the journal index information
  * @sdp: The GFS2 superblock
@@ -844,7 +592,7 @@ static void gfs2_dirty_inode(struct inode *inode, int flags)
  * Returns: errno
  */
 
-static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
+int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
 {
 	struct gfs2_holder freeze_gh;
 	int error;
@@ -1223,86 +971,6 @@ static int gfs2_statfs(struct dentry *dentry, struct kstatfs *buf)
 	return 0;
 }
 
-/**
- * gfs2_remount_fs - called when the FS is remounted
- * @sb:  the filesystem
- * @flags:  the remount flags
- * @data:  extra data passed in (not used right now)
- *
- * Returns: errno
- */
-
-static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)
-{
-	struct gfs2_sbd *sdp = sb->s_fs_info;
-	struct gfs2_args args = sdp->sd_args; /* Default to current settings */
-	struct gfs2_tune *gt = &sdp->sd_tune;
-	int error;
-
-	sync_filesystem(sb);
-
-	spin_lock(&gt->gt_spin);
-	args.ar_commit = gt->gt_logd_secs;
-	args.ar_quota_quantum = gt->gt_quota_quantum;
-	if (gt->gt_statfs_slow)
-		args.ar_statfs_quantum = 0;
-	else
-		args.ar_statfs_quantum = gt->gt_statfs_quantum;
-	spin_unlock(&gt->gt_spin);
-	error = gfs2_mount_args(&args, data);
-	if (error)
-		return error;
-
-	/* Not allowed to change locking details */
-	if (strcmp(args.ar_lockproto, sdp->sd_args.ar_lockproto) ||
-	    strcmp(args.ar_locktable, sdp->sd_args.ar_locktable) ||
-	    strcmp(args.ar_hostdata, sdp->sd_args.ar_hostdata))
-		return -EINVAL;
-
-	/* Some flags must not be changed */
-	if (args_neq(&args, &sdp->sd_args, spectator) ||
-	    args_neq(&args, &sdp->sd_args, localflocks) ||
-	    args_neq(&args, &sdp->sd_args, meta))
-		return -EINVAL;
-
-	if (sdp->sd_args.ar_spectator)
-		*flags |= SB_RDONLY;
-
-	if ((sb->s_flags ^ *flags) & SB_RDONLY) {
-		if (*flags & SB_RDONLY)
-			error = gfs2_make_fs_ro(sdp);
-		else
-			error = gfs2_make_fs_rw(sdp);
-		if (error)
-			return error;
-	}
-
-	sdp->sd_args = args;
-	if (sdp->sd_args.ar_posix_acl)
-		sb->s_flags |= SB_POSIXACL;
-	else
-		sb->s_flags &= ~SB_POSIXACL;
-	if (sdp->sd_args.ar_nobarrier)
-		set_bit(SDF_NOBARRIERS, &sdp->sd_flags);
-	else
-		clear_bit(SDF_NOBARRIERS, &sdp->sd_flags);
-	spin_lock(&gt->gt_spin);
-	gt->gt_logd_secs = args.ar_commit;
-	gt->gt_quota_quantum = args.ar_quota_quantum;
-	if (args.ar_statfs_quantum) {
-		gt->gt_statfs_slow = 0;
-		gt->gt_statfs_quantum = args.ar_statfs_quantum;
-	}
-	else {
-		gt->gt_statfs_slow = 1;
-		gt->gt_statfs_quantum = 30;
-	}
-	spin_unlock(&gt->gt_spin);
-
-	gfs2_online_uevent(sdp);
-	return 0;
-}
-
 /**
  * gfs2_drop_inode - Drop an inode (test for remote unlink)
  * @inode: The inode to drop
@@ -1758,7 +1426,6 @@ const struct super_operations gfs2_super_ops = {
 	.freeze_super		= gfs2_freeze,
 	.thaw_super		= gfs2_unfreeze,
 	.statfs			= gfs2_statfs,
-	.remount_fs		= gfs2_remount_fs,
 	.drop_inode		= gfs2_drop_inode,
 	.show_options		= gfs2_show_options,
 };
diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h
index 73c97dccae21..b9dc2065dd72 100644
--- a/fs/gfs2/super.h
+++ b/fs/gfs2/super.h
@@ -27,8 +27,6 @@ static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp)
 
 extern void gfs2_jindex_free(struct gfs2_sbd *sdp);
 
-extern int gfs2_mount_args(struct gfs2_args *args, char *data);
-
 extern struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid);
 extern int gfs2_jdesc_check(struct gfs2_jdesc *jd);
 
@@ -36,6 +34,7 @@ extern int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename,
 				     struct gfs2_inode **ipp);
 
 extern int gfs2_make_fs_rw(struct gfs2_sbd *sdp);
+extern int gfs2_make_fs_ro(struct gfs2_sbd *sdp);
 extern void gfs2_online_uevent(struct gfs2_sbd *sdp);
 extern int gfs2_statfs_init(struct gfs2_sbd *sdp);
 extern void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free,


^ permalink raw reply related	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 57/68] vfs: Convert pstore to use the new mount API
  2019-03-27 23:47 ` [RFC PATCH 57/68] vfs: Convert pstore " David Howells
@ 2019-03-28  0:24   ` Kees Cook
  0 siblings, 0 replies; 87+ messages in thread
From: Kees Cook @ 2019-03-28  0:24 UTC (permalink / raw)
  To: David Howells
  Cc: Al Viro, Anton Vorontsov, Colin Cross, Tony Luck, linux-fsdevel, LKML

On Wed, Mar 27, 2019 at 4:47 PM David Howells <dhowells@redhat.com> wrote:
>
> Convert the pstore filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
>
> See Documentation/filesystems/mount_api.txt for more information.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: Kees Cook <keescook@chromium.org>
> cc: Anton Vorontsov <anton@enomsg.org>
> cc: Colin Cross <ccross@android.com>
> cc: Tony Luck <tony.luck@intel.com>

Acked-by: Kees Cook <keescook@chromium.org>

-Kees

> ---
>
>  fs/pstore/inode.c |  109 +++++++++++++++++++++++++++++++++++------------------
>  1 file changed, 71 insertions(+), 38 deletions(-)
>
> diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
> index c60ee46f3e39..bb4e4cabd11f 100644
> --- a/fs/pstore/inode.c
> +++ b/fs/pstore/inode.c
> @@ -19,6 +19,8 @@
>
>  #include <linux/module.h>
>  #include <linux/fs.h>
> +#include <linux/fs_context.h>
> +#include <linux/fs_parser.h>
>  #include <linux/fsnotify.h>
>  #include <linux/pagemap.h>
>  #include <linux/highmem.h>
> @@ -26,10 +28,8 @@
>  #include <linux/init.h>
>  #include <linux/list.h>
>  #include <linux/string.h>
> -#include <linux/mount.h>
>  #include <linux/seq_file.h>
>  #include <linux/ramfs.h>
> -#include <linux/parser.h>
>  #include <linux/sched.h>
>  #include <linux/magic.h>
>  #include <linux/pstore.h>
> @@ -227,38 +227,48 @@ static struct inode *pstore_get_inode(struct super_block *sb)
>         return inode;
>  }
>
> +struct pstore_fs_context {
> +       unsigned int    kmsg_bytes;
> +};
> +
>  enum {
> -       Opt_kmsg_bytes, Opt_err
> +       Opt_kmsg_bytes,
>  };
>
> -static const match_table_t tokens = {
> -       {Opt_kmsg_bytes, "kmsg_bytes=%u"},
> -       {Opt_err, NULL}
> +static const struct fs_parameter_spec pstore_param_specs[] = {
> +       fsparam_u32     ("kmsg_bytes",          Opt_kmsg_bytes),
> +       {}
>  };
>
> -static void parse_options(char *options)
> -{
> -       char            *p;
> -       substring_t     args[MAX_OPT_ARGS];
> -       int             option;
> +static const struct fs_parameter_description pstore_fs_parameters = {
> +       .name           = "pstore",
> +       .specs          = pstore_param_specs,
> +};
>
> -       if (!options)
> -               return;
> +static int pstore_parse_param(struct fs_context *fc, struct fs_parameter *param)
> +{
> +       struct pstore_fs_context *ctx = fc->fs_private;
> +       struct fs_parse_result result;
> +       int opt;
> +
> +       opt = fs_parse(fc, &pstore_fs_parameters, param, &result);
> +       if (opt < 0)
> +               return opt;
> +
> +       switch (opt) {
> +       case Opt_kmsg_bytes:
> +               ctx->kmsg_bytes = result.uint_32;
> +               break;
> +       }
>
> -       while ((p = strsep(&options, ",")) != NULL) {
> -               int token;
> +       return 0;
> +}
>
> -               if (!*p)
> -                       continue;
> +static void pstore_apply_param(struct fs_context *fc)
> +{
> +       struct pstore_fs_context *ctx = fc->fs_private;
>
> -               token = match_token(p, tokens, args);
> -               switch (token) {
> -               case Opt_kmsg_bytes:
> -                       if (!match_int(&args[0], &option))
> -                               pstore_set_kmsg_bytes(option);
> -                       break;
> -               }
> -       }
> +       pstore_set_kmsg_bytes(ctx->kmsg_bytes);
>  }
>
>  /*
> @@ -271,11 +281,10 @@ static int pstore_show_options(struct seq_file *m, struct dentry *root)
>         return 0;
>  }
>
> -static int pstore_remount(struct super_block *sb, int *flags, char *data)
> +static int pstore_reconfigure(struct fs_context *fc)
>  {
> -       sync_filesystem(sb);
> -       parse_options(data);
> -
> +       sync_filesystem(fc->root->d_sb);
> +       pstore_apply_param(fc);
>         return 0;
>  }
>
> @@ -283,7 +292,6 @@ static const struct super_operations pstore_ops = {
>         .statfs         = simple_statfs,
>         .drop_inode     = generic_delete_inode,
>         .evict_inode    = pstore_evict_inode,
> -       .remount_fs     = pstore_remount,
>         .show_options   = pstore_show_options,
>  };
>
> @@ -389,12 +397,10 @@ void pstore_get_records(int quiet)
>         inode_unlock(d_inode(root));
>  }
>
> -static int pstore_fill_super(struct super_block *sb, void *data, int silent)
> +static int pstore_fill_super(struct super_block *sb, struct fs_context *fc)
>  {
>         struct inode *inode;
>
> -       pstore_sb = sb;
> -
>         sb->s_maxbytes          = MAX_LFS_FILESIZE;
>         sb->s_blocksize         = PAGE_SIZE;
>         sb->s_blocksize_bits    = PAGE_SHIFT;
> @@ -402,7 +408,8 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
>         sb->s_op                = &pstore_ops;
>         sb->s_time_gran         = 1;
>
> -       parse_options(data);
> +       pstore_sb = sb;
> +       pstore_apply_param(fc);
>
>         inode = pstore_get_inode(sb);
>         if (inode) {
> @@ -420,10 +427,35 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
>         return 0;
>  }
>
> -static struct dentry *pstore_mount(struct file_system_type *fs_type,
> -       int flags, const char *dev_name, void *data)
> +static int pstore_get_tree(struct fs_context *fc)
> +{
> +       return vfs_get_super(fc, vfs_get_single_reconf_super,
> +                            pstore_fill_super);
> +}
> +
> +static void pstore_free_fc(struct fs_context *fc)
>  {
> -       return mount_single(fs_type, flags, data, pstore_fill_super);
> +       kfree(fc->fs_private);
> +}
> +
> +static const struct fs_context_operations pstore_context_ops = {
> +       .free           = pstore_free_fc,
> +       .parse_param    = pstore_parse_param,
> +       .get_tree       = pstore_get_tree,
> +       .reconfigure    = pstore_reconfigure,
> +};
> +
> +static int pstore_init_fs_context(struct fs_context *fc)
> +{
> +       struct pstore_fs_context *ctx;
> +
> +       ctx = kzalloc(sizeof(struct pstore_fs_context), GFP_KERNEL);
> +       if (!ctx)
> +               return -ENOMEM;
> +
> +       fc->fs_private = ctx;
> +       fc->ops = &pstore_context_ops;
> +       return 0;
>  }
>
>  static void pstore_kill_sb(struct super_block *sb)
> @@ -435,7 +467,8 @@ static void pstore_kill_sb(struct super_block *sb)
>  static struct file_system_type pstore_fs_type = {
>         .owner          = THIS_MODULE,
>         .name           = "pstore",
> -       .mount          = pstore_mount,
> +       .init_fs_context = pstore_init_fs_context,
> +       .parameters     = &pstore_fs_parameters,
>         .kill_sb        = pstore_kill_sb,
>  };
>
>


-- 
Kees Cook

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x
  2019-03-27 23:40 ` [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x David Howells
@ 2019-03-28  0:53   ` Masahiro Yamada
  2019-03-30 12:11     ` Masahiro Yamada
  2019-04-10  9:54   ` David Howells
  1 sibling, 1 reply; 87+ messages in thread
From: Masahiro Yamada @ 2019-03-28  0:53 UTC (permalink / raw)
  To: David Howells
  Cc: Al Viro, Andreas Schwab, linux-fsdevel, Linux Kernel Mailing List

David,


On Thu, Mar 28, 2019 at 8:41 AM David Howells <dhowells@redhat.com> wrote:
>
> From: Masahiro Yamada <yamada.masahiro@socionext.com>
>
> Commit 2b50f7ab6368 ("kbuild: add workaround for Debian make-kpkg")
> annoyed people who want to wrap the top Makefile with GNUmakefile
> or something in order to customize it for their use.
>
> On second thought, we do not need to run the sub-make for in-tree
> build with Make 4.x because the 'MAKEFLAGS += -rR' issue only happens
> on GNU Make 3.x.
>
> With this commit, people will get back the workflow, and the Debian
> make-kpkg will still work.
>
> Fixes: 2b50f7ab6368 ("kbuild: add workaround for Debian make-kpkg")
> Reported-by: Andreas Schwab <schwab@suse.de>
> Reported-by: David Howells <dhowells@redhat.com>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> Signed-off-by: David Howells <dhowells@redhat.com>


Why should this patch be included in a
totally unrelated patch series?


I already applied this to my fixes branch,
and am planning to send a PR to Linus soon.

(Sorry, I missed to send a PR for -rc2)




> ---
>
>  Makefile |   31 +++++++++++++++----------------
>  1 file changed, 15 insertions(+), 16 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index c0a34064c574..cd38d203e71a 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -31,26 +31,12 @@ _all:
>  # descending is started. They are now explicitly listed as the
>  # prepare rule.
>
> -# Ugly workaround for Debian make-kpkg:
> -# make-kpkg directly includes the top Makefile of Linux kernel. In such a case,
> -# skip sub-make to support debian_* targets in ruleset/kernel_version.mk, but
> -# displays warning to discourage such abusage.
> -ifneq ($(word 2, $(MAKEFILE_LIST)),)
> -$(warning Do not include top Makefile of Linux Kernel)
> -sub-make-done := 1
> -MAKEFLAGS += -rR
> -endif
> -
>  ifneq ($(sub-make-done),1)
>
>  # Do not use make's built-in rules and variables
>  # (this increases performance and avoids hard-to-debug behaviour)
>  MAKEFLAGS += -rR
>
> -# 'MAKEFLAGS += -rR' does not become immediately effective for old
> -# GNU Make versions. Cancel implicit rules for this Makefile.
> -$(lastword $(MAKEFILE_LIST)): ;
> -
>  # Avoid funny character set dependencies
>  unexport LC_ALL
>  LC_COLLATE=C
> @@ -153,6 +139,7 @@ $(if $(KBUILD_OUTPUT),, \
>  # 'sub-make' below.
>  MAKEFLAGS += --include-dir=$(CURDIR)
>
> +need-sub-make := 1
>  else
>
>  # Do not print "Entering directory ..." at all for in-tree build.
> @@ -160,6 +147,15 @@ MAKEFLAGS += --no-print-directory
>
>  endif # ifneq ($(KBUILD_OUTPUT),)
>
> +ifneq ($(filter 3.%,$(MAKE_VERSION)),)
> +# 'MAKEFLAGS += -rR' does not immediately become effective for GNU Make 3.x
> +# We need to run sub-make to avoid implicit rules in the top Makefile.
> +need-sub-make := 1
> +# Cancel implicit rules for this Makefile.
> +$(lastword $(MAKEFILE_LIST)): ;
> +endif
> +
> +ifeq ($(need-sub-make),1)
>  PHONY += $(MAKECMDGOALS) sub-make
>
>  $(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
> @@ -171,8 +167,11 @@ sub-make:
>         $(if $(KBUILD_OUTPUT),-C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR)) \
>         -f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))
>
> -else # sub-make-done
> +endif # need-sub-make
> +endif # sub-make-done
> +
>  # We process the rest of the Makefile if this is the final invocation of make
> +ifeq ($(need-sub-make),)
>
>  # Do not print "Entering directory ...",
>  # but we want to display it when entering to the output directory
> @@ -1757,7 +1756,7 @@ existing-targets := $(wildcard $(sort $(targets)))
>
>  endif   # ifeq ($(config-targets),1)
>  endif   # ifeq ($(mixed-targets),1)
> -endif   # sub-make-done
> +endif   # need-sub-make
>
>  PHONY += FORCE
>  FORCE:
>


-- 
Best Regards
Masahiro Yamada

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 14/68] vfs: Convert drm to use the new mount API
  2019-03-27 23:42 ` [RFC PATCH 14/68] vfs: Convert drm " David Howells
@ 2019-03-28  7:47   ` Daniel Vetter
  2019-04-10  9:59   ` David Howells
  1 sibling, 0 replies; 87+ messages in thread
From: Daniel Vetter @ 2019-03-28  7:47 UTC (permalink / raw)
  To: David Howells
  Cc: viro, David Airlie, Daniel Vetter, dri-devel, linux-fsdevel,
	linux-kernel

On Wed, Mar 27, 2019 at 11:42:18PM +0000, David Howells wrote:
> Convert the drm filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
> 
> See Documentation/filesystems/mount_api.txt for more information.
> 
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: David Airlie <airlied@linux.ie>
> cc: Daniel Vetter <daniel@ffwll.ch>
> cc: dri-devel@lists.freedesktop.org

No clue about vfs, but feel free to merge through whatever tree this is
suitable for.

Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
> 
>  drivers/gpu/drm/drm_drv.c |   14 ++++++--------
>  1 file changed, 6 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 381581b01d48..9eead5a478de 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -31,6 +31,7 @@
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
>  #include <linux/mount.h>
> +#include <linux/fs_context.h>
>  #include <linux/slab.h>
>  #include <linux/srcu.h>
>  
> @@ -413,20 +414,17 @@ static const struct super_operations drm_fs_sops = {
>  	.statfs		= simple_statfs,
>  };
>  
> -static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags,
> -				   const char *dev_name, void *data)
> +static int drm_fs_init_fs_context(struct fs_context *fc)
>  {
> -	return mount_pseudo(fs_type,
> -			    "drm:",
> -			    &drm_fs_sops,
> -			    &drm_fs_dops,
> -			    0x010203ff);
> +	return vfs_init_pseudo_fs_context(fc, "drm:",
> +					  &drm_fs_sops, NULL,
> +					  &drm_fs_dops, 0x010203ff);
>  }
>  
>  static struct file_system_type drm_fs_type = {
>  	.name		= "drm",
>  	.owner		= THIS_MODULE,
> -	.mount		= drm_fs_mount,
> +	.init_fs_context = drm_fs_init_fs_context,
>  	.kill_sb	= kill_anon_super,
>  };
>  
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 53/68] vfs: Convert functionfs to use the new mount API
  2019-03-27 23:47 ` [RFC PATCH 53/68] vfs: Convert functionfs " David Howells
@ 2019-03-28 10:43   ` Michał Nazarewicz
  0 siblings, 0 replies; 87+ messages in thread
From: Michał Nazarewicz @ 2019-03-28 10:43 UTC (permalink / raw)
  To: David Howells; +Cc: viro, Felipe Balbi, USB list, linux-fsdevel, linux-kernel

I'm not familiar with the new mount API but the code looks reasonable to me:

Acked-by: Michal Nazarewicz <mina86@mina86.com>

On Wed, 27 Mar 2019 at 23:47, David Howells <dhowells@redhat.com> wrote:
> Convert the functionfs filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
>
> See Documentation/filesystems/mount_api.txt for more information.

-- 
Best regards
ミハウ “𝓶𝓲𝓷𝓪86” ナザレヴイツ
«If at first you don’t succeed, give up skydiving»

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 22/68] vfs: Convert binderfs to use the new mount API
  2019-03-27 23:43 ` [RFC PATCH 22/68] vfs: Convert binderfs to use the new mount API David Howells
@ 2019-03-28 14:45   ` Dan Carpenter
  2019-04-10 10:01   ` David Howells
  1 sibling, 0 replies; 87+ messages in thread
From: Dan Carpenter @ 2019-03-28 14:45 UTC (permalink / raw)
  To: David Howells
  Cc: viro, devel, Todd Kjos, Greg Kroah-Hartman, linux-kernel,
	linux-fsdevel, Arve Hjønnevåg, Joel Fernandes,
	Martijn Coenen, Christian Brauner

On Wed, Mar 27, 2019 at 11:43:20PM +0000, David Howells wrote:
> +static void binderfs_free_fc(struct fs_context *fc)
> +{
> +	struct binderfs_info *info = fc->s_fs_info;
> +
> +	if (info) {
> +		struct ipc_namespace *ipc_ns = fc->s_fs_info;
> +		put_ipc_ns(ipc_ns);

I feel like put_ipc_ns(info->ipc_ns) would be more readable.

> +		kfree(info);
> +	}
> +}
> +


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x
  2019-03-28  0:53   ` Masahiro Yamada
@ 2019-03-30 12:11     ` Masahiro Yamada
  0 siblings, 0 replies; 87+ messages in thread
From: Masahiro Yamada @ 2019-03-30 12:11 UTC (permalink / raw)
  To: David Howells
  Cc: Al Viro, Andreas Schwab, linux-fsdevel, Linux Kernel Mailing List

On Thu, Mar 28, 2019 at 9:53 AM Masahiro Yamada
<yamada.masahiro@socionext.com> wrote:
>
> David,
>
>
> On Thu, Mar 28, 2019 at 8:41 AM David Howells <dhowells@redhat.com> wrote:
> >
> > From: Masahiro Yamada <yamada.masahiro@socionext.com>
> >
> > Commit 2b50f7ab6368 ("kbuild: add workaround for Debian make-kpkg")
> > annoyed people who want to wrap the top Makefile with GNUmakefile
> > or something in order to customize it for their use.
> >
> > On second thought, we do not need to run the sub-make for in-tree
> > build with Make 4.x because the 'MAKEFLAGS += -rR' issue only happens
> > on GNU Make 3.x.
> >
> > With this commit, people will get back the workflow, and the Debian
> > make-kpkg will still work.
> >
> > Fixes: 2b50f7ab6368 ("kbuild: add workaround for Debian make-kpkg")
> > Reported-by: Andreas Schwab <schwab@suse.de>
> > Reported-by: David Howells <dhowells@redhat.com>
> > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > Signed-off-by: David Howells <dhowells@redhat.com>
>
>
> Why should this patch be included in a
> totally unrelated patch series?
>
>
> I already applied this to my fixes branch,
> and am planning to send a PR to Linus soon.
>
> (Sorry, I missed to send a PR for -rc2)


Now, this landed in Linus tree.



-- 
Best Regards
Masahiro Yamada

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 48/68] vfs: Convert cramfs to use the new mount API
  2019-03-27 23:46 ` [RFC PATCH 48/68] vfs: Convert cramfs " David Howells
@ 2019-04-01 15:25   ` Nicolas Pitre
  0 siblings, 0 replies; 87+ messages in thread
From: Nicolas Pitre @ 2019-04-01 15:25 UTC (permalink / raw)
  To: David Howells; +Cc: viro, linux-mtd, linux-block, linux-fsdevel, linux-kernel

On Wed, 27 Mar 2019, David Howells wrote:

> Convert the cramfs filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
> 
> See Documentation/filesystems/mount_api.txt for more information.
> 
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: Nicolas Pitre <nico@linaro.org>
> cc: linux-mtd@lists.infradead.org
> cc: linux-block@vger.kernel.org

Tested-by: Nicolas Pitre <nico@fluxnic.net>
Acked-by: Nicolas Pitre <nico@fluxnic.net>




> ---
> 
>  fs/cramfs/inode.c |   69 ++++++++++++++++++++++++++++++-----------------------
>  1 file changed, 39 insertions(+), 30 deletions(-)
> 
> diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
> index 9352487bd0fc..2ee89a353d64 100644
> --- a/fs/cramfs/inode.c
> +++ b/fs/cramfs/inode.c
> @@ -24,6 +24,7 @@
>  #include <linux/blkdev.h>
>  #include <linux/mtd/mtd.h>
>  #include <linux/mtd/super.h>
> +#include <linux/fs_context.h>
>  #include <linux/slab.h>
>  #include <linux/vfs.h>
>  #include <linux/mutex.h>
> @@ -506,18 +507,19 @@ static void cramfs_kill_sb(struct super_block *sb)
>  	kfree(sbi);
>  }
>  
> -static int cramfs_remount(struct super_block *sb, int *flags, char *data)
> +static int cramfs_reconfigure(struct fs_context *fc)
>  {
> -	sync_filesystem(sb);
> -	*flags |= SB_RDONLY;
> +	sync_filesystem(fc->root->d_sb);
> +	fc->sb_flags |= SB_RDONLY;
>  	return 0;
>  }
>  
> -static int cramfs_read_super(struct super_block *sb,
> -			     struct cramfs_super *super, int silent)
> +static int cramfs_read_super(struct super_block *sb, struct fs_context *fc,
> +			     struct cramfs_super *super)
>  {
>  	struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
>  	unsigned long root_offset;
> +	bool silent = fc->sb_flags & SB_SILENT;
>  
>  	/* We don't know the real size yet */
>  	sbi->size = PAGE_SIZE;
> @@ -532,7 +534,7 @@ static int cramfs_read_super(struct super_block *sb,
>  		/* check for wrong endianness */
>  		if (super->magic == CRAMFS_MAGIC_WEND) {
>  			if (!silent)
> -				pr_err("wrong endianness\n");
> +				errorf(fc, "cramfs: wrong endianness");
>  			return -EINVAL;
>  		}
>  
> @@ -544,22 +546,22 @@ static int cramfs_read_super(struct super_block *sb,
>  		mutex_unlock(&read_mutex);
>  		if (super->magic != CRAMFS_MAGIC) {
>  			if (super->magic == CRAMFS_MAGIC_WEND && !silent)
> -				pr_err("wrong endianness\n");
> +				errorf(fc, "cramfs: wrong endianness");
>  			else if (!silent)
> -				pr_err("wrong magic\n");
> +				errorf(fc, "cramfs: wrong magic");
>  			return -EINVAL;
>  		}
>  	}
>  
>  	/* get feature flags first */
>  	if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) {
> -		pr_err("unsupported filesystem features\n");
> +		errorf(fc, "cramfs: unsupported filesystem features");
>  		return -EINVAL;
>  	}
>  
>  	/* Check that the root inode is in a sane state */
>  	if (!S_ISDIR(super->root.mode)) {
> -		pr_err("root is not a directory\n");
> +		errorf(fc, "cramfs: root is not a directory");
>  		return -EINVAL;
>  	}
>  	/* correct strange, hard-coded permissions of mkcramfs */
> @@ -578,12 +580,12 @@ static int cramfs_read_super(struct super_block *sb,
>  	sbi->magic = super->magic;
>  	sbi->flags = super->flags;
>  	if (root_offset == 0)
> -		pr_info("empty filesystem");
> +		infof(fc, "cramfs: empty filesystem");
>  	else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
>  		 ((root_offset != sizeof(struct cramfs_super)) &&
>  		  (root_offset != 512 + sizeof(struct cramfs_super))))
>  	{
> -		pr_err("bad root offset %lu\n", root_offset);
> +		errorf(fc, "cramfs: bad root offset %lu", root_offset);
>  		return -EINVAL;
>  	}
>  
> @@ -607,8 +609,7 @@ static int cramfs_finalize_super(struct super_block *sb,
>  	return 0;
>  }
>  
> -static int cramfs_blkdev_fill_super(struct super_block *sb, void *data,
> -				    int silent)
> +static int cramfs_blkdev_fill_super(struct super_block *sb, struct fs_context *fc)
>  {
>  	struct cramfs_sb_info *sbi;
>  	struct cramfs_super super;
> @@ -623,14 +624,13 @@ static int cramfs_blkdev_fill_super(struct super_block *sb, void *data,
>  	for (i = 0; i < READ_BUFFERS; i++)
>  		buffer_blocknr[i] = -1;
>  
> -	err = cramfs_read_super(sb, &super, silent);
> +	err = cramfs_read_super(sb, fc, &super);
>  	if (err)
>  		return err;
>  	return cramfs_finalize_super(sb, &super.root);
>  }
>  
> -static int cramfs_mtd_fill_super(struct super_block *sb, void *data,
> -				 int silent)
> +static int cramfs_mtd_fill_super(struct super_block *sb, struct fs_context *fc)
>  {
>  	struct cramfs_sb_info *sbi;
>  	struct cramfs_super super;
> @@ -652,7 +652,7 @@ static int cramfs_mtd_fill_super(struct super_block *sb, void *data,
>  
>  	pr_info("checking physical address %pap for linear cramfs image\n",
>  		&sbi->linear_phys_addr);
> -	err = cramfs_read_super(sb, &super, silent);
> +	err = cramfs_read_super(sb, fc, &super);
>  	if (err)
>  		return err;
>  
> @@ -947,32 +947,41 @@ static const struct inode_operations cramfs_dir_inode_operations = {
>  };
>  
>  static const struct super_operations cramfs_ops = {
> -	.remount_fs	= cramfs_remount,
>  	.statfs		= cramfs_statfs,
>  };
>  
> -static struct dentry *cramfs_mount(struct file_system_type *fs_type, int flags,
> -				   const char *dev_name, void *data)
> +static int cramfs_get_tree(struct fs_context *fc)
>  {
> -	struct dentry *ret = ERR_PTR(-ENOPROTOOPT);
> +	int ret = -ENOPROTOOPT;
>  
>  	if (IS_ENABLED(CONFIG_CRAMFS_MTD)) {
> -		ret = mount_mtd(fs_type, flags, dev_name, data,
> -				cramfs_mtd_fill_super);
> -		if (!IS_ERR(ret))
> +		ret = vfs_get_mtd_super(fc, cramfs_mtd_fill_super);
> +		if (ret < 0)
>  			return ret;
>  	}
> -	if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) {
> -		ret = mount_bdev(fs_type, flags, dev_name, data,
> -				 cramfs_blkdev_fill_super);
> -	}
> +	if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV))
> +		ret = vfs_get_block_super(fc, cramfs_blkdev_fill_super);
>  	return ret;
>  }
>  
> +static const struct fs_context_operations cramfs_context_ops = {
> +	.get_tree	= cramfs_get_tree,
> +	.reconfigure	= cramfs_reconfigure,
> +};
> +
> +/*
> + * Set up the filesystem mount context.
> + */
> +static int cramfs_init_fs_context(struct fs_context *fc)
> +{
> +	fc->ops = &cramfs_context_ops;
> +	return 0;
> +}
> +
>  static struct file_system_type cramfs_fs_type = {
>  	.owner		= THIS_MODULE,
>  	.name		= "cramfs",
> -	.mount		= cramfs_mount,
> +	.init_fs_context = cramfs_init_fs_context,
>  	.kill_sb	= cramfs_kill_sb,
>  	.fs_flags	= FS_REQUIRES_DEV,
>  };
> 
> 

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 35/68] vfs: Convert xenfs to use the new mount API
  2019-03-27 23:44 ` [RFC PATCH 35/68] vfs: Convert xenfs " David Howells
@ 2019-04-02  7:38   ` Juergen Gross
  0 siblings, 0 replies; 87+ messages in thread
From: Juergen Gross @ 2019-04-02  7:38 UTC (permalink / raw)
  To: David Howells, viro
  Cc: Boris Ostrovsky, Stefano Stabellini, xen-devel, linux-fsdevel,
	linux-kernel

On 28/03/2019 00:44, David Howells wrote:
> Convert the xenfs filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
> 
> See Documentation/filesystems/mount_api.txt for more information.
> 
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
> cc: Juergen Gross <jgross@suse.com>
> cc: Stefano Stabellini <sstabellini@kernel.org>
> cc: xen-devel@lists.xenproject.org

Reviewed-by: Juergen Gross <jgross@suse.com>


Juergen

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 64/68] vfs: Convert devpts to use the new mount API
  2019-03-27 23:48 ` [RFC PATCH 64/68] vfs: Convert devpts " David Howells
@ 2019-04-05 12:52   ` Christian Brauner
  0 siblings, 0 replies; 87+ messages in thread
From: Christian Brauner @ 2019-04-05 12:52 UTC (permalink / raw)
  To: David Howells; +Cc: viro, Eric W. Biederman, linux-fsdevel, linux-kernel

On Wed, Mar 27, 2019 at 11:48:41PM +0000, David Howells wrote:
> Convert the devpts filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
> 
> See Documentation/filesystems/mount_api.txt for more information.
> 
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: "Eric W. Biederman" <ebiederm@xmission.com>
> cc: Christian Brauner <christian.brauner@ubuntu.com>

One nit below otherwise:

Acked-by: Christian Brauner <christian@brauner.io>

> ---
> 
>  fs/devpts/inode.c |  265 +++++++++++++++++++++++++----------------------------
>  1 file changed, 124 insertions(+), 141 deletions(-)
> 
> diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
> index 553a3f3300ae..ed4a49012570 100644
> --- a/fs/devpts/inode.c
> +++ b/fs/devpts/inode.c
> @@ -15,6 +15,8 @@
>  #include <linux/module.h>
>  #include <linux/init.h>
>  #include <linux/fs.h>
> +#include <linux/fs_context.h>
> +#include <linux/fs_parser.h>
>  #include <linux/sched.h>
>  #include <linux/namei.h>
>  #include <linux/slab.h>
> @@ -24,7 +26,6 @@
>  #include <linux/magic.h>
>  #include <linux/idr.h>
>  #include <linux/devpts_fs.h>
> -#include <linux/parser.h>
>  #include <linux/fsnotify.h>
>  #include <linux/seq_file.h>
>  
> @@ -109,14 +110,19 @@ enum {
>  	Opt_err
>  };
>  
> -static const match_table_t tokens = {
> -	{Opt_uid, "uid=%u"},
> -	{Opt_gid, "gid=%u"},
> -	{Opt_mode, "mode=%o"},
> -	{Opt_ptmxmode, "ptmxmode=%o"},
> -	{Opt_newinstance, "newinstance"},
> -	{Opt_max, "max=%d"},
> -	{Opt_err, NULL}
> +static const struct fs_parameter_spec devpts_param_specs[] = {
> +	fsparam_u32	("gid",		Opt_gid),
> +	fsparam_s32	("max",		Opt_max),
> +	fsparam_u32oct	("mode",	Opt_mode),
> +	fsparam_flag	("newinstance",	Opt_newinstance),
> +	fsparam_u32oct	("ptmxmode",	Opt_ptmxmode),
> +	fsparam_u32	("uid",		Opt_uid),
> +	{}
> +};
> +
> +static const struct fs_parameter_description devpts_fs_parameters = {
> +	.name		= "devpts",
> +	.specs		= devpts_param_specs,
>  };
>  
>  struct pts_fs_info {
> @@ -236,93 +242,56 @@ void devpts_release(struct pts_fs_info *fsi)
>  	deactivate_super(fsi->sb);
>  }
>  
> -#define PARSE_MOUNT	0
> -#define PARSE_REMOUNT	1
> -
>  /*
> - * parse_mount_options():
> - *	Set @opts to mount options specified in @data. If an option is not
> - *	specified in @data, set it to its default value.
> - *
> - * Note: @data may be NULL (in which case all options are set to default).
> + * devpts_parse_param - Parse mount parameters
>   */
> -static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
> +static int devpts_parse_param(struct fs_context *fc, struct fs_parameter *param)
>  {
> -	char *p;
> +	struct pts_fs_info *fsi = fc->s_fs_info;
> +	struct pts_mount_opts *opts = &fsi->mount_opts;
> +	struct fs_parse_result result;
>  	kuid_t uid;
>  	kgid_t gid;
> -
> -	opts->setuid  = 0;
> -	opts->setgid  = 0;
> -	opts->uid     = GLOBAL_ROOT_UID;
> -	opts->gid     = GLOBAL_ROOT_GID;
> -	opts->mode    = DEVPTS_DEFAULT_MODE;
> -	opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
> -	opts->max     = NR_UNIX98_PTY_MAX;
> -
> -	/* Only allow instances mounted from the initial mount
> -	 * namespace to tap the reserve pool of ptys.
> -	 */
> -	if (op == PARSE_MOUNT)
> -		opts->reserve =
> -			(current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns);
> -
> -	while ((p = strsep(&data, ",")) != NULL) {
> -		substring_t args[MAX_OPT_ARGS];
> -		int token;
> -		int option;
> -
> -		if (!*p)
> -			continue;
> -
> -		token = match_token(p, tokens, args);
> -		switch (token) {
> -		case Opt_uid:
> -			if (match_int(&args[0], &option))
> -				return -EINVAL;
> -			uid = make_kuid(current_user_ns(), option);
> -			if (!uid_valid(uid))
> -				return -EINVAL;
> -			opts->uid = uid;
> -			opts->setuid = 1;
> -			break;
> -		case Opt_gid:
> -			if (match_int(&args[0], &option))
> -				return -EINVAL;
> -			gid = make_kgid(current_user_ns(), option);
> -			if (!gid_valid(gid))
> -				return -EINVAL;
> -			opts->gid = gid;
> -			opts->setgid = 1;
> -			break;
> -		case Opt_mode:
> -			if (match_octal(&args[0], &option))
> -				return -EINVAL;
> -			opts->mode = option & S_IALLUGO;
> -			break;
> -		case Opt_ptmxmode:
> -			if (match_octal(&args[0], &option))
> -				return -EINVAL;
> -			opts->ptmxmode = option & S_IALLUGO;
> -			break;
> -		case Opt_newinstance:
> -			break;
> -		case Opt_max:
> -			if (match_int(&args[0], &option) ||
> -			    option < 0 || option > NR_UNIX98_PTY_MAX)
> -				return -EINVAL;
> -			opts->max = option;
> -			break;
> -		default:
> -			pr_err("called with bogus options\n");
> -			return -EINVAL;
> -		}
> +	int opt;
> +
> +	opt = fs_parse(fc, &devpts_fs_parameters, param, &result);
> +	if (opt < 0)
> +		return opt;
> +
> +	switch (opt) {
> +	case Opt_uid:
> +		uid = make_kuid(current_user_ns(), result.uint_32);
> +		if (!uid_valid(uid))
> +			return invalf(fc, "Unknown uid");
> +		opts->uid = uid;
> +		opts->setuid = 1;
> +		break;
> +	case Opt_gid:
> +		gid = make_kgid(current_user_ns(), result.uint_32);
> +		if (!gid_valid(gid))
> +			return invalf(fc, "Unknown gid");
> +		opts->gid = gid;
> +		opts->setgid = 1;
> +		break;
> +	case Opt_mode:
> +		opts->mode = result.uint_32 & S_IALLUGO;
> +		break;
> +	case Opt_ptmxmode:
> +		opts->ptmxmode = result.uint_32 & S_IALLUGO;
> +		break;
> +	case Opt_newinstance:
> +		break;
> +	case Opt_max:
> +		if (result.uint_32 > NR_UNIX98_PTY_MAX)
> +			return invalf(fc, "max out of range");
> +		opts->max = result.uint_32;
> +		break;
>  	}
>  
>  	return 0;
>  }
>  
> -static int mknod_ptmx(struct super_block *sb)
> +static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
>  {
>  	int mode;
>  	int rc = -ENOMEM;
> @@ -344,7 +313,7 @@ static int mknod_ptmx(struct super_block *sb)
>  
>  	dentry = d_alloc_name(root, "ptmx");
>  	if (!dentry) {
> -		pr_err("Unable to alloc dentry for ptmx node\n");
> +		errorf(fc, "Unable to alloc dentry for ptmx node\n");
>  		goto out;
>  	}
>  
> @@ -353,7 +322,7 @@ static int mknod_ptmx(struct super_block *sb)
>  	 */
>  	inode = new_inode(sb);
>  	if (!inode) {
> -		pr_err("Unable to alloc inode for ptmx node\n");
> +		errorf(fc, "Unable to alloc inode for ptmx node\n");
>  		dput(dentry);
>  		goto out;
>  	}
> @@ -384,13 +353,23 @@ static void update_ptmx_mode(struct pts_fs_info *fsi)
>  	}
>  }
>  
> -static int devpts_remount(struct super_block *sb, int *flags, char *data)
> +static int devpts_reconfigure(struct fs_context *fc)
>  {
> -	int err;
> -	struct pts_fs_info *fsi = DEVPTS_SB(sb);
> -	struct pts_mount_opts *opts = &fsi->mount_opts;
> +	struct pts_fs_info *fsi = DEVPTS_SB(fc->root->d_sb);
> +	struct pts_fs_info *new = fc->s_fs_info;
>  
> -	err = parse_mount_options(data, PARSE_REMOUNT, opts);
> +	/* Apply the revised options.  We don't want to change ->reserve.
> +	 * Ideally, we'd update each option conditionally on it having being

s/being/been/ ?

> +	 * explicitly changed, but the default is to reset everything so that
> +	 * would break UAPI...
> +	 */
> +	fsi->mount_opts.setuid		= new->mount_opts.setuid;
> +	fsi->mount_opts.setgid		= new->mount_opts.setgid;
> +	fsi->mount_opts.uid		= new->mount_opts.uid;
> +	fsi->mount_opts.gid		= new->mount_opts.gid;
> +	fsi->mount_opts.mode		= new->mount_opts.mode;
> +	fsi->mount_opts.ptmxmode	= new->mount_opts.ptmxmode;
> +	fsi->mount_opts.max		= new->mount_opts.max;
>  
>  	/*
>  	 * parse_mount_options() restores options to default values
> @@ -400,7 +379,7 @@ static int devpts_remount(struct super_block *sb, int *flags, char *data)
>  	 */
>  	update_ptmx_mode(fsi);
>  
> -	return err;
> +	return 0;
>  }
>  
>  static int devpts_show_options(struct seq_file *seq, struct dentry *root)
> @@ -424,31 +403,13 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root)
>  
>  static const struct super_operations devpts_sops = {
>  	.statfs		= simple_statfs,
> -	.remount_fs	= devpts_remount,
>  	.show_options	= devpts_show_options,
>  };
>  
> -static void *new_pts_fs_info(struct super_block *sb)
> -{
> -	struct pts_fs_info *fsi;
> -
> -	fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
> -	if (!fsi)
> -		return NULL;
> -
> -	ida_init(&fsi->allocated_ptys);
> -	fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
> -	fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
> -	fsi->sb = sb;
> -
> -	return fsi;
> -}
> -
> -static int
> -devpts_fill_super(struct super_block *s, void *data, int silent)
> +static int devpts_fill_super(struct super_block *s, struct fs_context *fc)
>  {
> +	struct pts_fs_info *fsi = DEVPTS_SB(s);
>  	struct inode *inode;
> -	int error;
>  
>  	s->s_iflags &= ~SB_I_NODEV;
>  	s->s_blocksize = 1024;
> @@ -457,20 +418,12 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
>  	s->s_op = &devpts_sops;
>  	s->s_d_op = &simple_dentry_operations;
>  	s->s_time_gran = 1;
> +	fsi->sb = s;
>  
> -	error = -ENOMEM;
> -	s->s_fs_info = new_pts_fs_info(s);
> -	if (!s->s_fs_info)
> -		goto fail;
> -
> -	error = parse_mount_options(data, PARSE_MOUNT, &DEVPTS_SB(s)->mount_opts);
> -	if (error)
> -		goto fail;
> -
> -	error = -ENOMEM;
>  	inode = new_inode(s);
>  	if (!inode)
> -		goto fail;
> +		return -ENOMEM;
> +
>  	inode->i_ino = 1;
>  	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
>  	inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
> @@ -480,20 +433,11 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
>  
>  	s->s_root = d_make_root(inode);
>  	if (!s->s_root) {
> -		pr_err("get root dentry failed\n");
> -		goto fail;
> +		errorf(fc, "get root dentry failed");
> +		return -ENOMEM;
>  	}
>  
> -	error = mknod_ptmx(s);
> -	if (error)
> -		goto fail_dput;
> -
> -	return 0;
> -fail_dput:
> -	dput(s->s_root);
> -	s->s_root = NULL;
> -fail:
> -	return error;
> +	return mknod_ptmx(s, fc);
>  }
>  
>  /*
> @@ -502,10 +446,48 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
>   *     Mount a new (private) instance of devpts.  PTYs created in this
>   *     instance are independent of the PTYs in other devpts instances.
>   */
> -static struct dentry *devpts_mount(struct file_system_type *fs_type,
> -	int flags, const char *dev_name, void *data)
> +static int devpts_get_tree(struct fs_context *fc)
> +{
> +	return vfs_get_super(fc, vfs_get_independent_super, devpts_fill_super);
> +}
> +
> +static void devpts_free_fc(struct fs_context *fc)
> +{
> +	kfree(fc->s_fs_info);
> +}
> +
> +static const struct fs_context_operations devpts_context_ops = {
> +	.free		= devpts_free_fc,
> +	.parse_param	= devpts_parse_param,
> +	.get_tree	= devpts_get_tree,
> +	.reconfigure	= devpts_reconfigure,
> +};
> +
> +/*
> + * Set up the filesystem mount context.
> + */
> +static int devpts_init_fs_context(struct fs_context *fc)
>  {
> -	return mount_nodev(fs_type, flags, data, devpts_fill_super);
> +	struct pts_fs_info *fsi;
> +
> +	fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
> +	if (!fsi)
> +		return -ENOMEM;
> +
> +	ida_init(&fsi->allocated_ptys);
> +	fsi->mount_opts.uid     = GLOBAL_ROOT_UID;
> +	fsi->mount_opts.gid     = GLOBAL_ROOT_GID;
> +	fsi->mount_opts.mode    = DEVPTS_DEFAULT_MODE;
> +	fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
> +	fsi->mount_opts.max     = NR_UNIX98_PTY_MAX;
> +
> +	if (fc->purpose == FS_CONTEXT_FOR_MOUNT &&
> +	    current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns)
> +		fsi->mount_opts.reserve = true;
> +
> +	fc->s_fs_info = fsi;
> +	fc->ops = &devpts_context_ops;
> +	return 0;
>  }
>  
>  static void devpts_kill_sb(struct super_block *sb)
> @@ -520,7 +502,8 @@ static void devpts_kill_sb(struct super_block *sb)
>  
>  static struct file_system_type devpts_fs_type = {
>  	.name		= "devpts",
> -	.mount		= devpts_mount,
> +	.init_fs_context = devpts_init_fs_context,
> +	.parameters	= &devpts_fs_parameters,
>  	.kill_sb	= devpts_kill_sb,
>  	.fs_flags	= FS_USERNS_MOUNT,
>  };
> 

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 31/68] vfs: Convert qib_fs/ipathfs to use the new mount API
  2019-03-27 23:44 ` [RFC PATCH 31/68] vfs: Convert qib_fs/ipathfs " David Howells
@ 2019-04-09 17:14   ` Dennis Dalessandro
  0 siblings, 0 replies; 87+ messages in thread
From: Dennis Dalessandro @ 2019-04-09 17:14 UTC (permalink / raw)
  To: David Howells, viro
  Cc: Mike Marciniszyn, linux-rdma, linux-fsdevel, linux-kernel

On 3/27/2019 7:44 PM, David Howells wrote:
> Convert the qib_fs/ipathfs filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
> 
> See Documentation/filesystems/mount_api.txt for more information.
> 
> [Q] Can qib_remove() race with qibfs_kill_super()?  Should qib_super
>      accesses be serialised with some sort of lock?

Sure looks to be possible to me. Although I don't think that's a reason 
not to apply your patch, you aren't making it any worse I don't think.

> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: Dennis Dalessandro <dennis.dalessandro@intel.com>
> cc: Mike Marciniszyn <mike.marciniszyn@intel.com>
> cc: linux-rdma@vger.kernel.org

Reviewed-by: Dennis Dalessandro <dennis.dalessandro@intel.com>

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x
  2019-03-27 23:40 ` [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x David Howells
  2019-03-28  0:53   ` Masahiro Yamada
@ 2019-04-10  9:54   ` David Howells
  1 sibling, 0 replies; 87+ messages in thread
From: David Howells @ 2019-04-10  9:54 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: dhowells, Al Viro, Andreas Schwab, linux-fsdevel,
	Linux Kernel Mailing List

Masahiro Yamada <yamada.masahiro@socionext.com> wrote:

> Why should this patch be included in a
> totally unrelated patch series?

Sorry, I forgot to exclude it.  It's in all my branches that I've touched
since upstream got broken.  I've now rebased and it's gone from this branch.

David

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 14/68] vfs: Convert drm to use the new mount API
  2019-03-27 23:42 ` [RFC PATCH 14/68] vfs: Convert drm " David Howells
  2019-03-28  7:47   ` Daniel Vetter
@ 2019-04-10  9:59   ` David Howells
  1 sibling, 0 replies; 87+ messages in thread
From: David Howells @ 2019-04-10  9:59 UTC (permalink / raw)
  To: viro, Daniel Vetter
  Cc: dhowells, David Airlie, dri-devel, linux-fsdevel, linux-kernel

Hi Al,

I wonder if it would be possible to extend anon_inodes to return just an
anonymous inode, thereby allowing the drm filesystem to be removed in favour
of just using an anon_inode.

David

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 22/68] vfs: Convert binderfs to use the new mount API
  2019-03-27 23:43 ` [RFC PATCH 22/68] vfs: Convert binderfs to use the new mount API David Howells
  2019-03-28 14:45   ` Dan Carpenter
@ 2019-04-10 10:01   ` David Howells
  1 sibling, 0 replies; 87+ messages in thread
From: David Howells @ 2019-04-10 10:01 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: dhowells, viro, devel, Todd Kjos, Greg Kroah-Hartman,
	linux-kernel, linux-fsdevel, Arve Hjønnevåg,
	Joel Fernandes, Martijn Coenen, Christian Brauner

Dan Carpenter <dan.carpenter@oracle.com> wrote:

> > +		struct ipc_namespace *ipc_ns = fc->s_fs_info;
> > +		put_ipc_ns(ipc_ns);
> 
> I feel like put_ipc_ns(info->ipc_ns) would be more readable.

Not so much more readable as more correct.

David

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 30/68] vfs: Convert fusectl to use the new mount API
  2019-03-27 23:44 ` [RFC PATCH 30/68] vfs: Convert fusectl " David Howells
@ 2019-04-24 13:49   ` Miklos Szeredi
  2019-04-24 15:16   ` David Howells
  1 sibling, 0 replies; 87+ messages in thread
From: Miklos Szeredi @ 2019-04-24 13:49 UTC (permalink / raw)
  To: David Howells; +Cc: Al Viro, linux-fsdevel, linux-kernel

On Thu, Mar 28, 2019 at 12:44 AM David Howells <dhowells@redhat.com> wrote:
>
> Convert the fusectl filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
>
> See Documentation/filesystems/mount_api.txt for more information.

Applied to fuse.git#for-next.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API
  2019-03-27 23:46 ` [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API David Howells
@ 2019-04-24 13:53   ` Miklos Szeredi
  2019-04-24 15:22   ` David Howells
  1 sibling, 0 replies; 87+ messages in thread
From: Miklos Szeredi @ 2019-04-24 13:53 UTC (permalink / raw)
  To: David Howells; +Cc: Al Viro, linux-fsdevel, linux-kernel

On Thu, Mar 28, 2019 at 12:46 AM David Howells <dhowells@redhat.com> wrote:
>
> Convert the fuse filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
>
> See Documentation/filesystems/mount_api.txt for more information.

Should I be applying this, together with the vfs dependencies?  Or
will you take care of these?

One minor comment is that fuse conventionally uses "struct fuse_conn
*fc", so "struct fs_context *fc" is confusing here.  There's one place
you use "struct fs_context *fsc", which seems the right thing to do
for all the cases.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 30/68] vfs: Convert fusectl to use the new mount API
  2019-03-27 23:44 ` [RFC PATCH 30/68] vfs: Convert fusectl " David Howells
  2019-04-24 13:49   ` Miklos Szeredi
@ 2019-04-24 15:16   ` David Howells
  1 sibling, 0 replies; 87+ messages in thread
From: David Howells @ 2019-04-24 15:16 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: dhowells, Al Viro, linux-fsdevel, linux-kernel

Miklos Szeredi <miklos@szeredi.hu> wrote:

> Applied to fuse.git#for-next.

Ah...  I was trying to get Al to take this entire series as there are cleanup
patches in it that depend on the filesystem patches.

David

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API
  2019-03-27 23:46 ` [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API David Howells
  2019-04-24 13:53   ` Miklos Szeredi
@ 2019-04-24 15:22   ` David Howells
  2019-04-24 17:28     ` Miklos Szeredi
  1 sibling, 1 reply; 87+ messages in thread
From: David Howells @ 2019-04-24 15:22 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: dhowells, Al Viro, linux-fsdevel, linux-kernel

Miklos Szeredi <miklos@szeredi.hu> wrote:

> Should I be applying this, together with the vfs dependencies?  Or
> will you take care of these?

I'm hoping Al will take the entire series.

> One minor comment is that fuse conventionally uses "struct fuse_conn
> *fc", so "struct fs_context *fc" is confusing here.  There's one place
> you use "struct fs_context *fsc", which seems the right thing to do
> for all the cases.

Except in ceph, where "struct ceph_fs_client *fsc" is a common thing...

David

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API
  2019-04-24 15:22   ` David Howells
@ 2019-04-24 17:28     ` Miklos Szeredi
  0 siblings, 0 replies; 87+ messages in thread
From: Miklos Szeredi @ 2019-04-24 17:28 UTC (permalink / raw)
  To: David Howells; +Cc: Al Viro, linux-fsdevel, linux-kernel

On Wed, Apr 24, 2019 at 5:22 PM David Howells <dhowells@redhat.com> wrote:
>
> Miklos Szeredi <miklos@szeredi.hu> wrote:
>
> > Should I be applying this, together with the vfs dependencies?  Or
> > will you take care of these?
>
> I'm hoping Al will take the entire series.
>
> > One minor comment is that fuse conventionally uses "struct fuse_conn
> > *fc", so "struct fs_context *fc" is confusing here.  There's one place
> > you use "struct fs_context *fsc", which seems the right thing to do
> > for all the cases.
>
> Except in ceph, where "struct ceph_fs_client *fsc" is a common thing...

I'd be happy if it was consistently "struct fs_context *fsc" in just
fuse/inode.c.

Thanks,
Miklos

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [RFC PATCH 52/68] vfs: Convert ceph to use the new mount API
  2019-03-27 23:47 ` [RFC PATCH 52/68] vfs: Convert ceph " David Howells
@ 2019-04-27 12:27   ` Jeff Layton
  0 siblings, 0 replies; 87+ messages in thread
From: Jeff Layton @ 2019-04-27 12:27 UTC (permalink / raw)
  To: David Howells, viro
  Cc: Yan, Zheng, Ilya Dryomov, Sage Weil, ceph-devel, linux-fsdevel,
	linux-kernel

On Wed, 2019-03-27 at 23:47 +0000, David Howells wrote:
> Convert the ceph filesystem to the new internal mount API as the old
> one will be obsoleted and removed.  This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
> 
> See Documentation/filesystems/mount_api.txt for more information.
> 
> Signed-off-by: David Howells <dhowells@redhat.com>
> Reviewed-by: "Yan, Zheng" <zyan@redhat.com>
> cc: Ilya Dryomov <idryomov@gmail.com>
> cc: Sage Weil <sage@redhat.com>
> cc: ceph-devel@vger.kernel.org
> ---
> 
>  drivers/block/rbd.c             |  363 +++++++++++---------
>  fs/ceph/cache.c                 |    9 -
>  fs/ceph/cache.h                 |    5 
>  fs/ceph/super.c                 |  697 +++++++++++++++++++--------------------
>  fs/ceph/super.h                 |    1 
>  fs/fs_context.c                 |    2 
>  fs/fs_parser.c                  |    2 
>  include/linux/ceph/ceph_debug.h |    1 
>  include/linux/ceph/libceph.h    |   17 +
>  net/ceph/ceph_common.c          |  410 ++++++++++-------------
>  10 files changed, 729 insertions(+), 778 deletions(-)
> 
> diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
> index 2210c1b9491b..1970af223d4b 100644
> --- a/drivers/block/rbd.c
> +++ b/drivers/block/rbd.c
> @@ -34,7 +34,7 @@
>  #include <linux/ceph/cls_lock_client.h>
>  #include <linux/ceph/striper.h>
>  #include <linux/ceph/decode.h>
> -#include <linux/parser.h>
> +#include <linux/fs_parser.h>
>  #include <linux/bsearch.h>
>  
>  #include <linux/kernel.h>
> @@ -747,40 +747,6 @@ static struct rbd_client *rbd_client_find(struct ceph_options *ceph_opts)
>  /*
>   * (Per device) rbd map options
>   */
> -enum {
> -	Opt_queue_depth,
> -	Opt_alloc_size,
> -	Opt_lock_timeout,
> -	Opt_last_int,
> -	/* int args above */
> -	Opt_pool_ns,
> -	Opt_last_string,
> -	/* string args above */
> -	Opt_read_only,
> -	Opt_read_write,
> -	Opt_lock_on_read,
> -	Opt_exclusive,
> -	Opt_notrim,
> -	Opt_err
> -};
> -
> -static match_table_t rbd_opts_tokens = {
> -	{Opt_queue_depth, "queue_depth=%d"},
> -	{Opt_alloc_size, "alloc_size=%d"},
> -	{Opt_lock_timeout, "lock_timeout=%d"},
> -	/* int args above */
> -	{Opt_pool_ns, "_pool_ns=%s"},
> -	/* string args above */
> -	{Opt_read_only, "read_only"},
> -	{Opt_read_only, "ro"},		/* Alternate spelling */
> -	{Opt_read_write, "read_write"},
> -	{Opt_read_write, "rw"},		/* Alternate spelling */
> -	{Opt_lock_on_read, "lock_on_read"},
> -	{Opt_exclusive, "exclusive"},
> -	{Opt_notrim, "notrim"},
> -	{Opt_err, NULL}
> -};
> -
>  struct rbd_options {
>  	int	queue_depth;
>  	int	alloc_size;
> @@ -799,85 +765,98 @@ struct rbd_options {
>  #define RBD_EXCLUSIVE_DEFAULT	false
>  #define RBD_TRIM_DEFAULT	true
>  
> -struct parse_rbd_opts_ctx {
> -	struct rbd_spec		*spec;
> -	struct rbd_options	*opts;
> +enum {
> +	Opt_alloc_size,
> +	Opt_exclusive,
> +	Opt_lock_on_read,
> +	Opt_lock_timeout,
> +	Opt_notrim,
> +	Opt_pool_ns,
> +	Opt_queue_depth,
> +	Opt_read_only,
> +	Opt_read_write,
> +};
> +
> +static const struct fs_parameter_spec rbd_param_specs[] = {
> +	fsparam_u32	("alloc_size",			Opt_alloc_size),
> +	fsparam_flag	("exclusive",			Opt_exclusive),
> +	fsparam_flag	("lock_on_read",		Opt_lock_on_read),
> +	fsparam_u32	("lock_timeout",		Opt_lock_timeout),
> +	fsparam_flag	("notrim",			Opt_notrim),
> +	fsparam_string	("_pool_ns",			Opt_pool_ns),
> +	fsparam_u32	("queue_depth",			Opt_queue_depth),
> +	fsparam_flag	("ro",				Opt_read_only),
> +	fsparam_flag	("rw",				Opt_read_write),
> +	{}
> +};
> +
> +static const struct fs_parameter_description rbd_parameters = {
> +	.name		= "rbd",
> +	.specs		= rbd_param_specs,
>  };
>  
> -static int parse_rbd_opts_token(char *c, void *private)
> +static int rbd_parse_param(struct ceph_config_context *ctx, struct fs_parameter *param)
>  {
> -	struct parse_rbd_opts_ctx *pctx = private;
> -	substring_t argstr[MAX_OPT_ARGS];
> -	int token, intval, ret;
> +	struct rbd_options *opts = ctx->rbd_opts;
> +	struct rbd_spec *spec = ctx->rbd_spec;
> +	struct fs_parse_result result;
> +	int ret, opt;
>  
> -	token = match_token(c, rbd_opts_tokens, argstr);
> -	if (token < Opt_last_int) {
> -		ret = match_int(&argstr[0], &intval);
> -		if (ret < 0) {
> -			pr_err("bad option arg (not int) at '%s'\n", c);
> -			return ret;
> -		}
> -		dout("got int token %d val %d\n", token, intval);
> -	} else if (token > Opt_last_int && token < Opt_last_string) {
> -		dout("got string token %d val %s\n", token, argstr[0].from);
> -	} else {
> -		dout("got token %d\n", token);
> -	}
> +	ret = ceph_parse_option(ctx->opt, NULL, param);
> +	if (ret != -ENOPARAM)
> +		return ret;
> +
> +	opt = fs_parse(NULL, &rbd_parameters, param, &result);
> +	if (opt < 0)
> +		return opt;
>  
> -	switch (token) {
> +	switch (opt) {
>  	case Opt_queue_depth:
> -		if (intval < 1) {
> -			pr_err("queue_depth out of range\n");
> -			return -EINVAL;
> -		}
> -		pctx->opts->queue_depth = intval;
> +		if (result.uint_32 < 1)
> +			goto out_of_range;
> +		opts->queue_depth = result.uint_32;
>  		break;
>  	case Opt_alloc_size:
> -		if (intval < SECTOR_SIZE) {
> -			pr_err("alloc_size out of range\n");
> -			return -EINVAL;
> -		}
> -		if (!is_power_of_2(intval)) {
> -			pr_err("alloc_size must be a power of 2\n");
> -			return -EINVAL;
> -		}
> -		pctx->opts->alloc_size = intval;
> +		if (result.uint_32 < SECTOR_SIZE)
> +			goto out_of_range;
> +		if (!is_power_of_2(result.uint_32))
> +			return invalf(NULL, "alloc_size must be a power of 2\n");
> +		opts->alloc_size = result.uint_32;
>  		break;
>  	case Opt_lock_timeout:
>  		/* 0 is "wait forever" (i.e. infinite timeout) */
> -		if (intval < 0 || intval > INT_MAX / 1000) {
> -			pr_err("lock_timeout out of range\n");
> -			return -EINVAL;
> -		}
> -		pctx->opts->lock_timeout = msecs_to_jiffies(intval * 1000);
> +		if (result.uint_32 > INT_MAX / 1000)
> +			goto out_of_range;
> +		opts->lock_timeout = msecs_to_jiffies(result.uint_32 * 1000);
>  		break;
>  	case Opt_pool_ns:
> -		kfree(pctx->spec->pool_ns);
> -		pctx->spec->pool_ns = match_strdup(argstr);
> -		if (!pctx->spec->pool_ns)
> -			return -ENOMEM;
> +		kfree(spec->pool_ns);
> +		spec->pool_ns = param->string;
> +		param->string = NULL;
>  		break;
>  	case Opt_read_only:
> -		pctx->opts->read_only = true;
> +		opts->read_only = true;
>  		break;
>  	case Opt_read_write:
> -		pctx->opts->read_only = false;
> +		opts->read_only = false;
>  		break;
>  	case Opt_lock_on_read:
> -		pctx->opts->lock_on_read = true;
> +		opts->lock_on_read = true;
>  		break;
>  	case Opt_exclusive:
> -		pctx->opts->exclusive = true;
> +		opts->exclusive = true;
>  		break;
>  	case Opt_notrim:
> -		pctx->opts->trim = false;
> +		opts->trim = false;
>  		break;
>  	default:
> -		/* libceph prints "bad option" msg */
>  		return -EINVAL;
>  	}
>  
>  	return 0;
> +
> +out_of_range:
> +	return invalf(NULL, "ceph: %s out of range", param->key);
>  }
>  
>  static char* obj_op_name(enum obj_operation_type op_type)
> @@ -5428,22 +5407,80 @@ static inline size_t next_token(const char **buf)
>   *
>   * Note: uses GFP_KERNEL for allocation.
>   */
> -static inline char *dup_token(const char **buf, size_t *lenp)
> +static inline char *dup_token(const char **buf)
>  {
>  	char *dup;
>  	size_t len;
>  
>  	len = next_token(buf);
> -	dup = kmemdup(*buf, len + 1, GFP_KERNEL);
> -	if (!dup)
> -		return NULL;
> -	*(dup + len) = '\0';
> -	*buf += len;
> +	dup = kmemdup_nul(*buf, len, GFP_KERNEL);
> +	if (dup)
> +		*buf += len;
> +	return dup;
> +}
>  
> -	if (lenp)
> -		*lenp = len;
> +/*
> + * Parse the parameter string.
> + */
> +static int rbd_parse_monolithic(struct ceph_config_context *ctx, const char *data)
> +{
> +	const char *sep, *key, *eq, *value;
> +	char key_buf[32];
> +	size_t size, klen;
> +	int ret = 0;
>  
> -	return dup;
> +	struct fs_parameter param = {
> +		.key	= key_buf,
> +		.type	= fs_value_is_string,
> +	};
> +
> +	do {
> +		key = data;
> +		sep = strchr(data, ',');
> +		if (sep) {
> +			data = sep + 1;
> +			size = sep - key;
> +		} else {
> +			data = NULL;
> +			size = strlen(key);
> +		}
> +
> +		if (!size)
> +			continue;
> +
> +		eq = memchr(key, '=', sep - key);
> +		if (eq) {
> +			klen = eq - key;
> +			if (klen == 0)
> +				return invalf(NULL, "Invalid option \"\"");
> +			value = eq + 1;
> +			param.size = sep - value;
> +		} else {
> +			klen = size;
> +			value = NULL;
> +			param.size = 0;
> +		}
> +
> +		if (klen >= sizeof(key_buf))
> +			return invalf(NULL, "Unknown option %*.*s",
> +				      (int)klen, (int)klen, key);
> +		memcpy(key_buf, key, klen);
> +		key_buf[klen] = 0;
> +
> +		if (param.size > 0) {
> +			param.string = kmemdup_nul(value, param.size,
> +						   GFP_KERNEL);
> +			if (!param.string)
> +				return -ENOMEM;
> +		}
> +
> +		ret = rbd_parse_param(ctx, &param);
> +		kfree(param.string);
> +		if (ret < 0)
> +			break;
> +	} while (data);
> +
> +	return ret;
>  }
>  
>  /*
> @@ -5487,18 +5524,11 @@ static inline char *dup_token(const char **buf, size_t *lenp)
>   *      created.  The image head is used if no snapshot id is
>   *      provided.  Snapshot mappings are always read-only.
>   */
> -static int rbd_add_parse_args(const char *buf,
> -				struct ceph_options **ceph_opts,
> -				struct rbd_options **opts,
> -				struct rbd_spec **rbd_spec)
> +static int rbd_add_parse_args(const char *buf, struct ceph_config_context *ctx)
>  {
> -	size_t len;
> -	char *options;
> -	const char *mon_addrs;
> +	const char *options, *mon_addrs;
> +	size_t len, options_len, mon_addrs_size;
>  	char *snap_name;
> -	size_t mon_addrs_size;
> -	struct parse_rbd_opts_ctx pctx = { 0 };
> -	struct ceph_options *copts;
>  	int ret;
>  
>  	/* The first four tokens are required */
> @@ -5509,36 +5539,35 @@ static int rbd_add_parse_args(const char *buf,
>  		return -EINVAL;
>  	}
>  	mon_addrs = buf;
> -	mon_addrs_size = len + 1;
> +	mon_addrs_size = len;
>  	buf += len;
>  
> -	ret = -EINVAL;
> -	options = dup_token(&buf, NULL);
> -	if (!options)
> -		return -ENOMEM;
> -	if (!*options) {
> +	options_len = next_token(&buf);
> +	if (options_len == 0) {
>  		rbd_warn(NULL, "no options provided");
> -		goto out_err;
> +		return -EINVAL;
>  	}
> +	options = buf;
> +	buf += len;
>  
> -	pctx.spec = rbd_spec_alloc();
> -	if (!pctx.spec)
> -		goto out_mem;
> +	ctx->rbd_spec = rbd_spec_alloc();
> +	if (!ctx->rbd_spec)
> +		return -ENOMEM;
>  
> -	pctx.spec->pool_name = dup_token(&buf, NULL);
> -	if (!pctx.spec->pool_name)
> -		goto out_mem;
> -	if (!*pctx.spec->pool_name) {
> +	ctx->rbd_spec->pool_name = dup_token(&buf);
> +	if (!ctx->rbd_spec->pool_name)
> +		return -ENOMEM;
> +	if (!*ctx->rbd_spec->pool_name) {
>  		rbd_warn(NULL, "no pool name provided");
> -		goto out_err;
> +		return -EINVAL;
>  	}
>  
> -	pctx.spec->image_name = dup_token(&buf, NULL);
> -	if (!pctx.spec->image_name)
> -		goto out_mem;
> -	if (!*pctx.spec->image_name) {
> +	ctx->rbd_spec->image_name = dup_token(&buf);
> +	if (!ctx->rbd_spec->image_name)
> +		return -ENOMEM;
> +	if (!*ctx->rbd_spec->image_name) {
>  		rbd_warn(NULL, "no image name provided");
> -		goto out_err;
> +		return -EINVAL;
>  	}
>  
>  	/*
> @@ -5550,51 +5579,37 @@ static int rbd_add_parse_args(const char *buf,
>  		buf = RBD_SNAP_HEAD_NAME; /* No snapshot supplied */
>  		len = sizeof (RBD_SNAP_HEAD_NAME) - 1;
>  	} else if (len > RBD_MAX_SNAP_NAME_LEN) {
> -		ret = -ENAMETOOLONG;
> -		goto out_err;
> +		return -ENAMETOOLONG;
>  	}
> -	snap_name = kmemdup(buf, len + 1, GFP_KERNEL);
> +
> +	snap_name = kmemdup_nul(buf, len, GFP_KERNEL);
>  	if (!snap_name)
> -		goto out_mem;
> -	*(snap_name + len) = '\0';
> -	pctx.spec->snap_name = snap_name;
> +		return -ENOMEM;
> +	ctx->rbd_spec->snap_name = snap_name;
>  
>  	/* Initialize all rbd options to the defaults */
>  
> -	pctx.opts = kzalloc(sizeof(*pctx.opts), GFP_KERNEL);
> -	if (!pctx.opts)
> -		goto out_mem;
> -
> -	pctx.opts->read_only = RBD_READ_ONLY_DEFAULT;
> -	pctx.opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
> -	pctx.opts->alloc_size = RBD_ALLOC_SIZE_DEFAULT;
> -	pctx.opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
> -	pctx.opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
> -	pctx.opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
> -	pctx.opts->trim = RBD_TRIM_DEFAULT;
> -
> -	copts = ceph_parse_options(options, mon_addrs,
> -				   mon_addrs + mon_addrs_size - 1,
> -				   parse_rbd_opts_token, &pctx);
> -	if (IS_ERR(copts)) {
> -		ret = PTR_ERR(copts);
> -		goto out_err;
> -	}
> -	kfree(options);
> +	ctx->rbd_opts = kzalloc(sizeof(*ctx->rbd_opts), GFP_KERNEL);
> +	if (!ctx->rbd_opts)
> +		return -ENOMEM;
>  
> -	*ceph_opts = copts;
> -	*opts = pctx.opts;
> -	*rbd_spec = pctx.spec;
> +	ctx->rbd_opts->read_only = RBD_READ_ONLY_DEFAULT;
> +	ctx->rbd_opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
> +	ctx->rbd_opts->alloc_size = RBD_ALLOC_SIZE_DEFAULT;
> +	ctx->rbd_opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
> +	ctx->rbd_opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
> +	ctx->rbd_opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
> +	ctx->rbd_opts->trim = RBD_TRIM_DEFAULT;
>  
> -	return 0;
> -out_mem:
> -	ret = -ENOMEM;
> -out_err:
> -	kfree(pctx.opts);
> -	rbd_spec_put(pctx.spec);
> -	kfree(options);
> +	ctx->opt = ceph_alloc_options();
> +	if (!ctx->opt)
> +		return -ENOMEM;
>  
> -	return ret;
> +	ret = ceph_parse_server_specs(ctx->opt, NULL, mon_addrs, mon_addrs_size);
> +	if (ret < 0)
> +		return ret;
> +
> +	return rbd_parse_monolithic(ctx, options);
>  }
>  
>  static void rbd_dev_image_unlock(struct rbd_device *rbd_dev)
> @@ -6001,10 +6016,8 @@ static ssize_t do_rbd_add(struct bus_type *bus,
>  			  const char *buf,
>  			  size_t count)
>  {
> +	struct ceph_config_context ctx = {};
>  	struct rbd_device *rbd_dev = NULL;
> -	struct ceph_options *ceph_opts = NULL;
> -	struct rbd_options *rbd_opts = NULL;
> -	struct rbd_spec *spec = NULL;
>  	struct rbd_client *rbdc;
>  	int rc;
>  
> @@ -6012,33 +6025,34 @@ static ssize_t do_rbd_add(struct bus_type *bus,
>  		return -ENODEV;
>  
>  	/* parse add command */
> -	rc = rbd_add_parse_args(buf, &ceph_opts, &rbd_opts, &spec);
> +	rc = rbd_add_parse_args(buf, &ctx);
>  	if (rc < 0)
>  		goto out;
>  
> -	rbdc = rbd_get_client(ceph_opts);
> +	rbdc = rbd_get_client(ctx.opt);
>  	if (IS_ERR(rbdc)) {
>  		rc = PTR_ERR(rbdc);
>  		goto err_out_args;
>  	}
>  
>  	/* pick the pool */
> -	rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, spec->pool_name);
> +	rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap,
> +				    ctx.rbd_spec->pool_name);
>  	if (rc < 0) {
>  		if (rc == -ENOENT)
> -			pr_info("pool %s does not exist\n", spec->pool_name);
> +			pr_info("pool %s does not exist\n", ctx.rbd_spec->pool_name);
>  		goto err_out_client;
>  	}
> -	spec->pool_id = (u64)rc;
> +	ctx.rbd_spec->pool_id = (u64)rc;
>  
> -	rbd_dev = rbd_dev_create(rbdc, spec, rbd_opts);
> +	rbd_dev = rbd_dev_create(rbdc, ctx.rbd_spec, ctx.rbd_opts);
>  	if (!rbd_dev) {
>  		rc = -ENOMEM;
>  		goto err_out_client;
>  	}
>  	rbdc = NULL;		/* rbd_dev now owns this */
> -	spec = NULL;		/* rbd_dev now owns this */
> -	rbd_opts = NULL;	/* rbd_dev now owns this */
> +	ctx.rbd_spec = NULL;	/* rbd_dev now owns this */
> +	ctx.rbd_opts = NULL;	/* rbd_dev now owns this */
>  
>  	rbd_dev->config_info = kstrdup(buf, GFP_KERNEL);
>  	if (!rbd_dev->config_info) {
> @@ -6106,8 +6120,9 @@ static ssize_t do_rbd_add(struct bus_type *bus,
>  err_out_client:
>  	rbd_put_client(rbdc);
>  err_out_args:
> -	rbd_spec_put(spec);
> -	kfree(rbd_opts);
> +	rbd_spec_put(ctx.rbd_spec);
> +	kfree(ctx.rbd_opts);
> +	ceph_destroy_options(ctx.opt);
>  	goto out;
>  }
>  
> diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c
> index 1bf3502bdd6f..890d914ff5d5 100644
> --- a/fs/ceph/cache.c
> +++ b/fs/ceph/cache.c
> @@ -21,6 +21,7 @@
>   *
>   */
>  
> +#include <linux/fs_context.h>
>  #include "super.h"
>  #include "cache.h"
>  
> @@ -62,7 +63,7 @@ void ceph_fscache_unregister(void)
>  	fscache_unregister_netfs(&ceph_cache_netfs);
>  }
>  
> -int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
> +int ceph_fscache_register_fs(struct fs_context *fc, struct ceph_fs_client* fsc)
>  {
>  	const struct ceph_fsid *fsid = &fsc->client->fsid;
>  	const char *fscache_uniq = fsc->mount_options->fscache_uniq;
> @@ -79,8 +80,8 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
>  		if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len))
>  			continue;
>  
> -		pr_err("fscache cookie already registered for fsid %pU\n", fsid);
> -		pr_err("  use fsc=%%s mount option to specify a uniquifier\n");
> +		errorf(fc, "fscache cookie already registered for fsid %pU\n", fsid);
> +		errorf(fc, "  use fsc=%%s mount option to specify a uniquifier\n");
>  		err = -EBUSY;
>  		goto out_unlock;
>  	}
> @@ -108,7 +109,7 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
>  		list_add_tail(&ent->list, &ceph_fscache_list);
>  	} else {
>  		kfree(ent);
> -		pr_err("unable to register fscache cookie for fsid %pU\n",
> +		errorf(fc, "unable to register fscache cookie for fsid %pU\n",
>  		       fsid);
>  		/* all other fs ignore this error */
>  	}
> diff --git a/fs/ceph/cache.h b/fs/ceph/cache.h
> index 7e72c7594f0c..0df0eb9e7c33 100644
> --- a/fs/ceph/cache.h
> +++ b/fs/ceph/cache.h
> @@ -31,7 +31,7 @@ extern struct fscache_netfs ceph_cache_netfs;
>  int ceph_fscache_register(void);
>  void ceph_fscache_unregister(void);
>  
> -int ceph_fscache_register_fs(struct ceph_fs_client* fsc);
> +int ceph_fscache_register_fs(struct fs_context *fc, struct ceph_fs_client* fsc);
>  void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc);
>  
>  void ceph_fscache_register_inode_cookie(struct inode *inode);
> @@ -103,7 +103,8 @@ static inline void ceph_fscache_unregister(void)
>  {
>  }
>  
> -static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
> +static inline int ceph_fscache_register_fs(struct fs_context *fc,
> +					   struct ceph_fs_client *fsc)
>  {
>  	return 0;
>  }
> diff --git a/fs/ceph/super.c b/fs/ceph/super.c
> index 6d5bb2f74612..edfe9cf08fe5 100644
> --- a/fs/ceph/super.c
> +++ b/fs/ceph/super.c
> @@ -8,7 +8,8 @@
>  #include <linux/in6.h>
>  #include <linux/module.h>
>  #include <linux/mount.h>
> -#include <linux/parser.h>
> +#include <linux/fs_context.h>
> +#include <linux/fs_parser.h>
>  #include <linux/sched.h>
>  #include <linux/seq_file.h>
>  #include <linux/slab.h>
> @@ -128,272 +129,290 @@ static int ceph_sync_fs(struct super_block *sb, int wait)
>   * mount options
>   */
>  enum {
> -	Opt_wsize,
> -	Opt_rsize,
> -	Opt_rasize,
> -	Opt_caps_wanted_delay_min,
> -	Opt_caps_wanted_delay_max,
> +	Opt_acl,
> +	Opt_asyncreaddir,
>  	Opt_caps_max,
> -	Opt_readdir_max_entries,
> -	Opt_readdir_max_bytes,
> +	Opt_caps_wanted_delay_max,
> +	Opt_caps_wanted_delay_min,
>  	Opt_congestion_kb,
> -	Opt_last_int,
> -	/* int args above */
> -	Opt_snapdirname,
> -	Opt_mds_namespace,
> -	Opt_fscache_uniq,
> -	Opt_last_string,
> -	/* string args above */
> -	Opt_dirstat,
> -	Opt_nodirstat,
> -	Opt_rbytes,
> -	Opt_norbytes,
> -	Opt_asyncreaddir,
> -	Opt_noasyncreaddir,
> +	Opt_copyfrom,
>  	Opt_dcache,
> -	Opt_nodcache,
> -	Opt_ino32,
> -	Opt_noino32,
> +	Opt_dirstat,
>  	Opt_fscache,
> -	Opt_nofscache,
> +	Opt_ino32,
> +	Opt_mds_namespace,
>  	Opt_poolperm,
> -	Opt_nopoolperm,
> -	Opt_require_active_mds,
> -	Opt_norequire_active_mds,
> -#ifdef CONFIG_CEPH_FS_POSIX_ACL
> -	Opt_acl,
> -#endif
> -	Opt_noacl,
>  	Opt_quotadf,
> -	Opt_noquotadf,
> -	Opt_copyfrom,
> -	Opt_nocopyfrom,
> +	Opt_rasize,
> +	Opt_rbytes,
> +	Opt_readdir_max_bytes,
> +	Opt_readdir_max_entries,
> +	Opt_require_active_mds,
> +	Opt_rsize,
> +	Opt_snapdirname,
> +	Opt_source,
> +	Opt_wsize,
>  };
>  
> -static match_table_t fsopt_tokens = {
> -	{Opt_wsize, "wsize=%d"},
> -	{Opt_rsize, "rsize=%d"},
> -	{Opt_rasize, "rasize=%d"},
> -	{Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"},
> -	{Opt_caps_wanted_delay_max, "caps_wanted_delay_max=%d"},
> -	{Opt_caps_max, "caps_max=%d"},
> -	{Opt_readdir_max_entries, "readdir_max_entries=%d"},
> -	{Opt_readdir_max_bytes, "readdir_max_bytes=%d"},
> -	{Opt_congestion_kb, "write_congestion_kb=%d"},
> -	/* int args above */
> -	{Opt_snapdirname, "snapdirname=%s"},
> -	{Opt_mds_namespace, "mds_namespace=%s"},
> -	{Opt_fscache_uniq, "fsc=%s"},
> -	/* string args above */
> -	{Opt_dirstat, "dirstat"},
> -	{Opt_nodirstat, "nodirstat"},
> -	{Opt_rbytes, "rbytes"},
> -	{Opt_norbytes, "norbytes"},
> -	{Opt_asyncreaddir, "asyncreaddir"},
> -	{Opt_noasyncreaddir, "noasyncreaddir"},
> -	{Opt_dcache, "dcache"},
> -	{Opt_nodcache, "nodcache"},
> -	{Opt_ino32, "ino32"},
> -	{Opt_noino32, "noino32"},
> -	{Opt_fscache, "fsc"},
> -	{Opt_nofscache, "nofsc"},
> -	{Opt_poolperm, "poolperm"},
> -	{Opt_nopoolperm, "nopoolperm"},
> -	{Opt_require_active_mds, "require_active_mds"},
> -	{Opt_norequire_active_mds, "norequire_active_mds"},
> -#ifdef CONFIG_CEPH_FS_POSIX_ACL
> -	{Opt_acl, "acl"},
> -#endif
> -	{Opt_noacl, "noacl"},
> -	{Opt_quotadf, "quotadf"},
> -	{Opt_noquotadf, "noquotadf"},
> -	{Opt_copyfrom, "copyfrom"},
> -	{Opt_nocopyfrom, "nocopyfrom"},
> -	{-1, NULL}
> +static const struct fs_parameter_spec ceph_param_specs[] = {
> +	fsparam_flag_no ("acl",				Opt_acl),
> +	fsparam_flag_no ("asyncreaddir",		Opt_asyncreaddir),
> +	fsparam_u32	("caps_max",			Opt_caps_max),
> +	fsparam_u32	("caps_wanted_delay_max",	Opt_caps_wanted_delay_max),
> +	fsparam_u32	("caps_wanted_delay_min",	Opt_caps_wanted_delay_min),
> +	fsparam_s32	("write_congestion_kb",		Opt_congestion_kb),
> +	fsparam_flag_no ("copyfrom",			Opt_copyfrom),
> +	fsparam_flag_no ("dcache",			Opt_dcache),
> +	fsparam_flag_no ("dirstat",			Opt_dirstat),
> +	__fsparam	(fs_param_is_string, "fsc",	Opt_fscache,
> +			 fs_param_neg_with_no | fs_param_v_optional),
> +	fsparam_flag_no ("ino32",			Opt_ino32),
> +	fsparam_string	("mds_namespace",		Opt_mds_namespace),
> +	fsparam_flag_no ("poolperm",			Opt_poolperm),
> +	fsparam_flag_no ("quotadf",			Opt_quotadf),
> +	fsparam_u32	("rasize",			Opt_rasize),
> +	fsparam_flag_no ("rbytes",			Opt_rbytes),
> +	fsparam_s32	("readdir_max_bytes",		Opt_readdir_max_bytes),
> +	fsparam_s32	("readdir_max_entries",		Opt_readdir_max_entries),
> +	fsparam_flag_no ("require_active_mds",		Opt_require_active_mds),
> +	fsparam_u32	("rsize",			Opt_rsize),
> +	fsparam_string	("snapdirname",			Opt_snapdirname),
> +	fsparam_string	("source",			Opt_source),
> +	fsparam_u32	("wsize",			Opt_wsize),
> +	{}
>  };
>  
> -static int parse_fsopt_token(char *c, void *private)
> +static const struct fs_parameter_description ceph_fs_parameters = {
> +        .name           = "ceph",
> +        .specs          = ceph_param_specs,
> +};
> +
> +/*
> + * Parse the source parameter.  Distinguish the server list from the path.
> + * Internally we do not include the leading '/' in the path.
> + *
> + * The source will look like:
> + *     <server_spec>[,<server_spec>...]:[<path>]
> + * where
> + *     <server_spec> is <ip>[:<port>]
> + *     <path> is optional, but if present must begin with '/'
> + */
> +static int ceph_parse_source(struct fs_context *fc, struct fs_parameter *param)
>  {
> -	struct ceph_mount_options *fsopt = private;
> -	substring_t argstr[MAX_OPT_ARGS];
> -	int token, intval, ret;
> -
> -	token = match_token((char *)c, fsopt_tokens, argstr);
> -	if (token < 0)
> -		return -EINVAL;
> -
> -	if (token < Opt_last_int) {
> -		ret = match_int(&argstr[0], &intval);
> -		if (ret < 0) {
> -			pr_err("bad option arg (not int) at '%s'\n", c);
> -			return ret;
> +	struct ceph_config_context *ctx = fc->fs_private;
> +	struct ceph_mount_options *fsopt = ctx->mount_options;
> +	char *dev_name = param->string, *dev_name_end;
> +	int ret;
> +
> +	dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name);
> +
> +	if (fc->source)
> +		return invalf(fc, "Multiple sources specified");
> +	if (!dev_name || !*dev_name)
> +		return invalf(fc, "Empty source");
> +	if (dev_name[0] == '/')
> +		return invalf(fc, "Missing colon");
> +
> +	dev_name_end = strchr(dev_name + 1, '/');
> +	if (dev_name_end) {
> +		if (strlen(dev_name_end) > 1) {
> +			kfree(fsopt->server_path);
> +			fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
> +			if (!fsopt->server_path)
> +				return -ENOMEM;
>  		}
> -		dout("got int token %d val %d\n", token, intval);
> -	} else if (token > Opt_last_int && token < Opt_last_string) {
> -		dout("got string token %d val %s\n", token,
> -		     argstr[0].from);
>  	} else {
> -		dout("got token %d\n", token);
> +		dev_name_end = dev_name + strlen(dev_name);
> +	}
> +
> +	/* Trim off the path and the colon separator */
> +	dev_name_end--;
> +	if (*dev_name_end != ':')
> +		return invalf(fc, "device name is missing path (no : separator in %s)\n",
> +			      dev_name);
> +	*dev_name_end = 0;
> +
> +	dout("device name '%s'\n", dev_name);
> +	if (fsopt->server_path)
> +		dout("server path '%s'\n", fsopt->server_path);
> +
> +	param->size = dev_name_end - dev_name;
> +	ret = ceph_parse_server_specs(ctx->opt, fc,
> +				      param->string, dev_name_end - dev_name);
> +	if (ret == 0) {
> +		fc->source = param->string;
> +		param->string = NULL;
>  	}
>  
> -	switch (token) {
> +	return 0;
> +}
> +
> +static int ceph_parse_param(struct fs_context *fc, struct fs_parameter *param)
> +{
> +	struct ceph_config_context *ctx = fc->fs_private;
> +	struct ceph_mount_options *fsopt = ctx->mount_options;
> +	struct fs_parse_result result;
> +	int ret, opt;
> +
> +	ret = ceph_parse_option(ctx->opt, fc, param);
> +	if (ret != -ENOPARAM)
> +		return ret;
> +
> +	opt = fs_parse(fc, &ceph_fs_parameters, param, &result);
> +	if (opt < 0)
> +		return opt;
> +
> +	switch (opt) {
> +	case Opt_source:
> +		return ceph_parse_source(fc, param);
>  	case Opt_snapdirname:
>  		kfree(fsopt->snapdir_name);
> -		fsopt->snapdir_name = kstrndup(argstr[0].from,
> -					       argstr[0].to-argstr[0].from,
> -					       GFP_KERNEL);
> -		if (!fsopt->snapdir_name)
> -			return -ENOMEM;
> +		fsopt->snapdir_name = param->string;
> +		param->string = NULL;
>  		break;
>  	case Opt_mds_namespace:
>  		kfree(fsopt->mds_namespace);
> -		fsopt->mds_namespace = kstrndup(argstr[0].from,
> -						argstr[0].to-argstr[0].from,
> -						GFP_KERNEL);
> -		if (!fsopt->mds_namespace)
> -			return -ENOMEM;
> +		fsopt->mds_namespace = param->string;
> +		param->string = NULL;
>  		break;
> -	case Opt_fscache_uniq:
> -		kfree(fsopt->fscache_uniq);
> -		fsopt->fscache_uniq = kstrndup(argstr[0].from,
> -					       argstr[0].to-argstr[0].from,
> -					       GFP_KERNEL);
> -		if (!fsopt->fscache_uniq)
> -			return -ENOMEM;
> -		fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
> -		break;
> -		/* misc */
>  	case Opt_wsize:
> -		if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_WRITE_SIZE)
> -			return -EINVAL;
> -		fsopt->wsize = ALIGN(intval, PAGE_SIZE);
> +		if (result.uint_32 < (int)PAGE_SIZE || result.uint_32 > CEPH_MAX_WRITE_SIZE)
> +			goto invalid_value;
> +		fsopt->wsize = ALIGN(result.uint_32, PAGE_SIZE);
>  		break;
>  	case Opt_rsize:
> -		if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_READ_SIZE)
> -			return -EINVAL;
> -		fsopt->rsize = ALIGN(intval, PAGE_SIZE);
> +		if (result.uint_32 < (int)PAGE_SIZE || result.uint_32 > CEPH_MAX_READ_SIZE)
> +			goto invalid_value;
> +		fsopt->rsize = ALIGN(result.uint_32, PAGE_SIZE);
>  		break;
>  	case Opt_rasize:
> -		if (intval < 0)
> -			return -EINVAL;
> -		fsopt->rasize = ALIGN(intval, PAGE_SIZE);
> +		fsopt->rasize = ALIGN(result.uint_32, PAGE_SIZE);
>  		break;
>  	case Opt_caps_wanted_delay_min:
> -		if (intval < 1)
> -			return -EINVAL;
> -		fsopt->caps_wanted_delay_min = intval;
> +		if (result.uint_32 < 1)
> +			goto invalid_value;
> +		fsopt->caps_wanted_delay_min = result.uint_32;
>  		break;
>  	case Opt_caps_wanted_delay_max:
> -		if (intval < 1)
> -			return -EINVAL;
> -		fsopt->caps_wanted_delay_max = intval;
> +		if (result.uint_32 < 1)
> +			goto invalid_value;
> +		fsopt->caps_wanted_delay_max = result.uint_32;
>  		break;
>  	case Opt_caps_max:
> -		if (intval < 0)
> -			return -EINVAL;
> -		fsopt->caps_max = intval;
> +		fsopt->caps_max = result.uint_32;
>  		break;
>  	case Opt_readdir_max_entries:
> -		if (intval < 1)
> -			return -EINVAL;
> -		fsopt->max_readdir = intval;
> +		if (result.uint_32 < 1)
> +			goto invalid_value;
> +		fsopt->max_readdir = result.uint_32;
>  		break;
>  	case Opt_readdir_max_bytes:
> -		if (intval < (int)PAGE_SIZE && intval != 0)
> -			return -EINVAL;
> -		fsopt->max_readdir_bytes = intval;
> +		if (result.uint_32 < (int)PAGE_SIZE && result.uint_32 != 0)
> +			goto invalid_value;
> +		fsopt->max_readdir_bytes = result.uint_32;
>  		break;
>  	case Opt_congestion_kb:
> -		if (intval < 1024) /* at least 1M */
> -			return -EINVAL;
> -		fsopt->congestion_kb = intval;
> +		if (result.uint_32 < 1024) /* at least 1M */
> +			goto invalid_value;
> +		fsopt->congestion_kb = result.uint_32;
>  		break;
>  	case Opt_dirstat:
> -		fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT;
> -		break;
> -	case Opt_nodirstat:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT;
> +		if (!result.negated)
> +			fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT;
> +		else
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT;
>  		break;
>  	case Opt_rbytes:
> -		fsopt->flags |= CEPH_MOUNT_OPT_RBYTES;
> -		break;
> -	case Opt_norbytes:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES;
> +		if (!result.negated)
> +			fsopt->flags |= CEPH_MOUNT_OPT_RBYTES;
> +		else
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES;
>  		break;
>  	case Opt_asyncreaddir:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR;
> -		break;
> -	case Opt_noasyncreaddir:
> -		fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
> +		if (!result.negated)
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR;
> +		else
> +			fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
>  		break;
>  	case Opt_dcache:
> -		fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
> -		break;
> -	case Opt_nodcache:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
> +		if (!result.negated)
> +			fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
> +		else
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
>  		break;
>  	case Opt_ino32:
> -		fsopt->flags |= CEPH_MOUNT_OPT_INO32;
> -		break;
> -	case Opt_noino32:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
> +		if (!result.negated)
> +			fsopt->flags |= CEPH_MOUNT_OPT_INO32;
> +		else
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
>  		break;
> +
>  	case Opt_fscache:
> -		fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
> -		kfree(fsopt->fscache_uniq);
> -		fsopt->fscache_uniq = NULL;
> -		break;
> -	case Opt_nofscache:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
>  		kfree(fsopt->fscache_uniq);
>  		fsopt->fscache_uniq = NULL;
> +		if (result.negated) {
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
> +		} else {
> +			fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
> +			fsopt->fscache_uniq = param->string;
> +			param->string = NULL;
> +		}
>  		break;
> +
>  	case Opt_poolperm:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM;
> -		break;
> -	case Opt_nopoolperm:
> -		fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM;
> +		if (!result.negated)
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM;
> +		else
> +			fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM;
>  		break;
>  	case Opt_require_active_mds:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT;
> -		break;
> -	case Opt_norequire_active_mds:
> -		fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
> +		if (!result.negated)
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT;
> +		else
> +			fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
>  		break;
>  	case Opt_quotadf:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
> -		break;
> -	case Opt_noquotadf:
> -		fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
> +		if (!result.negated)
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
> +		else
> +			fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
>  		break;
>  	case Opt_copyfrom:
> -		fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM;
> +		if (!result.negated)
> +			fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM;
> +		else
> +			fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM;
>  		break;
> -	case Opt_nocopyfrom:
> -		fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM;
> -		break;
> -#ifdef CONFIG_CEPH_FS_POSIX_ACL
>  	case Opt_acl:
> -		fsopt->sb_flags |= SB_POSIXACL;
> -		break;
> +		if (!result.negated) {
> +#ifdef CONFIG_CEPH_FS_POSIX_ACL
> +			fc->sb_flags |= SB_POSIXACL;
> +#else
> +			return invalf(fc, "POSIX ACL support is disabled");
>  #endif
> -	case Opt_noacl:
> -		fsopt->sb_flags &= ~SB_POSIXACL;
> +		} else {
> +			fc->sb_flags &= ~SB_POSIXACL;
> +		}
>  		break;
>  	default:
> -		BUG_ON(token);
> +		BUG();
>  	}
>  	return 0;
> +
> +invalid_value:
> +	return invalf(fc, "ceph: Invalid value for %s", param->key);
>  }
>  
>  static void destroy_mount_options(struct ceph_mount_options *args)
>  {
> -	dout("destroy_mount_options %p\n", args);
> -	kfree(args->snapdir_name);
> -	kfree(args->mds_namespace);
> -	kfree(args->server_path);
> -	kfree(args->fscache_uniq);
> -	kfree(args);
> +	if (args) {
> +		dout("destroy_mount_options %p\n", args);
> +		kfree(args->snapdir_name);
> +		kfree(args->mds_namespace);
> +		kfree(args->server_path);
> +		kfree(args->fscache_uniq);
> +		kfree(args);
> +	}
>  }
>  
>  static int strcmp_null(const char *s1, const char *s2)
> @@ -436,91 +455,6 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
>  	return ceph_compare_options(new_opt, fsc->client);
>  }
>  
> -static int parse_mount_options(struct ceph_mount_options **pfsopt,
> -			       struct ceph_options **popt,
> -			       int flags, char *options,
> -			       const char *dev_name)
> -{
> -	struct ceph_mount_options *fsopt;
> -	const char *dev_name_end;
> -	int err;
> -
> -	if (!dev_name || !*dev_name)
> -		return -EINVAL;
> -
> -	fsopt = kzalloc(sizeof(*fsopt), GFP_KERNEL);
> -	if (!fsopt)
> -		return -ENOMEM;
> -
> -	dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name);
> -
> -	fsopt->sb_flags = flags;
> -	fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
> -
> -	fsopt->wsize = CEPH_MAX_WRITE_SIZE;
> -	fsopt->rsize = CEPH_MAX_READ_SIZE;
> -	fsopt->rasize = CEPH_RASIZE_DEFAULT;
> -	fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
> -	if (!fsopt->snapdir_name) {
> -		err = -ENOMEM;
> -		goto out;
> -	}
> -
> -	fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
> -	fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
> -	fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
> -	fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
> -	fsopt->congestion_kb = default_congestion_kb();
> -
> -	/*
> -	 * Distinguish the server list from the path in "dev_name".
> -	 * Internally we do not include the leading '/' in the path.
> -	 *
> -	 * "dev_name" will look like:
> -	 *     <server_spec>[,<server_spec>...]:[<path>]
> -	 * where
> -	 *     <server_spec> is <ip>[:<port>]
> -	 *     <path> is optional, but if present must begin with '/'
> -	 */
> -	dev_name_end = strchr(dev_name, '/');
> -	if (dev_name_end) {
> -		if (strlen(dev_name_end) > 1) {
> -			fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
> -			if (!fsopt->server_path) {
> -				err = -ENOMEM;
> -				goto out;
> -			}
> -		}
> -	} else {
> -		dev_name_end = dev_name + strlen(dev_name);
> -	}
> -	err = -EINVAL;
> -	dev_name_end--;		/* back up to ':' separator */
> -	if (dev_name_end < dev_name || *dev_name_end != ':') {
> -		pr_err("device name is missing path (no : separator in %s)\n",
> -				dev_name);
> -		goto out;
> -	}
> -	dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name);
> -	if (fsopt->server_path)
> -		dout("server path '%s'\n", fsopt->server_path);
> -
> -	*popt = ceph_parse_options(options, dev_name, dev_name_end,
> -				 parse_fsopt_token, (void *)fsopt);
> -	if (IS_ERR(*popt)) {
> -		err = PTR_ERR(*popt);
> -		goto out;
> -	}
> -
> -	/* success */
> -	*pfsopt = fsopt;
> -	return 0;
> -
> -out:
> -	destroy_mount_options(fsopt);
> -	return err;
> -}
> -
>  /**
>   * ceph_show_options - Show mount options in /proc/mounts
>   * @m: seq_file to write to
> @@ -564,7 +498,7 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
>  		seq_puts(m, ",noquotadf");
>  
>  #ifdef CONFIG_CEPH_FS_POSIX_ACL
> -	if (fsopt->sb_flags & SB_POSIXACL)
> +	if (root->d_sb->s_flags & SB_POSIXACL)
>  		seq_puts(m, ",acl");
>  	else
>  		seq_puts(m, ",noacl");
> @@ -624,12 +558,10 @@ static int extra_mon_dispatch(struct ceph_client *client, struct ceph_msg *msg)
>  
>  /*
>   * create a new fs client
> - *
> - * Success or not, this function consumes @fsopt and @opt.
>   */
> -static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
> -					struct ceph_options *opt)
> +static struct ceph_fs_client *create_fs_client(struct fs_context *fc)
>  {
> +	struct ceph_config_context *ctx = fc->fs_private;
>  	struct ceph_fs_client *fsc;
>  	int page_count;
>  	size_t size;
> @@ -641,17 +573,18 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
>  		goto fail;
>  	}
>  
> -	fsc->client = ceph_create_client(opt, fsc);
> +	fsc->client = ceph_create_client(ctx->opt, fsc);
>  	if (IS_ERR(fsc->client)) {
> +		errorf(fc, "ceph: Failed to create client");
>  		err = PTR_ERR(fsc->client);
>  		goto fail;
>  	}
> -	opt = NULL; /* fsc->client now owns this */
> +	ctx->opt = NULL; /* fsc->client now owns this */
>  
>  	fsc->client->extra_mon_dispatch = extra_mon_dispatch;
>  	ceph_set_opt(fsc->client, ABORT_ON_FULL);
>  
> -	if (!fsopt->mds_namespace) {
> +	if (!ctx->mount_options->mds_namespace) {
>  		ceph_monc_want_map(&fsc->client->monc, CEPH_SUB_MDSMAP,
>  				   0, true);
>  	} else {
> @@ -659,7 +592,8 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
>  				   0, false);
>  	}
>  
> -	fsc->mount_options = fsopt;
> +	fsc->mount_options = ctx->mount_options;
> +	ctx->mount_options = NULL;
>  
>  	fsc->sb = NULL;
>  	fsc->mount_state = CEPH_MOUNT_MOUNTING;
> @@ -706,9 +640,6 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
>  	ceph_destroy_client(fsc->client);
>  fail:
>  	kfree(fsc);
> -	if (opt)
> -		ceph_destroy_options(opt);
> -	destroy_mount_options(fsopt);
>  	return ERR_PTR(err);
>  }
>  
> @@ -912,7 +843,7 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
>  /*
>   * mount: join the ceph cluster, and open root directory.
>   */
> -static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
> +static int ceph_real_mount(struct fs_context *fc, struct ceph_fs_client *fsc)
>  {
>  	int err;
>  	unsigned long started = jiffies;  /* note the start time */
> @@ -929,7 +860,7 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
>  
>  		/* setup fscache */
>  		if (fsc->mount_options->flags & CEPH_MOUNT_OPT_FSCACHE) {
> -			err = ceph_fscache_register_fs(fsc);
> +			err = ceph_fscache_register_fs(fc, fsc);
>  			if (err < 0)
>  				goto out;
>  		}
> @@ -943,41 +874,41 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
>  		}
>  
>  		err = ceph_fs_debugfs_init(fsc);
> -		if (err < 0)
> +		if (err < 0) {
> +			errorf(fc, "ceph: Can't create debugfs entries: %d",
> +			       err);
>  			goto out;
> +		}
>  
>  		root = open_root_dentry(fsc, path, started);
>  		if (IS_ERR(root)) {
>  			err = PTR_ERR(root);
>  			goto out;
>  		}
> -		fsc->sb->s_root = dget(root);
> -	} else {
> -		root = dget(fsc->sb->s_root);
> +		fsc->sb->s_root = root;
>  	}
>  
> +	fc->root = dget(fsc->sb->s_root);
>  	fsc->mount_state = CEPH_MOUNT_MOUNTED;
>  	dout("mount success\n");
>  	mutex_unlock(&fsc->client->mount_mutex);
> -	return root;
> +	return err;
>  
>  out:
>  	mutex_unlock(&fsc->client->mount_mutex);
> -	return ERR_PTR(err);
> +	return err;
>  }
>  
> -static int ceph_set_super(struct super_block *s, void *data)
> +static int ceph_set_super(struct super_block *s, struct fs_context *fc)
>  {
> -	struct ceph_fs_client *fsc = data;
> +	struct ceph_fs_client *fsc = s->s_fs_info;
>  	int ret;
>  
> -	dout("set_super %p data %p\n", s, data);
> +	dout("set_super %p\n", s);
>  
> -	s->s_flags = fsc->mount_options->sb_flags;
>  	s->s_maxbytes = MAX_LFS_FILESIZE;
>  
>  	s->s_xattr = ceph_xattr_handlers;
> -	s->s_fs_info = fsc;
>  	fsc->sb = s;
>  	fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */
>  
> @@ -987,24 +918,18 @@ static int ceph_set_super(struct super_block *s, void *data)
>  
>  	s->s_time_gran = 1000;  /* 1000 ns == 1 us */
>  
> -	ret = set_anon_super(s, NULL);  /* what is that second arg for? */
> +	ret = set_anon_super_fc(s, fc);
>  	if (ret != 0)
> -		goto fail;
> -
> -	return ret;
> -
> -fail:
> -	s->s_fs_info = NULL;
> -	fsc->sb = NULL;
> +		fsc->sb = NULL;
>  	return ret;
>  }
>  
>  /*
>   * share superblock if same fs AND options
>   */
> -static int ceph_compare_super(struct super_block *sb, void *data)
> +static int ceph_compare_super(struct super_block *sb, struct fs_context *fc)
>  {
> -	struct ceph_fs_client *new = data;
> +	struct ceph_fs_client *new = fc->s_fs_info;
>  	struct ceph_mount_options *fsopt = new->mount_options;
>  	struct ceph_options *opt = new->client->options;
>  	struct ceph_fs_client *other = ceph_sb_to_client(sb);
> @@ -1020,7 +945,7 @@ static int ceph_compare_super(struct super_block *sb, void *data)
>  		dout("fsid doesn't match\n");
>  		return 0;
>  	}
> -	if (fsopt->sb_flags != other->mount_options->sb_flags) {
> +	if (fc->sb_flags != (sb->s_flags & ~SB_BORN)) {
>  		dout("flags differ\n");
>  		return 0;
>  	}
> @@ -1050,46 +975,41 @@ static int ceph_setup_bdi(struct super_block *sb, struct ceph_fs_client *fsc)
>  	return 0;
>  }
>  
> -static struct dentry *ceph_mount(struct file_system_type *fs_type,
> -		       int flags, const char *dev_name, void *data)
> +static int ceph_get_tree(struct fs_context *fc)
>  {
> -	struct super_block *sb;
>  	struct ceph_fs_client *fsc;
> -	struct dentry *res;
> +	struct super_block *sb;
>  	int err;
> -	int (*compare_super)(struct super_block *, void *) = ceph_compare_super;
> -	struct ceph_mount_options *fsopt = NULL;
> -	struct ceph_options *opt = NULL;
> +	int (*compare_super)(struct super_block *, struct fs_context *) =
> +		ceph_compare_super;
> +
> +	dout("ceph_get_tree\n");
>  
> -	dout("ceph_mount\n");
> +	if (!fc->source)
> +		return invalf(fc, "source parameter not specified");
>  
>  #ifdef CONFIG_CEPH_FS_POSIX_ACL
> -	flags |= SB_POSIXACL;
> +	fc->sb_flags |= SB_POSIXACL;
>  #endif
> -	err = parse_mount_options(&fsopt, &opt, flags, data, dev_name);
> -	if (err < 0) {
> -		res = ERR_PTR(err);
> -		goto out_final;
> -	}
>  
>  	/* create client (which we may/may not use) */
> -	fsc = create_fs_client(fsopt, opt);
> +	fsc = create_fs_client(fc);
>  	if (IS_ERR(fsc)) {
> -		res = ERR_CAST(fsc);
> -		goto out_final;
> +		err = PTR_ERR(fsc);
> +		goto out;
>  	}
>  
>  	err = ceph_mdsc_init(fsc);
> -	if (err < 0) {
> -		res = ERR_PTR(err);
> +	if (err < 0)
>  		goto out;
> -	}
>  
>  	if (ceph_test_opt(fsc->client, NOSHARE))
>  		compare_super = NULL;
> -	sb = sget(fs_type, compare_super, ceph_set_super, flags, fsc);
> +
> +	fc->s_fs_info = fsc;
> +	sb = sget_fc(fc, compare_super, ceph_set_super);
>  	if (IS_ERR(sb)) {
> -		res = ERR_CAST(sb);
> +		err = PTR_ERR(sb);
>  		goto out;
>  	}
>  
> @@ -1101,30 +1021,97 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type,
>  	} else {
>  		dout("get_sb using new client %p\n", fsc);
>  		err = ceph_setup_bdi(sb, fsc);
> -		if (err < 0) {
> -			res = ERR_PTR(err);
> +		if (err < 0)
>  			goto out_splat;
> -		}
>  	}
>  
> -	res = ceph_real_mount(fsc);
> -	if (IS_ERR(res))
> +	err = ceph_real_mount(fc, fsc);
> +	if (err < 0)
>  		goto out_splat;
> -	dout("root %p inode %p ino %llx.%llx\n", res,
> -	     d_inode(res), ceph_vinop(d_inode(res)));
> -	return res;
> +	dout("root %p inode %p ino %llx.%llx\n",
> +	     fc->root, d_inode(fc->root), ceph_vinop(d_inode(fc->root)));
> +	return 0;
>  
>  out_splat:
>  	ceph_mdsc_close_sessions(fsc->mdsc);
>  	deactivate_locked_super(sb);
> -	goto out_final;
> -
>  out:
> -	ceph_mdsc_destroy(fsc);
> -	destroy_fs_client(fsc);
> -out_final:
> -	dout("ceph_mount fail %ld\n", PTR_ERR(res));
> -	return res;
> +	dout("ceph_mount fail %d\n", err);
> +	return err;
> +}
> +
> +static void ceph_free_fc(struct fs_context *fc)
> +{
> +	struct ceph_config_context *ctx = fc->fs_private;
> +	struct ceph_fs_client *fsc = fc->s_fs_info;
> +
> +	if (fsc) {
> +		ceph_mdsc_destroy(fsc);
> +		destroy_fs_client(fsc);
> +	}
> +
> +	if (ctx) {
> +		destroy_mount_options(ctx->mount_options);
> +		ceph_destroy_options(ctx->opt);
> +		kfree(ctx);
> +	}
> +}
> +
> +static const struct fs_context_operations ceph_context_ops = {
> +	.free		= ceph_free_fc,
> +	.parse_param	= ceph_parse_param,
> +	.get_tree	= ceph_get_tree,
> +};
> +
> +/*
> + * Set up the filesystem mount context.
> + */
> +static int ceph_init_fs_context(struct fs_context *fc)
> +{
> +	struct ceph_config_context *ctx;
> +	struct ceph_mount_options *fsopt;
> +
> +	ctx = kzalloc(sizeof(struct ceph_config_context), GFP_KERNEL);
> +	if (!ctx)
> +		goto nomem;
> +
> +	ctx->mount_options = kzalloc(sizeof(struct ceph_mount_options), GFP_KERNEL);
> +	if (!ctx->mount_options)
> +		goto nomem_ctx;
> +
> +	ctx->mount_options->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
> +	if (!ctx->mount_options->snapdir_name)
> +		goto nomem_mo;
> +
> +	ctx->opt = ceph_alloc_options();
> +	if (!ctx->opt)
> +		goto nomem_snap;
> +
> +	fsopt = ctx->mount_options;
> +	fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
> +
> +	fsopt->wsize = CEPH_MAX_WRITE_SIZE;
> +	fsopt->rsize = CEPH_MAX_READ_SIZE;
> +	fsopt->rasize = CEPH_RASIZE_DEFAULT;
> +
> +	fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
> +	fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
> +	fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
> +	fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
> +	fsopt->congestion_kb = default_congestion_kb();
> +
> +	fc->fs_private = ctx;
> +	fc->ops = &ceph_context_ops;
> +	return 0;
> +
> +nomem_snap:
> +	kfree(ctx->mount_options->snapdir_name);
> +nomem_mo:
> +	kfree(ctx->mount_options);
> +nomem_ctx:
> +	kfree(ctx);
> +nomem:
> +	return -ENOMEM;
>  }
>  
>  static void ceph_kill_sb(struct super_block *s)
> @@ -1153,7 +1140,7 @@ static void ceph_kill_sb(struct super_block *s)
>  static struct file_system_type ceph_fs_type = {
>  	.owner		= THIS_MODULE,
>  	.name		= "ceph",
> -	.mount		= ceph_mount,
> +	.init_fs_context = ceph_init_fs_context,
>  	.kill_sb	= ceph_kill_sb,
>  	.fs_flags	= FS_RENAME_DOES_D_MOVE,
>  };
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index 16c03188578e..47e0e4b4f7f8 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -72,7 +72,6 @@
>  
>  struct ceph_mount_options {
>  	int flags;
> -	int sb_flags;
>  
>  	int wsize;            /* max write size */
>  	int rsize;            /* max read size */
> diff --git a/fs/fs_context.c b/fs/fs_context.c
> index f9ad2554c2b8..68822d126f50 100644
> --- a/fs/fs_context.c
> +++ b/fs/fs_context.c
> @@ -195,7 +195,7 @@ EXPORT_SYMBOL(vfs_parse_fs_string);
>  
>  /**
>   * generic_parse_monolithic - Parse key[=val][,key[=val]]* mount data
> - * @ctx: The superblock configuration to fill in.
> + * @fc: The filesystem configuration to fill in.
>   * @data: The data to parse
>   *
>   * Parse a blob of data that's in key[=val][,key[=val]]* form.  This can be
> diff --git a/fs/fs_parser.c b/fs/fs_parser.c
> index bd59e1725c50..a74f1c5f3599 100644
> --- a/fs/fs_parser.c
> +++ b/fs/fs_parser.c
> @@ -64,7 +64,7 @@ static const struct fs_parameter_spec *fs_lookup_key(
>  
>  /*
>   * fs_parse - Parse a filesystem configuration parameter
> - * @fc: The filesystem context to log errors through.
> + * @fc: The filesystem context to log errors through (or NULL).
>   * @desc: The parameter description to use.
>   * @param: The parameter.
>   * @result: Where to place the result of the parse
> diff --git a/include/linux/ceph/ceph_debug.h b/include/linux/ceph/ceph_debug.h
> index d5a5da838caf..fa4a84e0e018 100644
> --- a/include/linux/ceph/ceph_debug.h
> +++ b/include/linux/ceph/ceph_debug.h
> @@ -2,6 +2,7 @@
>  #ifndef _FS_CEPH_DEBUG_H
>  #define _FS_CEPH_DEBUG_H
>  
> +#undef pr_fmt
>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>  
>  #include <linux/string.h>
> diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h
> index 337d5049ff93..82fdb821f19d 100644
> --- a/include/linux/ceph/libceph.h
> +++ b/include/linux/ceph/libceph.h
> @@ -66,6 +66,13 @@ struct ceph_options {
>  	struct ceph_crypto_key *key;
>  };
>  
> +struct ceph_config_context {
> +	struct ceph_options		*opt;
> +	struct ceph_mount_options	*mount_options;
> +	struct rbd_spec			*rbd_spec;
> +	struct rbd_options		*rbd_opts;
> +};
> +
>  /*
>   * defaults
>   */
> @@ -278,10 +285,12 @@ extern const char *ceph_msg_type_name(int type);
>  extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid);
>  extern void *ceph_kvmalloc(size_t size, gfp_t flags);
>  
> -extern struct ceph_options *ceph_parse_options(char *options,
> -			      const char *dev_name, const char *dev_name_end,
> -			      int (*parse_extra_token)(char *c, void *private),
> -			      void *private);
> +struct fs_parameter;
> +extern struct ceph_options *ceph_alloc_options(void);
> +extern int ceph_parse_server_specs(struct ceph_options *opt, struct fs_context *fc,
> +				   const char *data, size_t size);
> +extern int ceph_parse_option(struct ceph_options *opt, struct fs_context *fc,
> +			     struct fs_parameter *param);
>  int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
>  			      bool show_all);
>  extern void ceph_destroy_options(struct ceph_options *opt);
> diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
> index 79eac465ec65..40387d7d30ef 100644
> --- a/net/ceph/ceph_common.c
> +++ b/net/ceph/ceph_common.c
> @@ -10,7 +10,7 @@
>  #include <linux/module.h>
>  #include <linux/mount.h>
>  #include <linux/nsproxy.h>
> -#include <linux/parser.h>
> +#include <linux/fs_parser.h>
>  #include <linux/sched.h>
>  #include <linux/seq_file.h>
>  #include <linux/slab.h>
> @@ -231,70 +231,84 @@ static int parse_fsid(const char *str, struct ceph_fsid *fsid)
>   * ceph options
>   */
>  enum {
> -	Opt_osdtimeout,
> -	Opt_osdkeepalivetimeout,
> +	Opt_abort_on_full,
> +	Opt_cephx_require_signatures,
> +	Opt_cephx_sign_messages,
> +	Opt_crc,
> +	Opt_fsid,
> +	Opt_ip,
> +	Opt_key,
>  	Opt_mount_timeout,
> +	Opt_name,
>  	Opt_osd_idle_ttl,
>  	Opt_osd_request_timeout,
> -	Opt_last_int,
> -	/* int args above */
> -	Opt_fsid,
> -	Opt_name,
> +	Opt_osdkeepalivetimeout,
> +	Opt_osdtimeout,
>  	Opt_secret,
> -	Opt_key,
> -	Opt_ip,
> -	Opt_last_string,
> -	/* string args above */
>  	Opt_share,
> -	Opt_noshare,
> -	Opt_crc,
> -	Opt_nocrc,
> -	Opt_cephx_require_signatures,
> -	Opt_nocephx_require_signatures,
> -	Opt_cephx_sign_messages,
> -	Opt_nocephx_sign_messages,
>  	Opt_tcp_nodelay,
> -	Opt_notcp_nodelay,
> -	Opt_abort_on_full,
>  };
>  
> -static match_table_t opt_tokens = {
> -	{Opt_osdtimeout, "osdtimeout=%d"},
> -	{Opt_osdkeepalivetimeout, "osdkeepalive=%d"},
> -	{Opt_mount_timeout, "mount_timeout=%d"},
> -	{Opt_osd_idle_ttl, "osd_idle_ttl=%d"},
> -	{Opt_osd_request_timeout, "osd_request_timeout=%d"},
> -	/* int args above */
> -	{Opt_fsid, "fsid=%s"},
> -	{Opt_name, "name=%s"},
> -	{Opt_secret, "secret=%s"},
> -	{Opt_key, "key=%s"},
> -	{Opt_ip, "ip=%s"},
> -	/* string args above */
> -	{Opt_share, "share"},
> -	{Opt_noshare, "noshare"},
> -	{Opt_crc, "crc"},
> -	{Opt_nocrc, "nocrc"},
> -	{Opt_cephx_require_signatures, "cephx_require_signatures"},
> -	{Opt_nocephx_require_signatures, "nocephx_require_signatures"},
> -	{Opt_cephx_sign_messages, "cephx_sign_messages"},
> -	{Opt_nocephx_sign_messages, "nocephx_sign_messages"},
> -	{Opt_tcp_nodelay, "tcp_nodelay"},
> -	{Opt_notcp_nodelay, "notcp_nodelay"},
> -	{Opt_abort_on_full, "abort_on_full"},
> -	{-1, NULL}
> +static const struct fs_parameter_spec ceph_option_specs[] = {
> +	fsparam_flag	("abort_on_full",		Opt_abort_on_full),
> +	fsparam_flag_no ("cephx_require_signatures",	Opt_cephx_require_signatures),
> +	fsparam_flag_no ("cephx_sign_messages",		Opt_cephx_sign_messages),
> +	fsparam_flag_no ("crc",				Opt_crc),
> +	fsparam_string	("fsid",			Opt_fsid),
> +	fsparam_string	("ip",				Opt_ip),
> +	fsparam_string	("key",				Opt_key),
> +	fsparam_u32	("mount_timeout",		Opt_mount_timeout),
> +	fsparam_string	("name",			Opt_name),
> +	fsparam_u32	("osd_idle_ttl",		Opt_osd_idle_ttl),
> +	fsparam_u32	("osd_request_timeout",		Opt_osd_request_timeout),
> +	fsparam_u32	("osdkeepalive",		Opt_osdkeepalivetimeout),
> +	__fsparam	(fs_param_is_s32, "osdtimeout", Opt_osdtimeout, fs_param_deprecated),
> +	fsparam_string	("secret",			Opt_secret),
> +	fsparam_flag_no ("share",			Opt_share),
> +	fsparam_flag_no ("tcp_nodelay",			Opt_tcp_nodelay),
> +	{}
> +};
> +
> +static const struct fs_parameter_description ceph_options = {
> +        .name           = "ceph",
> +        .specs          = ceph_option_specs,
>  };
>  
> +struct ceph_options *ceph_alloc_options(void)
> +{
> +	struct ceph_options *opt;
> +	
> +	opt = kzalloc(sizeof(struct ceph_options), GFP_KERNEL);
> +	if (!opt)
> +		return NULL;
> +
> +	opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr), GFP_KERNEL);
> +	if (!opt->mon_addr) {
> +		kfree(opt);
> +		return NULL;
> +	}
> +
> +	opt->flags = CEPH_OPT_DEFAULT;
> +	opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
> +	opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT;
> +	opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;
> +	opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT;
> +	return opt;
> +}
> +EXPORT_SYMBOL(ceph_alloc_options);
> +
>  void ceph_destroy_options(struct ceph_options *opt)
>  {
> -	dout("destroy_options %p\n", opt);
> -	kfree(opt->name);
> -	if (opt->key) {
> -		ceph_crypto_key_destroy(opt->key);
> -		kfree(opt->key);
> +	if (opt) {
> +		dout("destroy_options %p\n", opt);
> +		kfree(opt->name);
> +		if (opt->key) {
> +			ceph_crypto_key_destroy(opt->key);
> +			kfree(opt->key);
> +		}
> +		kfree(opt->mon_addr);
> +		kfree(opt);
>  	}
> -	kfree(opt->mon_addr);
> -	kfree(opt);
>  }
>  EXPORT_SYMBOL(ceph_destroy_options);
>  
> @@ -343,217 +357,141 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) {
>  	return err;
>  }
>  
> -struct ceph_options *
> -ceph_parse_options(char *options, const char *dev_name,
> -			const char *dev_name_end,
> -			int (*parse_extra_token)(char *c, void *private),
> -			void *private)
> +int ceph_parse_server_specs(struct ceph_options *opt, struct fs_context *fc,
> +			    const char *data, size_t size)
>  {
> -	struct ceph_options *opt;
> -	const char *c;
> -	int err = -ENOMEM;
> -	substring_t argstr[MAX_OPT_ARGS];
> -
> -	opt = kzalloc(sizeof(*opt), GFP_KERNEL);
> -	if (!opt)
> -		return ERR_PTR(-ENOMEM);
> -	opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr),
> -				GFP_KERNEL);
> -	if (!opt->mon_addr)
> -		goto out;
> -
> -	dout("parse_options %p options '%s' dev_name '%s'\n", opt, options,
> -	     dev_name);
> -
> -	/* start with defaults */
> -	opt->flags = CEPH_OPT_DEFAULT;
> -	opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
> -	opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT;
> -	opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;
> -	opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT;
> -
>  	/* get mon ip(s) */
>  	/* ip1[:port1][,ip2[:port2]...] */
> -	err = ceph_parse_ips(dev_name, dev_name_end, opt->mon_addr,
> -			     CEPH_MAX_MON, &opt->num_mon);
> -	if (err < 0)
> -		goto out;
> +	return ceph_parse_ips(data, data + size,
> +			      opt->mon_addr, CEPH_MAX_MON, &opt->num_mon);
> +}
> +EXPORT_SYMBOL(ceph_parse_server_specs);
>  
> -	/* parse mount options */
> -	while ((c = strsep(&options, ",")) != NULL) {
> -		int token, intval;
> -		if (!*c)
> -			continue;
> -		err = -EINVAL;
> -		token = match_token((char *)c, opt_tokens, argstr);
> -		if (token < 0 && parse_extra_token) {
> -			/* extra? */
> -			err = parse_extra_token((char *)c, private);
> -			if (err < 0) {
> -				pr_err("bad option at '%s'\n", c);
> -				goto out;
> -			}
> -			continue;
> -		}
> -		if (token < Opt_last_int) {
> -			err = match_int(&argstr[0], &intval);
> -			if (err < 0) {
> -				pr_err("bad option arg (not int) at '%s'\n", c);
> -				goto out;
> -			}
> -			dout("got int token %d val %d\n", token, intval);
> -		} else if (token > Opt_last_int && token < Opt_last_string) {
> -			dout("got string token %d val %s\n", token,
> -			     argstr[0].from);
> -		} else {
> -			dout("got token %d\n", token);
> -		}
> -		switch (token) {
> -		case Opt_ip:
> -			err = ceph_parse_ips(argstr[0].from,
> -					     argstr[0].to,
> -					     &opt->my_addr,
> -					     1, NULL);
> -			if (err < 0)
> -				goto out;
> +int ceph_parse_option(struct ceph_options *opt, struct fs_context *fc,
> +		      struct fs_parameter *param)
> +{
> +	struct fs_parse_result result;
> +	int token, err;
> +
> +	dout("parse_option '%s'\n", param->key);
> +
> +	token = fs_parse(fc, &ceph_options, param, &result);
> +	if (token < 0)
> +		return token;
> +
> +	switch (token) {
> +	case Opt_ip:
> +		err = ceph_parse_ips(param->string,
> +				     param->string + param->size,
> +				     &opt->my_addr,
> +				     1, NULL);
> +		if (err == 0)
>  			opt->flags |= CEPH_OPT_MYIP;
> -			break;
> +		break;
>  
> -		case Opt_fsid:
> -			err = parse_fsid(argstr[0].from, &opt->fsid);
> -			if (err == 0)
> -				opt->flags |= CEPH_OPT_FSID;
> -			break;
> -		case Opt_name:
> -			kfree(opt->name);
> -			opt->name = kstrndup(argstr[0].from,
> -					      argstr[0].to-argstr[0].from,
> -					      GFP_KERNEL);
> -			if (!opt->name) {
> -				err = -ENOMEM;
> -				goto out;
> -			}
> -			break;
> -		case Opt_secret:
> -			ceph_crypto_key_destroy(opt->key);
> -			kfree(opt->key);
> -
> -		        opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
> -			if (!opt->key) {
> -				err = -ENOMEM;
> -				goto out;
> -			}
> -			err = ceph_crypto_key_unarmor(opt->key, argstr[0].from);
> -			if (err < 0)
> -				goto out;
> -			break;
> -		case Opt_key:
> -			ceph_crypto_key_destroy(opt->key);
> -			kfree(opt->key);
> -
> -		        opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
> -			if (!opt->key) {
> -				err = -ENOMEM;
> -				goto out;
> -			}
> -			err = get_secret(opt->key, argstr[0].from);
> -			if (err < 0)
> -				goto out;
> -			break;
> +	case Opt_fsid:
> +		err = parse_fsid(param->string, &opt->fsid);
> +		if (err < 0)
> +			return invalf(fc, "Invalid fsid");
> +		opt->flags |= CEPH_OPT_FSID;
> +		break;
> +	case Opt_name:
> +		kfree(opt->name);
> +		opt->name = param->string;
> +		param->string = NULL;
> +		break;
> +	case Opt_secret:
> +		ceph_crypto_key_destroy(opt->key);
> +		kfree(opt->key);
>  
> -			/* misc */
> -		case Opt_osdtimeout:
> -			pr_warn("ignoring deprecated osdtimeout option\n");
> -			break;
> -		case Opt_osdkeepalivetimeout:
> -			/* 0 isn't well defined right now, reject it */
> -			if (intval < 1 || intval > INT_MAX / 1000) {
> -				pr_err("osdkeepalive out of range\n");
> -				err = -EINVAL;
> -				goto out;
> -			}
> -			opt->osd_keepalive_timeout =
> -					msecs_to_jiffies(intval * 1000);
> -			break;
> -		case Opt_osd_idle_ttl:
> -			/* 0 isn't well defined right now, reject it */
> -			if (intval < 1 || intval > INT_MAX / 1000) {
> -				pr_err("osd_idle_ttl out of range\n");
> -				err = -EINVAL;
> -				goto out;
> -			}
> -			opt->osd_idle_ttl = msecs_to_jiffies(intval * 1000);
> -			break;
> -		case Opt_mount_timeout:
> -			/* 0 is "wait forever" (i.e. infinite timeout) */
> -			if (intval < 0 || intval > INT_MAX / 1000) {
> -				pr_err("mount_timeout out of range\n");
> -				err = -EINVAL;
> -				goto out;
> -			}
> -			opt->mount_timeout = msecs_to_jiffies(intval * 1000);
> -			break;
> -		case Opt_osd_request_timeout:
> -			/* 0 is "wait forever" (i.e. infinite timeout) */
> -			if (intval < 0 || intval > INT_MAX / 1000) {
> -				pr_err("osd_request_timeout out of range\n");
> -				err = -EINVAL;
> -				goto out;
> -			}
> -			opt->osd_request_timeout = msecs_to_jiffies(intval * 1000);
> -			break;
> +		opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
> +		if (!opt->key)
> +			return -ENOMEM;
> +		return ceph_crypto_key_unarmor(opt->key, param->string);
> +	case Opt_key:
> +		ceph_crypto_key_destroy(opt->key);
> +		kfree(opt->key);
>  
> -		case Opt_share:
> +		opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
> +		if (!opt->key)
> +			return -ENOMEM;
> +		return get_secret(opt->key, param->string);
> +
> +		/* misc */
> +	case Opt_osdkeepalivetimeout:
> +		/* 0 isn't well defined right now, reject it */
> +		if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000)
> +			goto out_of_range;
> +		opt->osd_keepalive_timeout =
> +			msecs_to_jiffies(result.uint_32 * 1000);
> +		break;
> +	case Opt_osd_idle_ttl:
> +		/* 0 isn't well defined right now, reject it */
> +		if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000)
> +			goto out_of_range;
> +		opt->osd_idle_ttl = msecs_to_jiffies(result.uint_32 * 1000);
> +		break;
> +	case Opt_mount_timeout:
> +		/* 0 is "wait forever" (i.e. infinite timeout) */
> +		if (result.uint_32 > INT_MAX / 1000)
> +			goto out_of_range;
> +		opt->mount_timeout = msecs_to_jiffies(result.uint_32 * 1000);
> +		break;
> +	case Opt_osd_request_timeout:
> +		/* 0 is "wait forever" (i.e. infinite timeout) */
> +		if (result.uint_32 > INT_MAX / 1000)
> +			goto out_of_range;
> +		opt->osd_request_timeout = msecs_to_jiffies(result.uint_32 * 1000);
> +		break;
> +
> +	case Opt_share:
> +		if (!result.negated)
>  			opt->flags &= ~CEPH_OPT_NOSHARE;
> -			break;
> -		case Opt_noshare:
> +		else
>  			opt->flags |= CEPH_OPT_NOSHARE;
> -			break;
> +		break;
>  
> -		case Opt_crc:
> +	case Opt_crc:
> +		if (!result.negated)
>  			opt->flags &= ~CEPH_OPT_NOCRC;
> -			break;
> -		case Opt_nocrc:
> +		else
>  			opt->flags |= CEPH_OPT_NOCRC;
> -			break;
> +		break;
>  
> -		case Opt_cephx_require_signatures:
> +	case Opt_cephx_require_signatures:
> +		if (!result.negated)
>  			opt->flags &= ~CEPH_OPT_NOMSGAUTH;
> -			break;
> -		case Opt_nocephx_require_signatures:
> +		else
>  			opt->flags |= CEPH_OPT_NOMSGAUTH;
> -			break;
> -		case Opt_cephx_sign_messages:
> +		break;
> +	case Opt_cephx_sign_messages:
> +		if (!result.negated)
>  			opt->flags &= ~CEPH_OPT_NOMSGSIGN;
> -			break;
> -		case Opt_nocephx_sign_messages:
> +		else
>  			opt->flags |= CEPH_OPT_NOMSGSIGN;
> -			break;
> +		break;
>  
> -		case Opt_tcp_nodelay:
> +	case Opt_tcp_nodelay:
> +		if (!result.negated)
>  			opt->flags |= CEPH_OPT_TCP_NODELAY;
> -			break;
> -		case Opt_notcp_nodelay:
> +		else
>  			opt->flags &= ~CEPH_OPT_TCP_NODELAY;
> -			break;
> +		break;
>  
> -		case Opt_abort_on_full:
> -			opt->flags |= CEPH_OPT_ABORT_ON_FULL;
> -			break;
> +	case Opt_abort_on_full:
> +		opt->flags |= CEPH_OPT_ABORT_ON_FULL;
> +		break;
>  
> -		default:
> -			BUG_ON(token);
> -		}
> +	default:
> +		BUG();
>  	}
>  
> -	/* success */
> -	return opt;
> +	return 0;
>  
> -out:
> -	ceph_destroy_options(opt);
> -	return ERR_PTR(err);
> +out_of_range:
> +	return invalf(fc, "ceph: %s out of range", param->key);
>  }
> -EXPORT_SYMBOL(ceph_parse_options);
> +EXPORT_SYMBOL(ceph_parse_option);
>  
>  int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
>  			      bool show_all)
> 

Reviewed-by: Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 87+ messages in thread

end of thread, other threads:[~2019-04-27 12:27 UTC | newest]

Thread overview: 87+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <155372999953.7602.13784796495137723805.stgit@warthog.procyon.org.uk>
2019-03-27 23:40 ` [RFC PATCH 01/68] kbuild: skip sub-make for in-tree build with GNU Make 4.x David Howells
2019-03-28  0:53   ` Masahiro Yamada
2019-03-30 12:11     ` Masahiro Yamada
2019-04-10  9:54   ` David Howells
2019-03-27 23:40 ` [RFC PATCH 02/68] vfs: Update mount API docs David Howells
2019-03-27 23:40 ` [RFC PATCH 03/68] vfs: Fix refcounting of filenames in fs_parser David Howells
2019-03-27 23:41 ` [RFC PATCH 04/68] vfs: Provide sb->s_iflags settings in fs_context struct David Howells
2019-03-27 23:41 ` [RFC PATCH 05/68] vfs: Provide a mount_pseudo-replacement for the new mount API David Howells
2019-03-27 23:41 ` [RFC PATCH 06/68] vfs: Convert aio to use " David Howells
2019-03-27 23:41 ` [RFC PATCH 07/68] vfs: Convert anon_inodes " David Howells
2019-03-27 23:41 ` [RFC PATCH 08/68] vfs: Convert bdev " David Howells
2019-03-27 23:41 ` [RFC PATCH 09/68] vfs: Convert nsfs " David Howells
2019-03-27 23:41 ` [RFC PATCH 10/68] vfs: Convert pipe " David Howells
2019-03-27 23:41 ` [RFC PATCH 11/68] vfs: Convert zsmalloc " David Howells
2019-03-27 23:42 ` [RFC PATCH 12/68] vfs: Convert sockfs " David Howells
2019-03-27 23:42 ` [RFC PATCH 13/68] vfs: Convert dax " David Howells
2019-03-27 23:42 ` [RFC PATCH 14/68] vfs: Convert drm " David Howells
2019-03-28  7:47   ` Daniel Vetter
2019-04-10  9:59   ` David Howells
2019-03-27 23:42 ` [RFC PATCH 15/68] vfs: Convert ia64 perfmon " David Howells
2019-03-27 23:42 ` [RFC PATCH 16/68] vfs: Convert cxl " David Howells
2019-03-27 23:42 ` [RFC PATCH 17/68] vfs: Convert ocxlflash " David Howells
2019-03-27 23:42 ` [RFC PATCH 18/68] vfs: Convert virtio_balloon " David Howells
2019-03-27 23:42 ` [RFC PATCH 19/68] vfs: Convert btrfs_test " David Howells
2019-03-27 23:43 ` [RFC PATCH 20/68] vfs: Kill off mount_pseudo() and mount_pseudo_xattr() David Howells
2019-03-27 23:43 ` [RFC PATCH 21/68] vfs: Use sget_fc() for pseudo-filesystems David Howells
2019-03-27 23:43 ` [RFC PATCH 22/68] vfs: Convert binderfs to use the new mount API David Howells
2019-03-28 14:45   ` Dan Carpenter
2019-04-10 10:01   ` David Howells
2019-03-27 23:43 ` [RFC PATCH 23/68] vfs: Convert nfsctl " David Howells
2019-03-27 23:43 ` [RFC PATCH 24/68] vfs: Convert rpc_pipefs " David Howells
2019-03-27 23:43 ` [RFC PATCH 25/68] vfs: Kill mount_ns() David Howells
2019-03-27 23:43 ` [RFC PATCH 26/68] vfs: Kill sget_userns() David Howells
2019-03-27 23:43 ` [RFC PATCH 27/68] vfs: Convert binfmt_misc to use the new mount API David Howells
2019-03-27 23:44 ` [RFC PATCH 28/68] vfs: Convert configfs " David Howells
2019-03-27 23:44 ` [RFC PATCH 29/68] vfs: Convert efivarfs " David Howells
2019-03-27 23:44 ` [RFC PATCH 30/68] vfs: Convert fusectl " David Howells
2019-04-24 13:49   ` Miklos Szeredi
2019-04-24 15:16   ` David Howells
2019-03-27 23:44 ` [RFC PATCH 31/68] vfs: Convert qib_fs/ipathfs " David Howells
2019-04-09 17:14   ` Dennis Dalessandro
2019-03-27 23:44 ` [RFC PATCH 32/68] vfs: Convert ibmasmfs " David Howells
2019-03-27 23:44 ` [RFC PATCH 33/68] vfs: Convert oprofilefs " David Howells
2019-03-27 23:44 ` [RFC PATCH 34/68] vfs: Convert gadgetfs " David Howells
2019-03-27 23:44 ` [RFC PATCH 35/68] vfs: Convert xenfs " David Howells
2019-04-02  7:38   ` Juergen Gross
2019-03-27 23:45 ` [RFC PATCH 36/68] vfs: Convert openpromfs " David Howells
2019-03-27 23:45 ` [RFC PATCH 37/68] vfs: Convert apparmorfs " David Howells
2019-03-27 23:45 ` [RFC PATCH 38/68] vfs: Convert securityfs " David Howells
2019-03-27 23:45 ` [RFC PATCH 39/68] vfs: Convert selinuxfs " David Howells
2019-03-27 23:45 ` [RFC PATCH 40/68] vfs: Convert smackfs " David Howells
2019-03-27 23:45 ` [RFC PATCH 41/68] vfs: Convert ramfs, shmem, tmpfs, devtmpfs, rootfs " David Howells
2019-03-27 23:45 ` [RFC PATCH 42/68] vfs: Create fs_context-aware mount_bdev() replacement David Howells
2019-03-27 23:45 ` [RFC PATCH 43/68] vfs: Make fs_parse() handle fs_param_is_fd-type params better David Howells
2019-03-27 23:46 ` [RFC PATCH 44/68] vfs: Convert fuse to use the new mount API David Howells
2019-04-24 13:53   ` Miklos Szeredi
2019-04-24 15:22   ` David Howells
2019-04-24 17:28     ` Miklos Szeredi
2019-03-27 23:46 ` [RFC PATCH 45/68] vfs: Move the subtype parameter into fuse David Howells
2019-03-27 23:46 ` [RFC PATCH 46/68] mtd: Provide fs_context-aware mount_mtd() replacement David Howells
2019-03-27 23:46 ` [RFC PATCH 47/68] vfs: Convert romfs to use the new mount API David Howells
2019-03-27 23:46 ` [RFC PATCH 48/68] vfs: Convert cramfs " David Howells
2019-04-01 15:25   ` Nicolas Pitre
2019-03-27 23:46 ` [RFC PATCH 49/68] vfs: Convert jffs2 " David Howells
2019-03-27 23:46 ` [RFC PATCH 50/68] mtd: Kill mount_mtd() David Howells
2019-03-27 23:47 ` [RFC PATCH 51/68] vfs: Convert squashfs to use the new mount API David Howells
2019-03-27 23:47 ` [RFC PATCH 52/68] vfs: Convert ceph " David Howells
2019-04-27 12:27   ` Jeff Layton
2019-03-27 23:47 ` [RFC PATCH 53/68] vfs: Convert functionfs " David Howells
2019-03-28 10:43   ` Michał Nazarewicz
2019-03-27 23:47 ` [RFC PATCH 54/68] vfs: Add a single-or-reconfig keying to vfs_get_super() David Howells
2019-03-27 23:47 ` [RFC PATCH 55/68] vfs: Convert debugfs to use the new mount API David Howells
2019-03-27 23:47 ` [RFC PATCH 56/68] vfs: Convert tracefs " David Howells
2019-03-27 23:47 ` [RFC PATCH 57/68] vfs: Convert pstore " David Howells
2019-03-28  0:24   ` Kees Cook
2019-03-27 23:47 ` [RFC PATCH 58/68] hypfs: Fix error number left in struct pointer member David Howells
2019-03-27 23:48 ` [RFC PATCH 59/68] vfs: Convert hypfs to use the new mount API David Howells
2019-03-27 23:48 ` [RFC PATCH 60/68] vfs: Convert spufs " David Howells
2019-03-27 23:48 ` [RFC PATCH 61/68] vfs: Kill mount_single() David Howells
2019-03-27 23:48 ` [RFC PATCH 62/68] vfs: Convert coda to use the new mount API David Howells
2019-03-27 23:48 ` [RFC PATCH 63/68] vfs: Convert autofs " David Howells
2019-03-27 23:48 ` [RFC PATCH 64/68] vfs: Convert devpts " David Howells
2019-04-05 12:52   ` Christian Brauner
2019-03-27 23:48 ` [RFC PATCH 65/68] vfs: Convert bpf " David Howells
2019-03-27 23:48 ` [RFC PATCH 66/68] vfs: Convert ubifs " David Howells
2019-03-27 23:49 ` [RFC PATCH 67/68] vfs: Convert orangefs " David Howells
2019-03-27 23:49 ` [RFC PATCH 68/68] gfs2: Convert gfs2 to fs_context David Howells

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).