xref: /linux-6.15/fs/fs_parser.c (revision 34264ae3)
1b4d0d230SThomas 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 },
23*34264ae3SAl Viro 	{ },
2431d921c7SDavid Howells };
2531d921c7SDavid Howells 
26*34264ae3SAl Viro static const struct constant_table *
27*34264ae3SAl Viro __lookup_constant(const struct constant_table *tbl, const char *name)
28*34264ae3SAl Viro {
29*34264ae3SAl Viro 	for ( ; tbl->name; tbl++)
30*34264ae3SAl Viro 		if (strcmp(name, tbl->name) == 0)
31*34264ae3SAl Viro 			return tbl;
32*34264ae3SAl Viro 	return NULL;
33*34264ae3SAl Viro }
34*34264ae3SAl Viro 
3531d921c7SDavid Howells /**
3631d921c7SDavid Howells  * lookup_constant - Look up a constant by name in an ordered table
3731d921c7SDavid Howells  * @tbl: The table of constants to search.
3831d921c7SDavid Howells  * @name: The name to look up.
3931d921c7SDavid Howells  * @not_found: The value to return if the name is not found.
4031d921c7SDavid Howells  */
41*34264ae3SAl Viro int lookup_constant(const struct constant_table *tbl, const char *name, int not_found)
4231d921c7SDavid Howells {
43*34264ae3SAl Viro 	const struct constant_table *p = __lookup_constant(tbl, name);
4431d921c7SDavid Howells 
45*34264ae3SAl Viro 	return p ? p->value : not_found;
4631d921c7SDavid Howells }
47*34264ae3SAl Viro EXPORT_SYMBOL(lookup_constant);
4831d921c7SDavid Howells 
4931d921c7SDavid Howells static const struct fs_parameter_spec *fs_lookup_key(
5031d921c7SDavid Howells 	const struct fs_parameter_description *desc,
5131d921c7SDavid Howells 	const char *name)
5231d921c7SDavid Howells {
5331d921c7SDavid Howells 	const struct fs_parameter_spec *p;
5431d921c7SDavid Howells 
5531d921c7SDavid Howells 	if (!desc->specs)
5631d921c7SDavid Howells 		return NULL;
5731d921c7SDavid Howells 
5831d921c7SDavid Howells 	for (p = desc->specs; p->name; p++)
5931d921c7SDavid Howells 		if (strcmp(p->name, name) == 0)
6031d921c7SDavid Howells 			return p;
6131d921c7SDavid Howells 
6231d921c7SDavid Howells 	return NULL;
6331d921c7SDavid Howells }
6431d921c7SDavid Howells 
6531d921c7SDavid Howells /*
6631d921c7SDavid Howells  * fs_parse - Parse a filesystem configuration parameter
6731d921c7SDavid Howells  * @fc: The filesystem context to log errors through.
6831d921c7SDavid Howells  * @desc: The parameter description to use.
6931d921c7SDavid Howells  * @param: The parameter.
7031d921c7SDavid Howells  * @result: Where to place the result of the parse
7131d921c7SDavid Howells  *
7231d921c7SDavid Howells  * Parse a filesystem configuration parameter and attempt a conversion for a
7331d921c7SDavid Howells  * simple parameter for which this is requested.  If successful, the determined
7431d921c7SDavid Howells  * parameter ID is placed into @result->key, the desired type is indicated in
7531d921c7SDavid Howells  * @result->t and any converted value is placed into an appropriate member of
7631d921c7SDavid Howells  * the union in @result.
7731d921c7SDavid Howells  *
7831d921c7SDavid Howells  * The function returns the parameter number if the parameter was matched,
7931d921c7SDavid Howells  * -ENOPARAM if it wasn't matched and @desc->ignore_unknown indicated that
8031d921c7SDavid Howells  * unknown parameters are okay and -EINVAL if there was a conversion issue or
8131d921c7SDavid Howells  * the parameter wasn't recognised and unknowns aren't okay.
8231d921c7SDavid Howells  */
8331d921c7SDavid Howells int fs_parse(struct fs_context *fc,
8431d921c7SDavid Howells 	     const struct fs_parameter_description *desc,
8531d921c7SDavid Howells 	     struct fs_parameter *param,
8631d921c7SDavid Howells 	     struct fs_parse_result *result)
8731d921c7SDavid Howells {
8831d921c7SDavid Howells 	const struct fs_parameter_spec *p;
895eede625SAl Viro 	const struct constant_table *e;
9031d921c7SDavid Howells 	int ret = -ENOPARAM, b;
9131d921c7SDavid Howells 
9231d921c7SDavid Howells 	result->negated = false;
9331d921c7SDavid Howells 	result->uint_64 = 0;
9431d921c7SDavid Howells 
9531d921c7SDavid Howells 	p = fs_lookup_key(desc, param->key);
9631d921c7SDavid Howells 	if (!p) {
9731d921c7SDavid Howells 		/* If we didn't find something that looks like "noxxx", see if
9831d921c7SDavid Howells 		 * "xxx" takes the "no"-form negative - but only if there
9931d921c7SDavid Howells 		 * wasn't an value.
10031d921c7SDavid Howells 		 */
1010f89589aSAl Viro 		if (param->type != fs_value_is_flag)
10231d921c7SDavid Howells 			goto unknown_parameter;
10331d921c7SDavid Howells 		if (param->key[0] != 'n' || param->key[1] != 'o' || !param->key[2])
10431d921c7SDavid Howells 			goto unknown_parameter;
10531d921c7SDavid Howells 
10631d921c7SDavid Howells 		p = fs_lookup_key(desc, param->key + 2);
10731d921c7SDavid Howells 		if (!p)
10831d921c7SDavid Howells 			goto unknown_parameter;
10931d921c7SDavid Howells 		if (!(p->flags & fs_param_neg_with_no))
11031d921c7SDavid Howells 			goto unknown_parameter;
11131d921c7SDavid Howells 		result->boolean = false;
11231d921c7SDavid Howells 		result->negated = true;
11331d921c7SDavid Howells 	}
11431d921c7SDavid Howells 
11531d921c7SDavid Howells 	if (p->flags & fs_param_deprecated)
11631d921c7SDavid Howells 		warnf(fc, "%s: Deprecated parameter '%s'",
11731d921c7SDavid Howells 		      desc->name, param->key);
11831d921c7SDavid Howells 
11931d921c7SDavid Howells 	if (result->negated)
12031d921c7SDavid Howells 		goto okay;
12131d921c7SDavid Howells 
12231d921c7SDavid Howells 	/* Certain parameter types only take a string and convert it. */
12331d921c7SDavid Howells 	switch (p->type) {
12431d921c7SDavid Howells 	case __fs_param_wasnt_defined:
12531d921c7SDavid Howells 		return -EINVAL;
12631d921c7SDavid Howells 	case fs_param_is_u32:
12731d921c7SDavid Howells 	case fs_param_is_u32_octal:
12831d921c7SDavid Howells 	case fs_param_is_u32_hex:
12931d921c7SDavid Howells 	case fs_param_is_s32:
13031d921c7SDavid Howells 	case fs_param_is_u64:
13131d921c7SDavid Howells 	case fs_param_is_enum:
13231d921c7SDavid Howells 	case fs_param_is_string:
1330f89589aSAl Viro 		if (param->type == fs_value_is_string) {
1340f89589aSAl Viro 			if (p->flags & fs_param_v_optional)
1350f89589aSAl Viro 				break;
1360f89589aSAl Viro 			if (!*param->string)
13731d921c7SDavid Howells 				goto bad_value;
1380f89589aSAl Viro 			break;
1390f89589aSAl Viro 		}
1400f89589aSAl Viro 		if (param->type == fs_value_is_flag) {
14131d921c7SDavid Howells 			if (p->flags & fs_param_v_optional)
14231d921c7SDavid Howells 				goto okay;
14331d921c7SDavid Howells 		}
1440f89589aSAl Viro 		goto bad_value;
14531d921c7SDavid Howells 	default:
14631d921c7SDavid Howells 		break;
14731d921c7SDavid Howells 	}
14831d921c7SDavid Howells 
14931d921c7SDavid Howells 	/* Try to turn the type we were given into the type desired by the
15031d921c7SDavid Howells 	 * parameter and give an error if we can't.
15131d921c7SDavid Howells 	 */
15231d921c7SDavid Howells 	switch (p->type) {
15331d921c7SDavid Howells 	case fs_param_is_flag:
1540f89589aSAl Viro 		if (param->type != fs_value_is_flag)
15531d921c7SDavid Howells 			return invalf(fc, "%s: Unexpected value for '%s'",
15631d921c7SDavid Howells 				      desc->name, param->key);
15731d921c7SDavid Howells 		result->boolean = true;
15831d921c7SDavid Howells 		goto okay;
15931d921c7SDavid Howells 
16031d921c7SDavid Howells 	case fs_param_is_bool:
16131d921c7SDavid Howells 		switch (param->type) {
16231d921c7SDavid Howells 		case fs_value_is_flag:
16331d921c7SDavid Howells 			result->boolean = true;
16431d921c7SDavid Howells 			goto okay;
16531d921c7SDavid Howells 		case fs_value_is_string:
16631d921c7SDavid Howells 			if (param->size == 0) {
16731d921c7SDavid Howells 				result->boolean = true;
16831d921c7SDavid Howells 				goto okay;
16931d921c7SDavid Howells 			}
17031d921c7SDavid Howells 			b = lookup_constant(bool_names, param->string, -1);
17131d921c7SDavid Howells 			if (b == -1)
17231d921c7SDavid Howells 				goto bad_value;
17331d921c7SDavid Howells 			result->boolean = b;
17431d921c7SDavid Howells 			goto okay;
17531d921c7SDavid Howells 		default:
17631d921c7SDavid Howells 			goto bad_value;
17731d921c7SDavid Howells 		}
17831d921c7SDavid Howells 
17931d921c7SDavid Howells 	case fs_param_is_u32:
18031d921c7SDavid Howells 		ret = kstrtouint(param->string, 0, &result->uint_32);
18131d921c7SDavid Howells 		goto maybe_okay;
18231d921c7SDavid Howells 	case fs_param_is_u32_octal:
18331d921c7SDavid Howells 		ret = kstrtouint(param->string, 8, &result->uint_32);
18431d921c7SDavid Howells 		goto maybe_okay;
18531d921c7SDavid Howells 	case fs_param_is_u32_hex:
18631d921c7SDavid Howells 		ret = kstrtouint(param->string, 16, &result->uint_32);
18731d921c7SDavid Howells 		goto maybe_okay;
18831d921c7SDavid Howells 	case fs_param_is_s32:
18931d921c7SDavid Howells 		ret = kstrtoint(param->string, 0, &result->int_32);
19031d921c7SDavid Howells 		goto maybe_okay;
19131d921c7SDavid Howells 	case fs_param_is_u64:
19231d921c7SDavid Howells 		ret = kstrtoull(param->string, 0, &result->uint_64);
19331d921c7SDavid Howells 		goto maybe_okay;
19431d921c7SDavid Howells 
19531d921c7SDavid Howells 	case fs_param_is_enum:
196*34264ae3SAl Viro 		e = __lookup_constant(p->data, param->string);
197*34264ae3SAl Viro 		if (e) {
19831d921c7SDavid Howells 			result->uint_32 = e->value;
19931d921c7SDavid Howells 			goto okay;
20031d921c7SDavid Howells 		}
20131d921c7SDavid Howells 		goto bad_value;
20231d921c7SDavid Howells 
20331d921c7SDavid Howells 	case fs_param_is_string:
20431d921c7SDavid Howells 		goto okay;
20531d921c7SDavid Howells 	case fs_param_is_blob:
20631d921c7SDavid Howells 		if (param->type != fs_value_is_blob)
20731d921c7SDavid Howells 			goto bad_value;
20831d921c7SDavid Howells 		goto okay;
20931d921c7SDavid Howells 
21031d921c7SDavid Howells 	case fs_param_is_fd: {
21174983ac2SDavid Howells 		switch (param->type) {
21274983ac2SDavid Howells 		case fs_value_is_string:
21374983ac2SDavid Howells 			ret = kstrtouint(param->string, 0, &result->uint_32);
21474983ac2SDavid Howells 			break;
21574983ac2SDavid Howells 		case fs_value_is_file:
21674983ac2SDavid Howells 			result->uint_32 = param->dirfd;
21774983ac2SDavid Howells 			ret = 0;
21874983ac2SDavid Howells 		default:
21974983ac2SDavid Howells 			goto bad_value;
22074983ac2SDavid Howells 		}
22174983ac2SDavid Howells 
22274983ac2SDavid Howells 		if (result->uint_32 > INT_MAX)
22374983ac2SDavid Howells 			goto bad_value;
22474983ac2SDavid Howells 		goto maybe_okay;
22531d921c7SDavid Howells 	}
22631d921c7SDavid Howells 
22731d921c7SDavid Howells 	case fs_param_is_blockdev:
22831d921c7SDavid Howells 	case fs_param_is_path:
22931d921c7SDavid Howells 		goto okay;
23031d921c7SDavid Howells 	default:
23131d921c7SDavid Howells 		BUG();
23231d921c7SDavid Howells 	}
23331d921c7SDavid Howells 
23431d921c7SDavid Howells maybe_okay:
23531d921c7SDavid Howells 	if (ret < 0)
23631d921c7SDavid Howells 		goto bad_value;
23731d921c7SDavid Howells okay:
23831d921c7SDavid Howells 	return p->opt;
23931d921c7SDavid Howells 
24031d921c7SDavid Howells bad_value:
24131d921c7SDavid Howells 	return invalf(fc, "%s: Bad value for '%s'", desc->name, param->key);
24231d921c7SDavid Howells unknown_parameter:
24331d921c7SDavid Howells 	return -ENOPARAM;
24431d921c7SDavid Howells }
24531d921c7SDavid Howells EXPORT_SYMBOL(fs_parse);
24631d921c7SDavid Howells 
24731d921c7SDavid Howells /**
24831d921c7SDavid Howells  * fs_lookup_param - Look up a path referred to by a parameter
24931d921c7SDavid Howells  * @fc: The filesystem context to log errors through.
25031d921c7SDavid Howells  * @param: The parameter.
25131d921c7SDavid Howells  * @want_bdev: T if want a blockdev
25231d921c7SDavid Howells  * @_path: The result of the lookup
25331d921c7SDavid Howells  */
25431d921c7SDavid Howells int fs_lookup_param(struct fs_context *fc,
25531d921c7SDavid Howells 		    struct fs_parameter *param,
25631d921c7SDavid Howells 		    bool want_bdev,
25731d921c7SDavid Howells 		    struct path *_path)
25831d921c7SDavid Howells {
25931d921c7SDavid Howells 	struct filename *f;
26031d921c7SDavid Howells 	unsigned int flags = 0;
26131d921c7SDavid Howells 	bool put_f;
26231d921c7SDavid Howells 	int ret;
26331d921c7SDavid Howells 
26431d921c7SDavid Howells 	switch (param->type) {
26531d921c7SDavid Howells 	case fs_value_is_string:
26631d921c7SDavid Howells 		f = getname_kernel(param->string);
26731d921c7SDavid Howells 		if (IS_ERR(f))
26831d921c7SDavid Howells 			return PTR_ERR(f);
26931d921c7SDavid Howells 		put_f = true;
27031d921c7SDavid Howells 		break;
27131d921c7SDavid Howells 	case fs_value_is_filename_empty:
27231d921c7SDavid Howells 		flags = LOOKUP_EMPTY;
27331d921c7SDavid Howells 		/* Fall through */
27431d921c7SDavid Howells 	case fs_value_is_filename:
27531d921c7SDavid Howells 		f = param->name;
27631d921c7SDavid Howells 		put_f = false;
27731d921c7SDavid Howells 		break;
27831d921c7SDavid Howells 	default:
27931d921c7SDavid Howells 		return invalf(fc, "%s: not usable as path", param->key);
28031d921c7SDavid Howells 	}
28131d921c7SDavid Howells 
2827cdfa442SDavid Howells 	f->refcnt++; /* filename_lookup() drops our ref. */
28331d921c7SDavid Howells 	ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
28431d921c7SDavid Howells 	if (ret < 0) {
28531d921c7SDavid Howells 		errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
28631d921c7SDavid Howells 		goto out;
28731d921c7SDavid Howells 	}
28831d921c7SDavid Howells 
28931d921c7SDavid Howells 	if (want_bdev &&
29031d921c7SDavid Howells 	    !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) {
29131d921c7SDavid Howells 		path_put(_path);
29231d921c7SDavid Howells 		_path->dentry = NULL;
29331d921c7SDavid Howells 		_path->mnt = NULL;
29431d921c7SDavid Howells 		errorf(fc, "%s: Non-blockdev passed as '%s'",
29531d921c7SDavid Howells 		       param->key, f->name);
29631d921c7SDavid Howells 		ret = -ENOTBLK;
29731d921c7SDavid Howells 	}
29831d921c7SDavid Howells 
29931d921c7SDavid Howells out:
30031d921c7SDavid Howells 	if (put_f)
30131d921c7SDavid Howells 		putname(f);
30231d921c7SDavid Howells 	return ret;
30331d921c7SDavid Howells }
30431d921c7SDavid Howells EXPORT_SYMBOL(fs_lookup_param);
30531d921c7SDavid Howells 
30631d921c7SDavid Howells #ifdef CONFIG_VALIDATE_FS_PARSER
30731d921c7SDavid Howells /**
30831d921c7SDavid Howells  * validate_constant_table - Validate a constant table
30931d921c7SDavid Howells  * @name: Name to use in reporting
31031d921c7SDavid Howells  * @tbl: The constant table to validate.
31131d921c7SDavid Howells  * @tbl_size: The size of the table.
31231d921c7SDavid Howells  * @low: The lowest permissible value.
31331d921c7SDavid Howells  * @high: The highest permissible value.
31431d921c7SDavid Howells  * @special: One special permissible value outside of the range.
31531d921c7SDavid Howells  */
31631d921c7SDavid Howells bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
31731d921c7SDavid Howells 			     int low, int high, int special)
31831d921c7SDavid Howells {
31931d921c7SDavid Howells 	size_t i;
32031d921c7SDavid Howells 	bool good = true;
32131d921c7SDavid Howells 
32231d921c7SDavid Howells 	if (tbl_size == 0) {
32331d921c7SDavid Howells 		pr_warn("VALIDATE C-TBL: Empty\n");
32431d921c7SDavid Howells 		return true;
32531d921c7SDavid Howells 	}
32631d921c7SDavid Howells 
32731d921c7SDavid Howells 	for (i = 0; i < tbl_size; i++) {
32831d921c7SDavid Howells 		if (!tbl[i].name) {
32931d921c7SDavid Howells 			pr_err("VALIDATE C-TBL[%zu]: Null\n", i);
33031d921c7SDavid Howells 			good = false;
33131d921c7SDavid Howells 		} else if (i > 0 && tbl[i - 1].name) {
33231d921c7SDavid Howells 			int c = strcmp(tbl[i-1].name, tbl[i].name);
33331d921c7SDavid Howells 
33431d921c7SDavid Howells 			if (c == 0) {
33531d921c7SDavid Howells 				pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n",
33631d921c7SDavid Howells 				       i, tbl[i].name);
33731d921c7SDavid Howells 				good = false;
33831d921c7SDavid Howells 			}
33931d921c7SDavid Howells 			if (c > 0) {
34031d921c7SDavid Howells 				pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n",
34131d921c7SDavid Howells 				       i, tbl[i-1].name, tbl[i].name);
34231d921c7SDavid Howells 				good = false;
34331d921c7SDavid Howells 			}
34431d921c7SDavid Howells 		}
34531d921c7SDavid Howells 
34631d921c7SDavid Howells 		if (tbl[i].value != special &&
34731d921c7SDavid Howells 		    (tbl[i].value < low || tbl[i].value > high)) {
34831d921c7SDavid Howells 			pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n",
34931d921c7SDavid Howells 			       i, tbl[i].name, tbl[i].value, low, high);
35031d921c7SDavid Howells 			good = false;
35131d921c7SDavid Howells 		}
35231d921c7SDavid Howells 	}
35331d921c7SDavid Howells 
35431d921c7SDavid Howells 	return good;
35531d921c7SDavid Howells }
35631d921c7SDavid Howells 
35731d921c7SDavid Howells /**
35831d921c7SDavid Howells  * fs_validate_description - Validate a parameter description
35931d921c7SDavid Howells  * @desc: The parameter description to validate.
36031d921c7SDavid Howells  */
36131d921c7SDavid Howells bool fs_validate_description(const struct fs_parameter_description *desc)
36231d921c7SDavid Howells {
36331d921c7SDavid Howells 	const struct fs_parameter_spec *param, *p2;
36431d921c7SDavid Howells 	const char *name = desc->name;
3652710c957SAl Viro 	bool good = true;
36631d921c7SDavid Howells 
36731d921c7SDavid Howells 	pr_notice("*** VALIDATE %s ***\n", name);
36831d921c7SDavid Howells 
36931d921c7SDavid Howells 	if (!name[0]) {
37031d921c7SDavid Howells 		pr_err("VALIDATE Parser: No name\n");
37131d921c7SDavid Howells 		name = "Unknown";
37231d921c7SDavid Howells 		good = false;
37331d921c7SDavid Howells 	}
37431d921c7SDavid Howells 
37531d921c7SDavid Howells 	if (desc->specs) {
37631d921c7SDavid Howells 		for (param = desc->specs; param->name; param++) {
37731d921c7SDavid Howells 			enum fs_parameter_type t = param->type;
37831d921c7SDavid Howells 
37931d921c7SDavid Howells 			/* Check that the type is in range */
38031d921c7SDavid Howells 			if (t == __fs_param_wasnt_defined ||
38131d921c7SDavid Howells 			    t >= nr__fs_parameter_type) {
38231d921c7SDavid Howells 				pr_err("VALIDATE %s: PARAM[%s] Bad type %u\n",
38331d921c7SDavid Howells 				       name, param->name, t);
38431d921c7SDavid Howells 				good = false;
38531d921c7SDavid Howells 			} else if (t == fs_param_is_enum) {
3865eede625SAl Viro 				const struct constant_table *e = param->data;
3872710c957SAl Viro 				if (!e || !e->name) {
3882710c957SAl Viro 					pr_err("VALIDATE %s: PARAM[%s] enum with no values\n",
3892710c957SAl Viro 					       name, param->name);
3902710c957SAl Viro 					good = false;
3912710c957SAl Viro 				}
39231d921c7SDavid Howells 			}
39331d921c7SDavid Howells 
39431d921c7SDavid Howells 			/* Check for duplicate parameter names */
39531d921c7SDavid Howells 			for (p2 = desc->specs; p2 < param; p2++) {
39631d921c7SDavid Howells 				if (strcmp(param->name, p2->name) == 0) {
39731d921c7SDavid Howells 					pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n",
39831d921c7SDavid Howells 					       name, param->name);
39931d921c7SDavid Howells 					good = false;
40031d921c7SDavid Howells 				}
40131d921c7SDavid Howells 			}
40231d921c7SDavid Howells 		}
40331d921c7SDavid Howells 	}
40431d921c7SDavid Howells 	return good;
40531d921c7SDavid Howells }
40631d921c7SDavid Howells #endif /* CONFIG_VALIDATE_FS_PARSER */
407