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 }, 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: { 207*74983ac2SDavid Howells switch (param->type) { 208*74983ac2SDavid Howells case fs_value_is_string: 209*74983ac2SDavid Howells if (!result->has_value) 21031d921c7SDavid Howells goto bad_value; 211*74983ac2SDavid Howells 212*74983ac2SDavid Howells ret = kstrtouint(param->string, 0, &result->uint_32); 213*74983ac2SDavid Howells break; 214*74983ac2SDavid Howells case fs_value_is_file: 215*74983ac2SDavid Howells result->uint_32 = param->dirfd; 216*74983ac2SDavid Howells ret = 0; 217*74983ac2SDavid Howells default: 218*74983ac2SDavid Howells goto bad_value; 219*74983ac2SDavid Howells } 220*74983ac2SDavid Howells 221*74983ac2SDavid Howells if (result->uint_32 > INT_MAX) 222*74983ac2SDavid Howells goto bad_value; 223*74983ac2SDavid Howells goto maybe_okay; 22431d921c7SDavid Howells } 22531d921c7SDavid Howells 22631d921c7SDavid Howells case fs_param_is_blockdev: 22731d921c7SDavid Howells case fs_param_is_path: 22831d921c7SDavid Howells goto okay; 22931d921c7SDavid Howells default: 23031d921c7SDavid Howells BUG(); 23131d921c7SDavid Howells } 23231d921c7SDavid Howells 23331d921c7SDavid Howells maybe_okay: 23431d921c7SDavid Howells if (ret < 0) 23531d921c7SDavid Howells goto bad_value; 23631d921c7SDavid Howells okay: 23731d921c7SDavid Howells return p->opt; 23831d921c7SDavid Howells 23931d921c7SDavid Howells bad_value: 24031d921c7SDavid Howells return invalf(fc, "%s: Bad value for '%s'", desc->name, param->key); 24131d921c7SDavid Howells unknown_parameter: 24231d921c7SDavid Howells return -ENOPARAM; 24331d921c7SDavid Howells } 24431d921c7SDavid Howells EXPORT_SYMBOL(fs_parse); 24531d921c7SDavid Howells 24631d921c7SDavid Howells /** 24731d921c7SDavid Howells * fs_lookup_param - Look up a path referred to by a parameter 24831d921c7SDavid Howells * @fc: The filesystem context to log errors through. 24931d921c7SDavid Howells * @param: The parameter. 25031d921c7SDavid Howells * @want_bdev: T if want a blockdev 25131d921c7SDavid Howells * @_path: The result of the lookup 25231d921c7SDavid Howells */ 25331d921c7SDavid Howells int fs_lookup_param(struct fs_context *fc, 25431d921c7SDavid Howells struct fs_parameter *param, 25531d921c7SDavid Howells bool want_bdev, 25631d921c7SDavid Howells struct path *_path) 25731d921c7SDavid Howells { 25831d921c7SDavid Howells struct filename *f; 25931d921c7SDavid Howells unsigned int flags = 0; 26031d921c7SDavid Howells bool put_f; 26131d921c7SDavid Howells int ret; 26231d921c7SDavid Howells 26331d921c7SDavid Howells switch (param->type) { 26431d921c7SDavid Howells case fs_value_is_string: 26531d921c7SDavid Howells f = getname_kernel(param->string); 26631d921c7SDavid Howells if (IS_ERR(f)) 26731d921c7SDavid Howells return PTR_ERR(f); 26831d921c7SDavid Howells put_f = true; 26931d921c7SDavid Howells break; 27031d921c7SDavid Howells case fs_value_is_filename_empty: 27131d921c7SDavid Howells flags = LOOKUP_EMPTY; 27231d921c7SDavid Howells /* Fall through */ 27331d921c7SDavid Howells case fs_value_is_filename: 27431d921c7SDavid Howells f = param->name; 27531d921c7SDavid Howells put_f = false; 27631d921c7SDavid Howells break; 27731d921c7SDavid Howells default: 27831d921c7SDavid Howells return invalf(fc, "%s: not usable as path", param->key); 27931d921c7SDavid Howells } 28031d921c7SDavid Howells 2817cdfa442SDavid Howells f->refcnt++; /* filename_lookup() drops our ref. */ 28231d921c7SDavid Howells ret = filename_lookup(param->dirfd, f, flags, _path, NULL); 28331d921c7SDavid Howells if (ret < 0) { 28431d921c7SDavid Howells errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name); 28531d921c7SDavid Howells goto out; 28631d921c7SDavid Howells } 28731d921c7SDavid Howells 28831d921c7SDavid Howells if (want_bdev && 28931d921c7SDavid Howells !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) { 29031d921c7SDavid Howells path_put(_path); 29131d921c7SDavid Howells _path->dentry = NULL; 29231d921c7SDavid Howells _path->mnt = NULL; 29331d921c7SDavid Howells errorf(fc, "%s: Non-blockdev passed as '%s'", 29431d921c7SDavid Howells param->key, f->name); 29531d921c7SDavid Howells ret = -ENOTBLK; 29631d921c7SDavid Howells } 29731d921c7SDavid Howells 29831d921c7SDavid Howells out: 29931d921c7SDavid Howells if (put_f) 30031d921c7SDavid Howells putname(f); 30131d921c7SDavid Howells return ret; 30231d921c7SDavid Howells } 30331d921c7SDavid Howells EXPORT_SYMBOL(fs_lookup_param); 30431d921c7SDavid Howells 30531d921c7SDavid Howells #ifdef CONFIG_VALIDATE_FS_PARSER 30631d921c7SDavid Howells /** 30731d921c7SDavid Howells * validate_constant_table - Validate a constant table 30831d921c7SDavid Howells * @name: Name to use in reporting 30931d921c7SDavid Howells * @tbl: The constant table to validate. 31031d921c7SDavid Howells * @tbl_size: The size of the table. 31131d921c7SDavid Howells * @low: The lowest permissible value. 31231d921c7SDavid Howells * @high: The highest permissible value. 31331d921c7SDavid Howells * @special: One special permissible value outside of the range. 31431d921c7SDavid Howells */ 31531d921c7SDavid Howells bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size, 31631d921c7SDavid Howells int low, int high, int special) 31731d921c7SDavid Howells { 31831d921c7SDavid Howells size_t i; 31931d921c7SDavid Howells bool good = true; 32031d921c7SDavid Howells 32131d921c7SDavid Howells if (tbl_size == 0) { 32231d921c7SDavid Howells pr_warn("VALIDATE C-TBL: Empty\n"); 32331d921c7SDavid Howells return true; 32431d921c7SDavid Howells } 32531d921c7SDavid Howells 32631d921c7SDavid Howells for (i = 0; i < tbl_size; i++) { 32731d921c7SDavid Howells if (!tbl[i].name) { 32831d921c7SDavid Howells pr_err("VALIDATE C-TBL[%zu]: Null\n", i); 32931d921c7SDavid Howells good = false; 33031d921c7SDavid Howells } else if (i > 0 && tbl[i - 1].name) { 33131d921c7SDavid Howells int c = strcmp(tbl[i-1].name, tbl[i].name); 33231d921c7SDavid Howells 33331d921c7SDavid Howells if (c == 0) { 33431d921c7SDavid Howells pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n", 33531d921c7SDavid Howells i, tbl[i].name); 33631d921c7SDavid Howells good = false; 33731d921c7SDavid Howells } 33831d921c7SDavid Howells if (c > 0) { 33931d921c7SDavid Howells pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n", 34031d921c7SDavid Howells i, tbl[i-1].name, tbl[i].name); 34131d921c7SDavid Howells good = false; 34231d921c7SDavid Howells } 34331d921c7SDavid Howells } 34431d921c7SDavid Howells 34531d921c7SDavid Howells if (tbl[i].value != special && 34631d921c7SDavid Howells (tbl[i].value < low || tbl[i].value > high)) { 34731d921c7SDavid Howells pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n", 34831d921c7SDavid Howells i, tbl[i].name, tbl[i].value, low, high); 34931d921c7SDavid Howells good = false; 35031d921c7SDavid Howells } 35131d921c7SDavid Howells } 35231d921c7SDavid Howells 35331d921c7SDavid Howells return good; 35431d921c7SDavid Howells } 35531d921c7SDavid Howells 35631d921c7SDavid Howells /** 35731d921c7SDavid Howells * fs_validate_description - Validate a parameter description 35831d921c7SDavid Howells * @desc: The parameter description to validate. 35931d921c7SDavid Howells */ 36031d921c7SDavid Howells bool fs_validate_description(const struct fs_parameter_description *desc) 36131d921c7SDavid Howells { 36231d921c7SDavid Howells const struct fs_parameter_spec *param, *p2; 36331d921c7SDavid Howells const struct fs_parameter_enum *e; 36431d921c7SDavid Howells const char *name = desc->name; 36531d921c7SDavid Howells unsigned int nr_params = 0; 36631d921c7SDavid Howells bool good = true, enums = false; 36731d921c7SDavid Howells 36831d921c7SDavid Howells pr_notice("*** VALIDATE %s ***\n", name); 36931d921c7SDavid Howells 37031d921c7SDavid Howells if (!name[0]) { 37131d921c7SDavid Howells pr_err("VALIDATE Parser: No name\n"); 37231d921c7SDavid Howells name = "Unknown"; 37331d921c7SDavid Howells good = false; 37431d921c7SDavid Howells } 37531d921c7SDavid Howells 37631d921c7SDavid Howells if (desc->specs) { 37731d921c7SDavid Howells for (param = desc->specs; param->name; param++) { 37831d921c7SDavid Howells enum fs_parameter_type t = param->type; 37931d921c7SDavid Howells 38031d921c7SDavid Howells /* Check that the type is in range */ 38131d921c7SDavid Howells if (t == __fs_param_wasnt_defined || 38231d921c7SDavid Howells t >= nr__fs_parameter_type) { 38331d921c7SDavid Howells pr_err("VALIDATE %s: PARAM[%s] Bad type %u\n", 38431d921c7SDavid Howells name, param->name, t); 38531d921c7SDavid Howells good = false; 38631d921c7SDavid Howells } else if (t == fs_param_is_enum) { 38731d921c7SDavid Howells enums = true; 38831d921c7SDavid Howells } 38931d921c7SDavid Howells 39031d921c7SDavid Howells /* Check for duplicate parameter names */ 39131d921c7SDavid Howells for (p2 = desc->specs; p2 < param; p2++) { 39231d921c7SDavid Howells if (strcmp(param->name, p2->name) == 0) { 39331d921c7SDavid Howells pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n", 39431d921c7SDavid Howells name, param->name); 39531d921c7SDavid Howells good = false; 39631d921c7SDavid Howells } 39731d921c7SDavid Howells } 39831d921c7SDavid Howells } 39931d921c7SDavid Howells 40031d921c7SDavid Howells nr_params = param - desc->specs; 40131d921c7SDavid Howells } 40231d921c7SDavid Howells 40331d921c7SDavid Howells if (desc->enums) { 40431d921c7SDavid Howells if (!nr_params) { 40531d921c7SDavid Howells pr_err("VALIDATE %s: Enum table but no parameters\n", 40631d921c7SDavid Howells name); 40731d921c7SDavid Howells good = false; 40831d921c7SDavid Howells goto no_enums; 40931d921c7SDavid Howells } 41031d921c7SDavid Howells if (!enums) { 41131d921c7SDavid Howells pr_err("VALIDATE %s: Enum table but no enum-type values\n", 41231d921c7SDavid Howells name); 41331d921c7SDavid Howells good = false; 41431d921c7SDavid Howells goto no_enums; 41531d921c7SDavid Howells } 41631d921c7SDavid Howells 41731d921c7SDavid Howells for (e = desc->enums; e->name[0]; e++) { 41831d921c7SDavid Howells /* Check that all entries in the enum table have at 41931d921c7SDavid Howells * least one parameter that uses them. 42031d921c7SDavid Howells */ 42131d921c7SDavid Howells for (param = desc->specs; param->name; param++) { 42231d921c7SDavid Howells if (param->opt == e->opt && 42331d921c7SDavid Howells param->type != fs_param_is_enum) { 42426203278SRandy Dunlap pr_err("VALIDATE %s: e[%tu] enum val for %s\n", 42531d921c7SDavid Howells name, e - desc->enums, param->name); 42631d921c7SDavid Howells good = false; 42731d921c7SDavid Howells } 42831d921c7SDavid Howells } 42931d921c7SDavid Howells } 43031d921c7SDavid Howells 43131d921c7SDavid Howells /* Check that all enum-type parameters have at least one enum 43231d921c7SDavid Howells * value in the enum table. 43331d921c7SDavid Howells */ 43431d921c7SDavid Howells for (param = desc->specs; param->name; param++) { 43531d921c7SDavid Howells if (param->type != fs_param_is_enum) 43631d921c7SDavid Howells continue; 43731d921c7SDavid Howells for (e = desc->enums; e->name[0]; e++) 43831d921c7SDavid Howells if (e->opt == param->opt) 43931d921c7SDavid Howells break; 44031d921c7SDavid Howells if (!e->name[0]) { 44131d921c7SDavid Howells pr_err("VALIDATE %s: PARAM[%s] enum with no values\n", 44231d921c7SDavid Howells name, param->name); 44331d921c7SDavid Howells good = false; 44431d921c7SDavid Howells } 44531d921c7SDavid Howells } 44631d921c7SDavid Howells } else { 44731d921c7SDavid Howells if (enums) { 44831d921c7SDavid Howells pr_err("VALIDATE %s: enum-type values, but no enum table\n", 44931d921c7SDavid Howells name); 45031d921c7SDavid Howells good = false; 45131d921c7SDavid Howells goto no_enums; 45231d921c7SDavid Howells } 45331d921c7SDavid Howells } 45431d921c7SDavid Howells 45531d921c7SDavid Howells no_enums: 45631d921c7SDavid Howells return good; 45731d921c7SDavid Howells } 45831d921c7SDavid Howells #endif /* CONFIG_VALIDATE_FS_PARSER */ 459