Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* Filesystem parameter parser.
0003  *
0004  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
0005  * Written by David Howells (dhowells@redhat.com)
0006  */
0007 
0008 #include <linux/export.h>
0009 #include <linux/fs_context.h>
0010 #include <linux/fs_parser.h>
0011 #include <linux/slab.h>
0012 #include <linux/security.h>
0013 #include <linux/namei.h>
0014 #include "internal.h"
0015 
0016 static const struct constant_table bool_names[] = {
0017     { "0",      false },
0018     { "1",      true },
0019     { "false",  false },
0020     { "no",     false },
0021     { "true",   true },
0022     { "yes",    true },
0023     { },
0024 };
0025 
0026 static const struct constant_table *
0027 __lookup_constant(const struct constant_table *tbl, const char *name)
0028 {
0029     for ( ; tbl->name; tbl++)
0030         if (strcmp(name, tbl->name) == 0)
0031             return tbl;
0032     return NULL;
0033 }
0034 
0035 /**
0036  * lookup_constant - Look up a constant by name in an ordered table
0037  * @tbl: The table of constants to search.
0038  * @name: The name to look up.
0039  * @not_found: The value to return if the name is not found.
0040  */
0041 int lookup_constant(const struct constant_table *tbl, const char *name, int not_found)
0042 {
0043     const struct constant_table *p = __lookup_constant(tbl, name);
0044 
0045     return p ? p->value : not_found;
0046 }
0047 EXPORT_SYMBOL(lookup_constant);
0048 
0049 static inline bool is_flag(const struct fs_parameter_spec *p)
0050 {
0051     return p->type == NULL;
0052 }
0053 
0054 static const struct fs_parameter_spec *fs_lookup_key(
0055     const struct fs_parameter_spec *desc,
0056     struct fs_parameter *param, bool *negated)
0057 {
0058     const struct fs_parameter_spec *p, *other = NULL;
0059     const char *name = param->key;
0060     bool want_flag = param->type == fs_value_is_flag;
0061 
0062     *negated = false;
0063     for (p = desc; p->name; p++) {
0064         if (strcmp(p->name, name) != 0)
0065             continue;
0066         if (likely(is_flag(p) == want_flag))
0067             return p;
0068         other = p;
0069     }
0070     if (want_flag) {
0071         if (name[0] == 'n' && name[1] == 'o' && name[2]) {
0072             for (p = desc; p->name; p++) {
0073                 if (strcmp(p->name, name + 2) != 0)
0074                     continue;
0075                 if (!(p->flags & fs_param_neg_with_no))
0076                     continue;
0077                 *negated = true;
0078                 return p;
0079             }
0080         }
0081     }
0082     return other;
0083 }
0084 
0085 /*
0086  * fs_parse - Parse a filesystem configuration parameter
0087  * @fc: The filesystem context to log errors through.
0088  * @desc: The parameter description to use.
0089  * @param: The parameter.
0090  * @result: Where to place the result of the parse
0091  *
0092  * Parse a filesystem configuration parameter and attempt a conversion for a
0093  * simple parameter for which this is requested.  If successful, the determined
0094  * parameter ID is placed into @result->key, the desired type is indicated in
0095  * @result->t and any converted value is placed into an appropriate member of
0096  * the union in @result.
0097  *
0098  * The function returns the parameter number if the parameter was matched,
0099  * -ENOPARAM if it wasn't matched and @desc->ignore_unknown indicated that
0100  * unknown parameters are okay and -EINVAL if there was a conversion issue or
0101  * the parameter wasn't recognised and unknowns aren't okay.
0102  */
0103 int __fs_parse(struct p_log *log,
0104          const struct fs_parameter_spec *desc,
0105          struct fs_parameter *param,
0106          struct fs_parse_result *result)
0107 {
0108     const struct fs_parameter_spec *p;
0109 
0110     result->uint_64 = 0;
0111 
0112     p = fs_lookup_key(desc, param, &result->negated);
0113     if (!p)
0114         return -ENOPARAM;
0115 
0116     if (p->flags & fs_param_deprecated)
0117         warn_plog(log, "Deprecated parameter '%s'", param->key);
0118 
0119     /* Try to turn the type we were given into the type desired by the
0120      * parameter and give an error if we can't.
0121      */
0122     if (is_flag(p)) {
0123         if (param->type != fs_value_is_flag)
0124             return inval_plog(log, "Unexpected value for '%s'",
0125                       param->key);
0126         result->boolean = !result->negated;
0127     } else  {
0128         int ret = p->type(log, p, param, result);
0129         if (ret)
0130             return ret;
0131     }
0132     return p->opt;
0133 }
0134 EXPORT_SYMBOL(__fs_parse);
0135 
0136 /**
0137  * fs_lookup_param - Look up a path referred to by a parameter
0138  * @fc: The filesystem context to log errors through.
0139  * @param: The parameter.
0140  * @want_bdev: T if want a blockdev
0141  * @_path: The result of the lookup
0142  */
0143 int fs_lookup_param(struct fs_context *fc,
0144             struct fs_parameter *param,
0145             bool want_bdev,
0146             struct path *_path)
0147 {
0148     struct filename *f;
0149     unsigned int flags = 0;
0150     bool put_f;
0151     int ret;
0152 
0153     switch (param->type) {
0154     case fs_value_is_string:
0155         f = getname_kernel(param->string);
0156         if (IS_ERR(f))
0157             return PTR_ERR(f);
0158         put_f = true;
0159         break;
0160     case fs_value_is_filename:
0161         f = param->name;
0162         put_f = false;
0163         break;
0164     default:
0165         return invalf(fc, "%s: not usable as path", param->key);
0166     }
0167 
0168     ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
0169     if (ret < 0) {
0170         errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
0171         goto out;
0172     }
0173 
0174     if (want_bdev &&
0175         !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) {
0176         path_put(_path);
0177         _path->dentry = NULL;
0178         _path->mnt = NULL;
0179         errorf(fc, "%s: Non-blockdev passed as '%s'",
0180                param->key, f->name);
0181         ret = -ENOTBLK;
0182     }
0183 
0184 out:
0185     if (put_f)
0186         putname(f);
0187     return ret;
0188 }
0189 EXPORT_SYMBOL(fs_lookup_param);
0190 
0191 static int fs_param_bad_value(struct p_log *log, struct fs_parameter *param)
0192 {
0193     return inval_plog(log, "Bad value for '%s'", param->key);
0194 }
0195 
0196 int fs_param_is_bool(struct p_log *log, const struct fs_parameter_spec *p,
0197              struct fs_parameter *param, struct fs_parse_result *result)
0198 {
0199     int b;
0200     if (param->type != fs_value_is_string)
0201         return fs_param_bad_value(log, param);
0202     if (!*param->string && (p->flags & fs_param_can_be_empty))
0203         return 0;
0204     b = lookup_constant(bool_names, param->string, -1);
0205     if (b == -1)
0206         return fs_param_bad_value(log, param);
0207     result->boolean = b;
0208     return 0;
0209 }
0210 EXPORT_SYMBOL(fs_param_is_bool);
0211 
0212 int fs_param_is_u32(struct p_log *log, const struct fs_parameter_spec *p,
0213             struct fs_parameter *param, struct fs_parse_result *result)
0214 {
0215     int base = (unsigned long)p->data;
0216     if (param->type != fs_value_is_string)
0217         return fs_param_bad_value(log, param);
0218     if (!*param->string && (p->flags & fs_param_can_be_empty))
0219         return 0;
0220     if (kstrtouint(param->string, base, &result->uint_32) < 0)
0221         return fs_param_bad_value(log, param);
0222     return 0;
0223 }
0224 EXPORT_SYMBOL(fs_param_is_u32);
0225 
0226 int fs_param_is_s32(struct p_log *log, const struct fs_parameter_spec *p,
0227             struct fs_parameter *param, struct fs_parse_result *result)
0228 {
0229     if (param->type != fs_value_is_string)
0230         return fs_param_bad_value(log, param);
0231     if (!*param->string && (p->flags & fs_param_can_be_empty))
0232         return 0;
0233     if (kstrtoint(param->string, 0, &result->int_32) < 0)
0234         return fs_param_bad_value(log, param);
0235     return 0;
0236 }
0237 EXPORT_SYMBOL(fs_param_is_s32);
0238 
0239 int fs_param_is_u64(struct p_log *log, const struct fs_parameter_spec *p,
0240             struct fs_parameter *param, struct fs_parse_result *result)
0241 {
0242     if (param->type != fs_value_is_string)
0243         return fs_param_bad_value(log, param);
0244     if (!*param->string && (p->flags & fs_param_can_be_empty))
0245         return 0;
0246     if (kstrtoull(param->string, 0, &result->uint_64) < 0)
0247         return fs_param_bad_value(log, param);
0248     return 0;
0249 }
0250 EXPORT_SYMBOL(fs_param_is_u64);
0251 
0252 int fs_param_is_enum(struct p_log *log, const struct fs_parameter_spec *p,
0253              struct fs_parameter *param, struct fs_parse_result *result)
0254 {
0255     const struct constant_table *c;
0256     if (param->type != fs_value_is_string)
0257         return fs_param_bad_value(log, param);
0258     if (!*param->string && (p->flags & fs_param_can_be_empty))
0259         return 0;
0260     c = __lookup_constant(p->data, param->string);
0261     if (!c)
0262         return fs_param_bad_value(log, param);
0263     result->uint_32 = c->value;
0264     return 0;
0265 }
0266 EXPORT_SYMBOL(fs_param_is_enum);
0267 
0268 int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p,
0269                struct fs_parameter *param, struct fs_parse_result *result)
0270 {
0271     if (param->type != fs_value_is_string ||
0272         (!*param->string && !(p->flags & fs_param_can_be_empty)))
0273         return fs_param_bad_value(log, param);
0274     return 0;
0275 }
0276 EXPORT_SYMBOL(fs_param_is_string);
0277 
0278 int fs_param_is_blob(struct p_log *log, const struct fs_parameter_spec *p,
0279              struct fs_parameter *param, struct fs_parse_result *result)
0280 {
0281     if (param->type != fs_value_is_blob)
0282         return fs_param_bad_value(log, param);
0283     return 0;
0284 }
0285 EXPORT_SYMBOL(fs_param_is_blob);
0286 
0287 int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p,
0288           struct fs_parameter *param, struct fs_parse_result *result)
0289 {
0290     switch (param->type) {
0291     case fs_value_is_string:
0292         if ((!*param->string && !(p->flags & fs_param_can_be_empty)) ||
0293             kstrtouint(param->string, 0, &result->uint_32) < 0)
0294             break;
0295         if (result->uint_32 <= INT_MAX)
0296             return 0;
0297         break;
0298     case fs_value_is_file:
0299         result->uint_32 = param->dirfd;
0300         if (result->uint_32 <= INT_MAX)
0301             return 0;
0302         break;
0303     default:
0304         break;
0305     }
0306     return fs_param_bad_value(log, param);
0307 }
0308 EXPORT_SYMBOL(fs_param_is_fd);
0309 
0310 int fs_param_is_blockdev(struct p_log *log, const struct fs_parameter_spec *p,
0311           struct fs_parameter *param, struct fs_parse_result *result)
0312 {
0313     return 0;
0314 }
0315 EXPORT_SYMBOL(fs_param_is_blockdev);
0316 
0317 int fs_param_is_path(struct p_log *log, const struct fs_parameter_spec *p,
0318              struct fs_parameter *param, struct fs_parse_result *result)
0319 {
0320     return 0;
0321 }
0322 EXPORT_SYMBOL(fs_param_is_path);
0323 
0324 #ifdef CONFIG_VALIDATE_FS_PARSER
0325 /**
0326  * validate_constant_table - Validate a constant table
0327  * @tbl: The constant table to validate.
0328  * @tbl_size: The size of the table.
0329  * @low: The lowest permissible value.
0330  * @high: The highest permissible value.
0331  * @special: One special permissible value outside of the range.
0332  */
0333 bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
0334                  int low, int high, int special)
0335 {
0336     size_t i;
0337     bool good = true;
0338 
0339     if (tbl_size == 0) {
0340         pr_warn("VALIDATE C-TBL: Empty\n");
0341         return true;
0342     }
0343 
0344     for (i = 0; i < tbl_size; i++) {
0345         if (!tbl[i].name) {
0346             pr_err("VALIDATE C-TBL[%zu]: Null\n", i);
0347             good = false;
0348         } else if (i > 0 && tbl[i - 1].name) {
0349             int c = strcmp(tbl[i-1].name, tbl[i].name);
0350 
0351             if (c == 0) {
0352                 pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n",
0353                        i, tbl[i].name);
0354                 good = false;
0355             }
0356             if (c > 0) {
0357                 pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n",
0358                        i, tbl[i-1].name, tbl[i].name);
0359                 good = false;
0360             }
0361         }
0362 
0363         if (tbl[i].value != special &&
0364             (tbl[i].value < low || tbl[i].value > high)) {
0365             pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n",
0366                    i, tbl[i].name, tbl[i].value, low, high);
0367             good = false;
0368         }
0369     }
0370 
0371     return good;
0372 }
0373 
0374 /**
0375  * fs_validate_description - Validate a parameter description
0376  * @name: The parameter name to search for.
0377  * @desc: The parameter description to validate.
0378  */
0379 bool fs_validate_description(const char *name,
0380     const struct fs_parameter_spec *desc)
0381 {
0382     const struct fs_parameter_spec *param, *p2;
0383     bool good = true;
0384 
0385     for (param = desc; param->name; param++) {
0386         /* Check for duplicate parameter names */
0387         for (p2 = desc; p2 < param; p2++) {
0388             if (strcmp(param->name, p2->name) == 0) {
0389                 if (is_flag(param) != is_flag(p2))
0390                     continue;
0391                 pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n",
0392                        name, param->name);
0393                 good = false;
0394             }
0395         }
0396     }
0397     return good;
0398 }
0399 #endif /* CONFIG_VALIDATE_FS_PARSER */