root/fs/fs_parser.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __lookup_constant
  2. fs_lookup_key
  3. fs_parse
  4. fs_lookup_param
  5. validate_constant_table
  6. fs_validate_description

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /* Filesystem parameter parser.
   3  *
   4  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
   5  * Written by David Howells (dhowells@redhat.com)
   6  */
   7 
   8 #include <linux/export.h>
   9 #include <linux/fs_context.h>
  10 #include <linux/fs_parser.h>
  11 #include <linux/slab.h>
  12 #include <linux/security.h>
  13 #include <linux/namei.h>
  14 #include "internal.h"
  15 
  16 static const struct constant_table bool_names[] = {
  17         { "0",          false },
  18         { "1",          true },
  19         { "false",      false },
  20         { "no",         false },
  21         { "true",       true },
  22         { "yes",        true },
  23 };
  24 
  25 /**
  26  * lookup_constant - Look up a constant by name in an ordered table
  27  * @tbl: The table of constants to search.
  28  * @tbl_size: The size of the table.
  29  * @name: The name to look up.
  30  * @not_found: The value to return if the name is not found.
  31  */
  32 int __lookup_constant(const struct constant_table *tbl, size_t tbl_size,
  33                       const char *name, int not_found)
  34 {
  35         unsigned int i;
  36 
  37         for (i = 0; i < tbl_size; i++)
  38                 if (strcmp(name, tbl[i].name) == 0)
  39                         return tbl[i].value;
  40 
  41         return not_found;
  42 }
  43 EXPORT_SYMBOL(__lookup_constant);
  44 
  45 static const struct fs_parameter_spec *fs_lookup_key(
  46         const struct fs_parameter_description *desc,
  47         const char *name)
  48 {
  49         const struct fs_parameter_spec *p;
  50 
  51         if (!desc->specs)
  52                 return NULL;
  53 
  54         for (p = desc->specs; p->name; p++)
  55                 if (strcmp(p->name, name) == 0)
  56                         return p;
  57 
  58         return NULL;
  59 }
  60 
  61 /*
  62  * fs_parse - Parse a filesystem configuration parameter
  63  * @fc: The filesystem context to log errors through.
  64  * @desc: The parameter description to use.
  65  * @param: The parameter.
  66  * @result: Where to place the result of the parse
  67  *
  68  * Parse a filesystem configuration parameter and attempt a conversion for a
  69  * simple parameter for which this is requested.  If successful, the determined
  70  * parameter ID is placed into @result->key, the desired type is indicated in
  71  * @result->t and any converted value is placed into an appropriate member of
  72  * the union in @result.
  73  *
  74  * The function returns the parameter number if the parameter was matched,
  75  * -ENOPARAM if it wasn't matched and @desc->ignore_unknown indicated that
  76  * unknown parameters are okay and -EINVAL if there was a conversion issue or
  77  * the parameter wasn't recognised and unknowns aren't okay.
  78  */
  79 int fs_parse(struct fs_context *fc,
  80              const struct fs_parameter_description *desc,
  81              struct fs_parameter *param,
  82              struct fs_parse_result *result)
  83 {
  84         const struct fs_parameter_spec *p;
  85         const struct fs_parameter_enum *e;
  86         int ret = -ENOPARAM, b;
  87 
  88         result->has_value = !!param->string;
  89         result->negated = false;
  90         result->uint_64 = 0;
  91 
  92         p = fs_lookup_key(desc, param->key);
  93         if (!p) {
  94                 /* If we didn't find something that looks like "noxxx", see if
  95                  * "xxx" takes the "no"-form negative - but only if there
  96                  * wasn't an value.
  97                  */
  98                 if (result->has_value)
  99                         goto unknown_parameter;
 100                 if (param->key[0] != 'n' || param->key[1] != 'o' || !param->key[2])
 101                         goto unknown_parameter;
 102 
 103                 p = fs_lookup_key(desc, param->key + 2);
 104                 if (!p)
 105                         goto unknown_parameter;
 106                 if (!(p->flags & fs_param_neg_with_no))
 107                         goto unknown_parameter;
 108                 result->boolean = false;
 109                 result->negated = true;
 110         }
 111 
 112         if (p->flags & fs_param_deprecated)
 113                 warnf(fc, "%s: Deprecated parameter '%s'",
 114                       desc->name, param->key);
 115 
 116         if (result->negated)
 117                 goto okay;
 118 
 119         /* Certain parameter types only take a string and convert it. */
 120         switch (p->type) {
 121         case __fs_param_wasnt_defined:
 122                 return -EINVAL;
 123         case fs_param_is_u32:
 124         case fs_param_is_u32_octal:
 125         case fs_param_is_u32_hex:
 126         case fs_param_is_s32:
 127         case fs_param_is_u64:
 128         case fs_param_is_enum:
 129         case fs_param_is_string:
 130                 if (param->type != fs_value_is_string)
 131                         goto bad_value;
 132                 if (!result->has_value) {
 133                         if (p->flags & fs_param_v_optional)
 134                                 goto okay;
 135                         goto bad_value;
 136                 }
 137                 /* Fall through */
 138         default:
 139                 break;
 140         }
 141 
 142         /* Try to turn the type we were given into the type desired by the
 143          * parameter and give an error if we can't.
 144          */
 145         switch (p->type) {
 146         case fs_param_is_flag:
 147                 if (param->type != fs_value_is_flag &&
 148                     (param->type != fs_value_is_string || result->has_value))
 149                         return invalf(fc, "%s: Unexpected value for '%s'",
 150                                       desc->name, param->key);
 151                 result->boolean = true;
 152                 goto okay;
 153 
 154         case fs_param_is_bool:
 155                 switch (param->type) {
 156                 case fs_value_is_flag:
 157                         result->boolean = true;
 158                         goto okay;
 159                 case fs_value_is_string:
 160                         if (param->size == 0) {
 161                                 result->boolean = true;
 162                                 goto okay;
 163                         }
 164                         b = lookup_constant(bool_names, param->string, -1);
 165                         if (b == -1)
 166                                 goto bad_value;
 167                         result->boolean = b;
 168                         goto okay;
 169                 default:
 170                         goto bad_value;
 171                 }
 172 
 173         case fs_param_is_u32:
 174                 ret = kstrtouint(param->string, 0, &result->uint_32);
 175                 goto maybe_okay;
 176         case fs_param_is_u32_octal:
 177                 ret = kstrtouint(param->string, 8, &result->uint_32);
 178                 goto maybe_okay;
 179         case fs_param_is_u32_hex:
 180                 ret = kstrtouint(param->string, 16, &result->uint_32);
 181                 goto maybe_okay;
 182         case fs_param_is_s32:
 183                 ret = kstrtoint(param->string, 0, &result->int_32);
 184                 goto maybe_okay;
 185         case fs_param_is_u64:
 186                 ret = kstrtoull(param->string, 0, &result->uint_64);
 187                 goto maybe_okay;
 188 
 189         case fs_param_is_enum:
 190                 for (e = desc->enums; e->name[0]; e++) {
 191                         if (e->opt == p->opt &&
 192                             strcmp(e->name, param->string) == 0) {
 193                                 result->uint_32 = e->value;
 194                                 goto okay;
 195                         }
 196                 }
 197                 goto bad_value;
 198 
 199         case fs_param_is_string:
 200                 goto okay;
 201         case fs_param_is_blob:
 202                 if (param->type != fs_value_is_blob)
 203                         goto bad_value;
 204                 goto okay;
 205 
 206         case fs_param_is_fd: {
 207                 switch (param->type) {
 208                 case fs_value_is_string:
 209                         if (!result->has_value)
 210                                 goto bad_value;
 211 
 212                         ret = kstrtouint(param->string, 0, &result->uint_32);
 213                         break;
 214                 case fs_value_is_file:
 215                         result->uint_32 = param->dirfd;
 216                         ret = 0;
 217                 default:
 218                         goto bad_value;
 219                 }
 220 
 221                 if (result->uint_32 > INT_MAX)
 222                         goto bad_value;
 223                 goto maybe_okay;
 224         }
 225 
 226         case fs_param_is_blockdev:
 227         case fs_param_is_path:
 228                 goto okay;
 229         default:
 230                 BUG();
 231         }
 232 
 233 maybe_okay:
 234         if (ret < 0)
 235                 goto bad_value;
 236 okay:
 237         return p->opt;
 238 
 239 bad_value:
 240         return invalf(fc, "%s: Bad value for '%s'", desc->name, param->key);
 241 unknown_parameter:
 242         return -ENOPARAM;
 243 }
 244 EXPORT_SYMBOL(fs_parse);
 245 
 246 /**
 247  * fs_lookup_param - Look up a path referred to by a parameter
 248  * @fc: The filesystem context to log errors through.
 249  * @param: The parameter.
 250  * @want_bdev: T if want a blockdev
 251  * @_path: The result of the lookup
 252  */
 253 int fs_lookup_param(struct fs_context *fc,
 254                     struct fs_parameter *param,
 255                     bool want_bdev,
 256                     struct path *_path)
 257 {
 258         struct filename *f;
 259         unsigned int flags = 0;
 260         bool put_f;
 261         int ret;
 262 
 263         switch (param->type) {
 264         case fs_value_is_string:
 265                 f = getname_kernel(param->string);
 266                 if (IS_ERR(f))
 267                         return PTR_ERR(f);
 268                 put_f = true;
 269                 break;
 270         case fs_value_is_filename_empty:
 271                 flags = LOOKUP_EMPTY;
 272                 /* Fall through */
 273         case fs_value_is_filename:
 274                 f = param->name;
 275                 put_f = false;
 276                 break;
 277         default:
 278                 return invalf(fc, "%s: not usable as path", param->key);
 279         }
 280 
 281         f->refcnt++; /* filename_lookup() drops our ref. */
 282         ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
 283         if (ret < 0) {
 284                 errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
 285                 goto out;
 286         }
 287 
 288         if (want_bdev &&
 289             !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) {
 290                 path_put(_path);
 291                 _path->dentry = NULL;
 292                 _path->mnt = NULL;
 293                 errorf(fc, "%s: Non-blockdev passed as '%s'",
 294                        param->key, f->name);
 295                 ret = -ENOTBLK;
 296         }
 297 
 298 out:
 299         if (put_f)
 300                 putname(f);
 301         return ret;
 302 }
 303 EXPORT_SYMBOL(fs_lookup_param);
 304 
 305 #ifdef CONFIG_VALIDATE_FS_PARSER
 306 /**
 307  * validate_constant_table - Validate a constant table
 308  * @name: Name to use in reporting
 309  * @tbl: The constant table to validate.
 310  * @tbl_size: The size of the table.
 311  * @low: The lowest permissible value.
 312  * @high: The highest permissible value.
 313  * @special: One special permissible value outside of the range.
 314  */
 315 bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
 316                              int low, int high, int special)
 317 {
 318         size_t i;
 319         bool good = true;
 320 
 321         if (tbl_size == 0) {
 322                 pr_warn("VALIDATE C-TBL: Empty\n");
 323                 return true;
 324         }
 325 
 326         for (i = 0; i < tbl_size; i++) {
 327                 if (!tbl[i].name) {
 328                         pr_err("VALIDATE C-TBL[%zu]: Null\n", i);
 329                         good = false;
 330                 } else if (i > 0 && tbl[i - 1].name) {
 331                         int c = strcmp(tbl[i-1].name, tbl[i].name);
 332 
 333                         if (c == 0) {
 334                                 pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n",
 335                                        i, tbl[i].name);
 336                                 good = false;
 337                         }
 338                         if (c > 0) {
 339                                 pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n",
 340                                        i, tbl[i-1].name, tbl[i].name);
 341                                 good = false;
 342                         }
 343                 }
 344 
 345                 if (tbl[i].value != special &&
 346                     (tbl[i].value < low || tbl[i].value > high)) {
 347                         pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n",
 348                                i, tbl[i].name, tbl[i].value, low, high);
 349                         good = false;
 350                 }
 351         }
 352 
 353         return good;
 354 }
 355 
 356 /**
 357  * fs_validate_description - Validate a parameter description
 358  * @desc: The parameter description to validate.
 359  */
 360 bool fs_validate_description(const struct fs_parameter_description *desc)
 361 {
 362         const struct fs_parameter_spec *param, *p2;
 363         const struct fs_parameter_enum *e;
 364         const char *name = desc->name;
 365         unsigned int nr_params = 0;
 366         bool good = true, enums = false;
 367 
 368         pr_notice("*** VALIDATE %s ***\n", name);
 369 
 370         if (!name[0]) {
 371                 pr_err("VALIDATE Parser: No name\n");
 372                 name = "Unknown";
 373                 good = false;
 374         }
 375 
 376         if (desc->specs) {
 377                 for (param = desc->specs; param->name; param++) {
 378                         enum fs_parameter_type t = param->type;
 379 
 380                         /* Check that the type is in range */
 381                         if (t == __fs_param_wasnt_defined ||
 382                             t >= nr__fs_parameter_type) {
 383                                 pr_err("VALIDATE %s: PARAM[%s] Bad type %u\n",
 384                                        name, param->name, t);
 385                                 good = false;
 386                         } else if (t == fs_param_is_enum) {
 387                                 enums = true;
 388                         }
 389 
 390                         /* Check for duplicate parameter names */
 391                         for (p2 = desc->specs; p2 < param; p2++) {
 392                                 if (strcmp(param->name, p2->name) == 0) {
 393                                         pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n",
 394                                                name, param->name);
 395                                         good = false;
 396                                 }
 397                         }
 398                 }
 399 
 400                 nr_params = param - desc->specs;
 401         }
 402 
 403         if (desc->enums) {
 404                 if (!nr_params) {
 405                         pr_err("VALIDATE %s: Enum table but no parameters\n",
 406                                name);
 407                         good = false;
 408                         goto no_enums;
 409                 }
 410                 if (!enums) {
 411                         pr_err("VALIDATE %s: Enum table but no enum-type values\n",
 412                                name);
 413                         good = false;
 414                         goto no_enums;
 415                 }
 416 
 417                 for (e = desc->enums; e->name[0]; e++) {
 418                         /* Check that all entries in the enum table have at
 419                          * least one parameter that uses them.
 420                          */
 421                         for (param = desc->specs; param->name; param++) {
 422                                 if (param->opt == e->opt &&
 423                                     param->type != fs_param_is_enum) {
 424                                         pr_err("VALIDATE %s: e[%tu] enum val for %s\n",
 425                                                name, e - desc->enums, param->name);
 426                                         good = false;
 427                                 }
 428                         }
 429                 }
 430 
 431                 /* Check that all enum-type parameters have at least one enum
 432                  * value in the enum table.
 433                  */
 434                 for (param = desc->specs; param->name; param++) {
 435                         if (param->type != fs_param_is_enum)
 436                                 continue;
 437                         for (e = desc->enums; e->name[0]; e++)
 438                                 if (e->opt == param->opt)
 439                                         break;
 440                         if (!e->name[0]) {
 441                                 pr_err("VALIDATE %s: PARAM[%s] enum with no values\n",
 442                                        name, param->name);
 443                                 good = false;
 444                         }
 445                 }
 446         } else {
 447                 if (enums) {
 448                         pr_err("VALIDATE %s: enum-type values, but no enum table\n",
 449                                name);
 450                         good = false;
 451                         goto no_enums;
 452                 }
 453         }
 454 
 455 no_enums:
 456         return good;
 457 }
 458 #endif /* CONFIG_VALIDATE_FS_PARSER */

/* [<][>][^][v][top][bottom][index][help] */