• David Howells's avatar
    vfs: Add configuration parser helpers · 82685ada
    David Howells authored
    Because the new API passes in key,value parameters, match_token() cannot be
    used with it.  Instead, provide three new helpers to aid with parsing:
     (1) fs_parse().  This takes a parameter and a simple static description of
         all the parameters and maps the key name to an ID.  It returns 1 on a
         match, 0 on no match if unknowns should be ignored and some other
         negative error code on a parse error.
         The parameter description includes a list of key names to IDs, desired
         parameter types and a list of enumeration name -> ID mappings.
         [!] Note that for the moment I've required that the key->ID mapping
         array is expected to be sorted and unterminated.  The size of the
         array is noted in the fsconfig_parser struct.  This allows me to use
         bsearch(), but I'm not sure any performance gain is worth the hassle
         of requiring people to keep the array sorted.
         The parameter type array is sized according to the number of parameter
         IDs and is indexed directly.  The optional enum mapping array is an
         unterminated, unsorted list and the size goes into the fsconfig_parser
         The function can do some additional things:
    	(a) If it's not ambiguous and no value is given, the prefix "no" on
    	    a key name is permitted to indicate that the parameter should
    	    be considered negatory.
    	(b) If the desired type is a single simple integer, it will perform
    	    an appropriate conversion and store the result in a union in
    	    the parse result.
    	(c) If the desired type is an enumeration, {key ID, name} will be
    	    looked up in the enumeration list and the matching value will
    	    be stored in the parse result union.
    	(d) Optionally generate an error if the key is unrecognised.
         This is called something like:
    	enum rdt_param {
    	const struct fs_parameter_spec rdt_param_specs[nr__rdt_params] = {
    		[Opt_cdp]	= { fs_param_is_bool },
    		[Opt_cdpl2]	= { fs_param_is_bool },
    		[Opt_mba_mpbs]	= { fs_param_is_bool },
    	const const char *const rdt_param_keys[nr__rdt_params] = {
    		[Opt_cdp]	= "cdp",
    		[Opt_cdpl2]	= "cdpl2",
    		[Opt_mba_mpbs]	= "mba_mbps",
    	const struct fs_parameter_description rdt_parser = {
    		.name		= "rdt",
    		.nr_params	= nr__rdt_params,
    		.keys		= rdt_param_keys,
    		.specs		= rdt_param_specs,
    		.no_source	= true,
    	int rdt_parse_param(struct fs_context *fc,
    			    struct fs_parameter *param)
    		struct fs_parse_result parse;
    		struct rdt_fs_context *ctx = rdt_fc2context(fc);
    		int ret;
    		ret = fs_parse(fc, &rdt_parser, param, &parse);
    		if (ret < 0)
    			return ret;
    		switch (parse.key) {
    		case Opt_cdp:
    			ctx->enable_cdpl3 = true;
    			return 0;
    		case Opt_cdpl2:
    			ctx->enable_cdpl2 = true;
    			return 0;
    		case Opt_mba_mpbs:
    			ctx->enable_mba_mbps = true;
    			return 0;
    		return -EINVAL;
     (2) fs_lookup_param().  This takes a { dirfd, path, LOOKUP_EMPTY? } or
         string value and performs an appropriate path lookup to convert it
         into a path object, which it will then return.
         If the desired type was a blockdev, the type of the looked up inode
         will be checked to make sure it is one.
         This can be used like:
    	enum foo_param {
    	const struct fs_parameter_spec foo_param_specs[nr__foo_params] = {
    		[Opt_source]	= { fs_param_is_blockdev },
    	const char *char foo_param_keys[nr__foo_params] = {
    		[Opt_source]	= "source",
    	const struct constant_table foo_param_alt_keys[] = {
    		{ "device",	Opt_source },
    	const struct fs_parameter_description foo_parser = {
    		.name		= "foo",
    		.nr_params	= nr__foo_params,
    		.nr_alt_keys	= ARRAY_SIZE(foo_param_alt_keys),
    		.keys		= foo_param_keys,
    		.alt_keys	= foo_param_alt_keys,
    		.specs		= foo_param_specs,
    	int foo_parse_param(struct fs_context *fc,
    			    struct fs_parameter *param)
    		struct fs_parse_result parse;
    		struct foo_fs_context *ctx = foo_fc2context(fc);
    		int ret;
    		ret = fs_parse(fc, &foo_parser, param, &parse);
    		if (ret < 0)
    			return ret;
    		switch (parse.key) {
    		case Opt_source:
    			return fs_lookup_param(fc, &foo_parser, param,
    					       &parse, &ctx->source);
    			return -EINVAL;
     (3) lookup_constant().  This takes a table of named constants and looks up
         the given name within it.  The table is expected to be sorted such
         that bsearch() be used upon it.
         Possibly I should require the table be terminated and just use a
         for-loop to scan it instead of using bsearch() to reduce hassle.
         Tables look something like:
    	static const struct constant_table bool_names[] = {
    		{ "0",		false },
    		{ "1",		true },
    		{ "false",	false },
    		{ "no",		false },
    		{ "true",	true },
    		{ "yes",	true },
         and a lookup is done with something like:
    	b = lookup_constant(bool_names, param->string, -1);
    Additionally, optional validation routines for the parameter description
    are provided that can be enabled at compile time.  A later patch will
    invoke these when a filesystem is registered.
    Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
    Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
internal.h 5.21 KB