xref: /linux-6.15/fs/fs_parser.c (revision b4d0d230)
1*b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
231d921c7SDavid Howells /* Filesystem parameter parser.
331d921c7SDavid Howells  *
431d921c7SDavid Howells  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
531d921c7SDavid Howells  * Written by David Howells ([email protected])
631d921c7SDavid Howells  */
731d921c7SDavid Howells 
831d921c7SDavid Howells #include <linux/export.h>
931d921c7SDavid Howells #include <linux/fs_context.h>
1031d921c7SDavid Howells #include <linux/fs_parser.h>
1131d921c7SDavid Howells #include <linux/slab.h>
1231d921c7SDavid Howells #include <linux/security.h>
1331d921c7SDavid Howells #include <linux/namei.h>
1431d921c7SDavid Howells #include "internal.h"
1531d921c7SDavid Howells 
1631d921c7SDavid Howells static const struct constant_table bool_names[] = {
1731d921c7SDavid Howells 	{ "0",		false },
1831d921c7SDavid Howells 	{ "1",		true },
1931d921c7SDavid Howells 	{ "false",	false },
2031d921c7SDavid Howells 	{ "no",		false },
2131d921c7SDavid Howells 	{ "true",	true },
2231d921c7SDavid Howells 	{ "yes",	true },
2331d921c7SDavid Howells };
2431d921c7SDavid Howells 
2531d921c7SDavid Howells /**
2631d921c7SDavid Howells  * lookup_constant - Look up a constant by name in an ordered table
2731d921c7SDavid Howells  * @tbl: The table of constants to search.
2831d921c7SDavid Howells  * @tbl_size: The size of the table.
2931d921c7SDavid Howells  * @name: The name to look up.
3031d921c7SDavid Howells  * @not_found: The value to return if the name is not found.
3131d921c7SDavid Howells  */
3231d921c7SDavid Howells int __lookup_constant(const struct constant_table *tbl, size_t tbl_size,
3331d921c7SDavid Howells 		      const char *name, int not_found)
3431d921c7SDavid Howells {
3531d921c7SDavid Howells 	unsigned int i;
3631d921c7SDavid Howells 
3731d921c7SDavid Howells 	for (i = 0; i < tbl_size; i++)
3831d921c7SDavid Howells 		if (strcmp(name, tbl[i].name) == 0)
3931d921c7SDavid Howells 			return tbl[i].value;
4031d921c7SDavid Howells 
4131d921c7SDavid Howells 	return not_found;
4231d921c7SDavid Howells }
4331d921c7SDavid Howells EXPORT_SYMBOL(__lookup_constant);
4431d921c7SDavid Howells 
4531d921c7SDavid Howells static const struct fs_parameter_spec *fs_lookup_key(
4631d921c7SDavid Howells 	const struct fs_parameter_description *desc,
4731d921c7SDavid Howells 	const char *name)
4831d921c7SDavid Howells {
4931d921c7SDavid Howells 	const struct fs_parameter_spec *p;
5031d921c7SDavid Howells 
5131d921c7SDavid Howells 	if (!desc->specs)
5231d921c7SDavid Howells 		return NULL;
5331d921c7SDavid Howells 
5431d921c7SDavid Howells 	for (p = desc->specs; p->name; p++)
5531d921c7SDavid Howells 		if (strcmp(p->name, name) == 0)
5631d921c7SDavid Howells 			return p;
5731d921c7SDavid Howells 
5831d921c7SDavid Howells 	return NULL;
5931d921c7SDavid Howells }
6031d921c7SDavid Howells 
6131d921c7SDavid Howells /*
6231d921c7SDavid Howells  * fs_parse - Parse a filesystem configuration parameter
6331d921c7SDavid Howells  * @fc: The filesystem context to log errors through.
6431d921c7SDavid Howells  * @desc: The parameter description to use.
6531d921c7SDavid Howells  * @param: The parameter.
6631d921c7SDavid Howells  * @result: Where to place the result of the parse
6731d921c7SDavid Howells  *
6831d921c7SDavid Howells  * Parse a filesystem configuration parameter and attempt a conversion for a
6931d921c7SDavid Howells  * simple parameter for which this is requested.  If successful, the determined
7031d921c7SDavid Howells  * parameter ID is placed into @result->key, the desired type is indicated in
7131d921c7SDavid Howells  * @result->t and any converted value is placed into an appropriate member of
7231d921c7SDavid Howells  * the union in @result.
7331d921c7SDavid Howells  *
7431d921c7SDavid Howells  * The function returns the parameter number if the parameter was matched,
7531d921c7SDavid Howells  * -ENOPARAM if it wasn't matched and @desc->ignore_unknown indicated that
7631d921c7SDavid Howells  * unknown parameters are okay and -EINVAL if there was a conversion issue or
7731d921c7SDavid Howells  * the parameter wasn't recognised and unknowns aren't okay.
7831d921c7SDavid Howells  */
7931d921c7SDavid Howells int fs_parse(struct fs_context *fc,
8031d921c7SDavid Howells 	     const struct fs_parameter_description *desc,
8131d921c7SDavid Howells 	     struct fs_parameter *param,
8231d921c7SDavid Howells 	     struct fs_parse_result *result)
8331d921c7SDavid Howells {
8431d921c7SDavid Howells 	const struct fs_parameter_spec *p;
8531d921c7SDavid Howells 	const struct fs_parameter_enum *e;
8631d921c7SDavid Howells 	int ret = -ENOPARAM, b;
8731d921c7SDavid Howells 
8831d921c7SDavid Howells 	result->has_value = !!param->string;
8931d921c7SDavid Howells 	result->negated = false;
9031d921c7SDavid Howells 	result->uint_64 = 0;
9131d921c7SDavid Howells 
9231d921c7SDavid Howells 	p = fs_lookup_key(desc, param->key);
9331d921c7SDavid Howells 	if (!p) {
9431d921c7SDavid Howells 		/* If we didn't find something that looks like "noxxx", see if
9531d921c7SDavid Howells 		 * "xxx" takes the "no"-form negative - but only if there
9631d921c7SDavid Howells 		 * wasn't an value.
9731d921c7SDavid Howells 		 */
9831d921c7SDavid Howells 		if (result->has_value)
9931d921c7SDavid Howells 			goto unknown_parameter;
10031d921c7SDavid Howells 		if (param->key[0] != 'n' || param->key[1] != 'o' || !param->key[2])
10131d921c7SDavid Howells 			goto unknown_parameter;
10231d921c7SDavid Howells 
10331d921c7SDavid Howells 		p = fs_lookup_key(desc, param->key + 2);
10431d921c7SDavid Howells 		if (!p)
10531d921c7SDavid Howells 			goto unknown_parameter;
10631d921c7SDavid Howells 		if (!(p->flags & fs_param_neg_with_no))
10731d921c7SDavid Howells 			goto unknown_parameter;
10831d921c7SDavid Howells 		result->boolean = false;
10931d921c7SDavid Howells 		result->negated = true;
11031d921c7SDavid Howells 	}
11131d921c7SDavid Howells 
11231d921c7SDavid Howells 	if (p->flags & fs_param_deprecated)
11331d921c7SDavid Howells 		warnf(fc, "%s: Deprecated parameter '%s'",
11431d921c7SDavid Howells 		      desc->name, param->key);
11531d921c7SDavid Howells 
11631d921c7SDavid Howells 	if (result->negated)
11731d921c7SDavid Howells 		goto okay;
11831d921c7SDavid Howells 
11931d921c7SDavid Howells 	/* Certain parameter types only take a string and convert it. */
12031d921c7SDavid Howells 	switch (p->type) {
12131d921c7SDavid Howells 	case __fs_param_wasnt_defined:
12231d921c7SDavid Howells 		return -EINVAL;
12331d921c7SDavid Howells 	case fs_param_is_u32:
12431d921c7SDavid Howells 	case fs_param_is_u32_octal:
12531d921c7SDavid Howells 	case fs_param_is_u32_hex:
12631d921c7SDavid Howells 	case fs_param_is_s32:
12731d921c7SDavid Howells 	case fs_param_is_u64:
12831d921c7SDavid Howells 	case fs_param_is_enum:
12931d921c7SDavid Howells 	case fs_param_is_string:
13031d921c7SDavid Howells 		if (param->type != fs_value_is_string)
13131d921c7SDavid Howells 			goto bad_value;
13231d921c7SDavid Howells 		if (!result->has_value) {
13331d921c7SDavid Howells 			if (p->flags & fs_param_v_optional)
13431d921c7SDavid Howells 				goto okay;
13531d921c7SDavid Howells 			goto bad_value;
13631d921c7SDavid Howells 		}
13731d921c7SDavid Howells 		/* Fall through */
13831d921c7SDavid Howells 	default:
13931d921c7SDavid Howells 		break;
14031d921c7SDavid Howells 	}
14131d921c7SDavid Howells 
14231d921c7SDavid Howells 	/* Try to turn the type we were given into the type desired by the
14331d921c7SDavid Howells 	 * parameter and give an error if we can't.
14431d921c7SDavid Howells 	 */
14531d921c7SDavid Howells 	switch (p->type) {
14631d921c7SDavid Howells 	case fs_param_is_flag:
14731d921c7SDavid Howells 		if (param->type != fs_value_is_flag &&
14831d921c7SDavid Howells 		    (param->type != fs_value_is_string || result->has_value))
14931d921c7SDavid Howells 			return invalf(fc, "%s: Unexpected value for '%s'",
15031d921c7SDavid Howells 				      desc->name, param->key);
15131d921c7SDavid Howells 		result->boolean = true;
15231d921c7SDavid Howells 		goto okay;
15331d921c7SDavid Howells 
15431d921c7SDavid Howells 	case fs_param_is_bool:
15531d921c7SDavid Howells 		switch (param->type) {
15631d921c7SDavid Howells 		case fs_value_is_flag:
15731d921c7SDavid Howells 			result->boolean = true;
15831d921c7SDavid Howells 			goto okay;
15931d921c7SDavid Howells 		case fs_value_is_string:
16031d921c7SDavid Howells 			if (param->size == 0) {
16131d921c7SDavid Howells 				result->boolean = true;
16231d921c7SDavid Howells 				goto okay;
16331d921c7SDavid Howells 			}
16431d921c7SDavid Howells 			b = lookup_constant(bool_names, param->string, -1);
16531d921c7SDavid Howells 			if (b == -1)
16631d921c7SDavid Howells 				goto bad_value;
16731d921c7SDavid Howells 			result->boolean = b;
16831d921c7SDavid Howells 			goto okay;
16931d921c7SDavid Howells 		default:
17031d921c7SDavid Howells 			goto bad_value;
17131d921c7SDavid Howells 		}
17231d921c7SDavid Howells 
17331d921c7SDavid Howells 	case fs_param_is_u32:
17431d921c7SDavid Howells 		ret = kstrtouint(param->string, 0, &result->uint_32);
17531d921c7SDavid Howells 		goto maybe_okay;
17631d921c7SDavid Howells 	case fs_param_is_u32_octal:
17731d921c7SDavid Howells 		ret = kstrtouint(param->string, 8, &result->uint_32);
17831d921c7SDavid Howells 		goto maybe_okay;
17931d921c7SDavid Howells 	case fs_param_is_u32_hex:
18031d921c7SDavid Howells 		ret = kstrtouint(param->string, 16, &result->uint_32);
18131d921c7SDavid Howells 		goto maybe_okay;
18231d921c7SDavid Howells 	case fs_param_is_s32:
18331d921c7SDavid Howells 		ret = kstrtoint(param->string, 0, &result->int_32);
18431d921c7SDavid Howells 		goto maybe_okay;
18531d921c7SDavid Howells 	case fs_param_is_u64:
18631d921c7SDavid Howells 		ret = kstrtoull(param->string, 0, &result->uint_64);
18731d921c7SDavid Howells 		goto maybe_okay;
18831d921c7SDavid Howells 
18931d921c7SDavid Howells 	case fs_param_is_enum:
19031d921c7SDavid Howells 		for (e = desc->enums; e->name[0]; e++) {
19131d921c7SDavid Howells 			if (e->opt == p->opt &&
19231d921c7SDavid Howells 			    strcmp(e->name, param->string) == 0) {
19331d921c7SDavid Howells 				result->uint_32 = e->value;
19431d921c7SDavid Howells 				goto okay;
19531d921c7SDavid Howells 			}
19631d921c7SDavid Howells 		}
19731d921c7SDavid Howells 		goto bad_value;
19831d921c7SDavid Howells 
19931d921c7SDavid Howells 	case fs_param_is_string:
20031d921c7SDavid Howells 		goto okay;
20131d921c7SDavid Howells 	case fs_param_is_blob:
20231d921c7SDavid Howells 		if (param->type != fs_value_is_blob)
20331d921c7SDavid Howells 			goto bad_value;
20431d921c7SDavid Howells 		goto okay;
20531d921c7SDavid Howells 
20631d921c7SDavid Howells 	case fs_param_is_fd: {
20731d921c7SDavid Howells 		if (param->type != fs_value_is_file)
20831d921c7SDavid Howells 			goto bad_value;
20931d921c7SDavid Howells 		goto okay;
21031d921c7SDavid Howells 	}
21131d921c7SDavid Howells 
21231d921c7SDavid Howells 	case fs_param_is_blockdev:
21331d921c7SDavid Howells 	case fs_param_is_path:
21431d921c7SDavid Howells 		goto okay;
21531d921c7SDavid Howells 	default:
21631d921c7SDavid Howells 		BUG();
21731d921c7SDavid Howells 	}
21831d921c7SDavid Howells 
21931d921c7SDavid Howells maybe_okay:
22031d921c7SDavid Howells 	if (ret < 0)
22131d921c7SDavid Howells 		goto bad_value;
22231d921c7SDavid Howells okay:
22331d921c7SDavid Howells 	return p->opt;
22431d921c7SDavid Howells 
22531d921c7SDavid Howells bad_value:
22631d921c7SDavid Howells 	return invalf(fc, "%s: Bad value for '%s'", desc->name, param->key);
22731d921c7SDavid Howells unknown_parameter:
22831d921c7SDavid Howells 	return -ENOPARAM;
22931d921c7SDavid Howells }
23031d921c7SDavid Howells EXPORT_SYMBOL(fs_parse);
23131d921c7SDavid Howells 
23231d921c7SDavid Howells /**
23331d921c7SDavid Howells  * fs_lookup_param - Look up a path referred to by a parameter
23431d921c7SDavid Howells  * @fc: The filesystem context to log errors through.
23531d921c7SDavid Howells  * @param: The parameter.
23631d921c7SDavid Howells  * @want_bdev: T if want a blockdev
23731d921c7SDavid Howells  * @_path: The result of the lookup
23831d921c7SDavid Howells  */
23931d921c7SDavid Howells int fs_lookup_param(struct fs_context *fc,
24031d921c7SDavid Howells 		    struct fs_parameter *param,
24131d921c7SDavid Howells 		    bool want_bdev,
24231d921c7SDavid Howells 		    struct path *_path)
24331d921c7SDavid Howells {
24431d921c7SDavid Howells 	struct filename *f;
24531d921c7SDavid Howells 	unsigned int flags = 0;
24631d921c7SDavid Howells 	bool put_f;
24731d921c7SDavid Howells 	int ret;
24831d921c7SDavid Howells 
24931d921c7SDavid Howells 	switch (param->type) {
25031d921c7SDavid Howells 	case fs_value_is_string:
25131d921c7SDavid Howells 		f = getname_kernel(param->string);
25231d921c7SDavid Howells 		if (IS_ERR(f))
25331d921c7SDavid Howells 			return PTR_ERR(f);
25431d921c7SDavid Howells 		put_f = true;
25531d921c7SDavid Howells 		break;
25631d921c7SDavid Howells 	case fs_value_is_filename_empty:
25731d921c7SDavid Howells 		flags = LOOKUP_EMPTY;
25831d921c7SDavid Howells 		/* Fall through */
25931d921c7SDavid Howells 	case fs_value_is_filename:
26031d921c7SDavid Howells 		f = param->name;
26131d921c7SDavid Howells 		put_f = false;
26231d921c7SDavid Howells 		break;
26331d921c7SDavid Howells 	default:
26431d921c7SDavid Howells 		return invalf(fc, "%s: not usable as path", param->key);
26531d921c7SDavid Howells 	}
26631d921c7SDavid Howells 
26731d921c7SDavid Howells 	ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
26831d921c7SDavid Howells 	if (ret < 0) {
26931d921c7SDavid Howells 		errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
27031d921c7SDavid Howells 		goto out;
27131d921c7SDavid Howells 	}
27231d921c7SDavid Howells 
27331d921c7SDavid Howells 	if (want_bdev &&
27431d921c7SDavid Howells 	    !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) {
27531d921c7SDavid Howells 		path_put(_path);
27631d921c7SDavid Howells 		_path->dentry = NULL;
27731d921c7SDavid Howells 		_path->mnt = NULL;
27831d921c7SDavid Howells 		errorf(fc, "%s: Non-blockdev passed as '%s'",
27931d921c7SDavid Howells 		       param->key, f->name);
28031d921c7SDavid Howells 		ret = -ENOTBLK;
28131d921c7SDavid Howells 	}
28231d921c7SDavid Howells 
28331d921c7SDavid Howells out:
28431d921c7SDavid Howells 	if (put_f)
28531d921c7SDavid Howells 		putname(f);
28631d921c7SDavid Howells 	return ret;
28731d921c7SDavid Howells }
28831d921c7SDavid Howells EXPORT_SYMBOL(fs_lookup_param);
28931d921c7SDavid Howells 
29031d921c7SDavid Howells #ifdef CONFIG_VALIDATE_FS_PARSER
29131d921c7SDavid Howells /**
29231d921c7SDavid Howells  * validate_constant_table - Validate a constant table
29331d921c7SDavid Howells  * @name: Name to use in reporting
29431d921c7SDavid Howells  * @tbl: The constant table to validate.
29531d921c7SDavid Howells  * @tbl_size: The size of the table.
29631d921c7SDavid Howells  * @low: The lowest permissible value.
29731d921c7SDavid Howells  * @high: The highest permissible value.
29831d921c7SDavid Howells  * @special: One special permissible value outside of the range.
29931d921c7SDavid Howells  */
30031d921c7SDavid Howells bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
30131d921c7SDavid Howells 			     int low, int high, int special)
30231d921c7SDavid Howells {
30331d921c7SDavid Howells 	size_t i;
30431d921c7SDavid Howells 	bool good = true;
30531d921c7SDavid Howells 
30631d921c7SDavid Howells 	if (tbl_size == 0) {
30731d921c7SDavid Howells 		pr_warn("VALIDATE C-TBL: Empty\n");
30831d921c7SDavid Howells 		return true;
30931d921c7SDavid Howells 	}
31031d921c7SDavid Howells 
31131d921c7SDavid Howells 	for (i = 0; i < tbl_size; i++) {
31231d921c7SDavid Howells 		if (!tbl[i].name) {
31331d921c7SDavid Howells 			pr_err("VALIDATE C-TBL[%zu]: Null\n", i);
31431d921c7SDavid Howells 			good = false;
31531d921c7SDavid Howells 		} else if (i > 0 && tbl[i - 1].name) {
31631d921c7SDavid Howells 			int c = strcmp(tbl[i-1].name, tbl[i].name);
31731d921c7SDavid Howells 
31831d921c7SDavid Howells 			if (c == 0) {
31931d921c7SDavid Howells 				pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n",
32031d921c7SDavid Howells 				       i, tbl[i].name);
32131d921c7SDavid Howells 				good = false;
32231d921c7SDavid Howells 			}
32331d921c7SDavid Howells 			if (c > 0) {
32431d921c7SDavid Howells 				pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n",
32531d921c7SDavid Howells 				       i, tbl[i-1].name, tbl[i].name);
32631d921c7SDavid Howells 				good = false;
32731d921c7SDavid Howells 			}
32831d921c7SDavid Howells 		}
32931d921c7SDavid Howells 
33031d921c7SDavid Howells 		if (tbl[i].value != special &&
33131d921c7SDavid Howells 		    (tbl[i].value < low || tbl[i].value > high)) {
33231d921c7SDavid Howells 			pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n",
33331d921c7SDavid Howells 			       i, tbl[i].name, tbl[i].value, low, high);
33431d921c7SDavid Howells 			good = false;
33531d921c7SDavid Howells 		}
33631d921c7SDavid Howells 	}
33731d921c7SDavid Howells 
33831d921c7SDavid Howells 	return good;
33931d921c7SDavid Howells }
34031d921c7SDavid Howells 
34131d921c7SDavid Howells /**
34231d921c7SDavid Howells  * fs_validate_description - Validate a parameter description
34331d921c7SDavid Howells  * @desc: The parameter description to validate.
34431d921c7SDavid Howells  */
34531d921c7SDavid Howells bool fs_validate_description(const struct fs_parameter_description *desc)
34631d921c7SDavid Howells {
34731d921c7SDavid Howells 	const struct fs_parameter_spec *param, *p2;
34831d921c7SDavid Howells 	const struct fs_parameter_enum *e;
34931d921c7SDavid Howells 	const char *name = desc->name;
35031d921c7SDavid Howells 	unsigned int nr_params = 0;
35131d921c7SDavid Howells 	bool good = true, enums = false;
35231d921c7SDavid Howells 
35331d921c7SDavid Howells 	pr_notice("*** VALIDATE %s ***\n", name);
35431d921c7SDavid Howells 
35531d921c7SDavid Howells 	if (!name[0]) {
35631d921c7SDavid Howells 		pr_err("VALIDATE Parser: No name\n");
35731d921c7SDavid Howells 		name = "Unknown";
35831d921c7SDavid Howells 		good = false;
35931d921c7SDavid Howells 	}
36031d921c7SDavid Howells 
36131d921c7SDavid Howells 	if (desc->specs) {
36231d921c7SDavid Howells 		for (param = desc->specs; param->name; param++) {
36331d921c7SDavid Howells 			enum fs_parameter_type t = param->type;
36431d921c7SDavid Howells 
36531d921c7SDavid Howells 			/* Check that the type is in range */
36631d921c7SDavid Howells 			if (t == __fs_param_wasnt_defined ||
36731d921c7SDavid Howells 			    t >= nr__fs_parameter_type) {
36831d921c7SDavid Howells 				pr_err("VALIDATE %s: PARAM[%s] Bad type %u\n",
36931d921c7SDavid Howells 				       name, param->name, t);
37031d921c7SDavid Howells 				good = false;
37131d921c7SDavid Howells 			} else if (t == fs_param_is_enum) {
37231d921c7SDavid Howells 				enums = true;
37331d921c7SDavid Howells 			}
37431d921c7SDavid Howells 
37531d921c7SDavid Howells 			/* Check for duplicate parameter names */
37631d921c7SDavid Howells 			for (p2 = desc->specs; p2 < param; p2++) {
37731d921c7SDavid Howells 				if (strcmp(param->name, p2->name) == 0) {
37831d921c7SDavid Howells 					pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n",
37931d921c7SDavid Howells 					       name, param->name);
38031d921c7SDavid Howells 					good = false;
38131d921c7SDavid Howells 				}
38231d921c7SDavid Howells 			}
38331d921c7SDavid Howells 		}
38431d921c7SDavid Howells 
38531d921c7SDavid Howells 		nr_params = param - desc->specs;
38631d921c7SDavid Howells 	}
38731d921c7SDavid Howells 
38831d921c7SDavid Howells 	if (desc->enums) {
38931d921c7SDavid Howells 		if (!nr_params) {
39031d921c7SDavid Howells 			pr_err("VALIDATE %s: Enum table but no parameters\n",
39131d921c7SDavid Howells 			       name);
39231d921c7SDavid Howells 			good = false;
39331d921c7SDavid Howells 			goto no_enums;
39431d921c7SDavid Howells 		}
39531d921c7SDavid Howells 		if (!enums) {
39631d921c7SDavid Howells 			pr_err("VALIDATE %s: Enum table but no enum-type values\n",
39731d921c7SDavid Howells 			       name);
39831d921c7SDavid Howells 			good = false;
39931d921c7SDavid Howells 			goto no_enums;
40031d921c7SDavid Howells 		}
40131d921c7SDavid Howells 
40231d921c7SDavid Howells 		for (e = desc->enums; e->name[0]; e++) {
40331d921c7SDavid Howells 			/* Check that all entries in the enum table have at
40431d921c7SDavid Howells 			 * least one parameter that uses them.
40531d921c7SDavid Howells 			 */
40631d921c7SDavid Howells 			for (param = desc->specs; param->name; param++) {
40731d921c7SDavid Howells 				if (param->opt == e->opt &&
40831d921c7SDavid Howells 				    param->type != fs_param_is_enum) {
40926203278SRandy Dunlap 					pr_err("VALIDATE %s: e[%tu] enum val for %s\n",
41031d921c7SDavid Howells 					       name, e - desc->enums, param->name);
41131d921c7SDavid Howells 					good = false;
41231d921c7SDavid Howells 				}
41331d921c7SDavid Howells 			}
41431d921c7SDavid Howells 		}
41531d921c7SDavid Howells 
41631d921c7SDavid Howells 		/* Check that all enum-type parameters have at least one enum
41731d921c7SDavid Howells 		 * value in the enum table.
41831d921c7SDavid Howells 		 */
41931d921c7SDavid Howells 		for (param = desc->specs; param->name; param++) {
42031d921c7SDavid Howells 			if (param->type != fs_param_is_enum)
42131d921c7SDavid Howells 				continue;
42231d921c7SDavid Howells 			for (e = desc->enums; e->name[0]; e++)
42331d921c7SDavid Howells 				if (e->opt == param->opt)
42431d921c7SDavid Howells 					break;
42531d921c7SDavid Howells 			if (!e->name[0]) {
42631d921c7SDavid Howells 				pr_err("VALIDATE %s: PARAM[%s] enum with no values\n",
42731d921c7SDavid Howells 				       name, param->name);
42831d921c7SDavid Howells 				good = false;
42931d921c7SDavid Howells 			}
43031d921c7SDavid Howells 		}
43131d921c7SDavid Howells 	} else {
43231d921c7SDavid Howells 		if (enums) {
43331d921c7SDavid Howells 			pr_err("VALIDATE %s: enum-type values, but no enum table\n",
43431d921c7SDavid Howells 			       name);
43531d921c7SDavid Howells 			good = false;
43631d921c7SDavid Howells 			goto no_enums;
43731d921c7SDavid Howells 		}
43831d921c7SDavid Howells 	}
43931d921c7SDavid Howells 
44031d921c7SDavid Howells no_enums:
44131d921c7SDavid Howells 	return good;
44231d921c7SDavid Howells }
44331d921c7SDavid Howells #endif /* CONFIG_VALIDATE_FS_PARSER */
444