Branch data Line data Source code
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 : 33539 : 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 [ + + ]: 217951 : for (i = 0; i < tbl_size; i++)
38 [ - + ]: 184270 : if (strcmp(name, tbl[i].name) == 0)
39 : 0 : return tbl[i].value;
40 : :
41 : : return not_found;
42 : : }
43 : : EXPORT_SYMBOL(__lookup_constant);
44 : :
45 : 11389 : 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 [ + - ]: 11389 : if (!desc->specs)
52 : : return NULL;
53 : :
54 [ + + ]: 60464 : for (p = desc->specs; p->name; p++)
55 [ + + ]: 65227 : if (strcmp(p->name, name) == 0)
56 : 4763 : 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 : 11389 : 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 : 11389 : result->has_value = !!param->string;
89 : 11389 : result->negated = false;
90 : 11389 : result->uint_64 = 0;
91 : :
92 : 11389 : p = fs_lookup_key(desc, param->key);
93 [ + + ]: 11389 : 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 [ + + ]: 6626 : if (result->has_value)
99 : : goto unknown_parameter;
100 [ + + - + : 2070 : if (param->key[0] != 'n' || param->key[1] != 'o' || !param->key[2])
# # ]
101 : : goto unknown_parameter;
102 : :
103 : 0 : p = fs_lookup_key(desc, param->key + 2);
104 [ # # ]: 0 : if (!p)
105 : : goto unknown_parameter;
106 [ # # ]: 0 : if (!(p->flags & fs_param_neg_with_no))
107 : : goto unknown_parameter;
108 : 0 : result->boolean = false;
109 : 0 : result->negated = true;
110 : : }
111 : :
112 [ - + ]: 4763 : if (p->flags & fs_param_deprecated)
113 : 0 : warnf(fc, "%s: Deprecated parameter '%s'",
114 : : desc->name, param->key);
115 : :
116 [ + - ]: 4763 : if (result->negated)
117 : : goto okay;
118 : :
119 : : /* Certain parameter types only take a string and convert it. */
120 [ + + - ]: 4763 : 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 [ + - ]: 4142 : if (param->type != fs_value_is_string)
131 : : goto bad_value;
132 [ - + ]: 4142 : if (!result->has_value) {
133 [ # # ]: 0 : 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 [ + - + + : 4763 : switch (p->type) {
- - - - -
- - + ]
146 : : case fs_param_is_flag:
147 [ + - + - ]: 621 : if (param->type != fs_value_is_flag &&
148 [ - + ]: 621 : (param->type != fs_value_is_string || result->has_value))
149 : 0 : return invalf(fc, "%s: Unexpected value for '%s'",
150 : : desc->name, param->key);
151 : 621 : result->boolean = true;
152 : 621 : goto okay;
153 : :
154 : : case fs_param_is_bool:
155 [ # # # ]: 0 : switch (param->type) {
156 : : case fs_value_is_flag:
157 : 0 : result->boolean = true;
158 : 0 : goto okay;
159 : : case fs_value_is_string:
160 [ # # ]: 0 : if (param->size == 0) {
161 : 0 : result->boolean = true;
162 : 0 : goto okay;
163 : : }
164 : 0 : b = lookup_constant(bool_names, param->string, -1);
165 [ # # ]: 0 : if (b == -1)
166 : : goto bad_value;
167 : 0 : result->boolean = b;
168 : 0 : goto okay;
169 : : default:
170 : : goto bad_value;
171 : : }
172 : :
173 : : case fs_param_is_u32:
174 : 1035 : ret = kstrtouint(param->string, 0, &result->uint_32);
175 : 1035 : goto maybe_okay;
176 : : case fs_param_is_u32_octal:
177 : 2072 : ret = kstrtouint(param->string, 8, &result->uint_32);
178 : 2072 : goto maybe_okay;
179 : : case fs_param_is_u32_hex:
180 : 0 : ret = kstrtouint(param->string, 16, &result->uint_32);
181 : 0 : goto maybe_okay;
182 : : case fs_param_is_s32:
183 : 0 : ret = kstrtoint(param->string, 0, &result->int_32);
184 : 0 : goto maybe_okay;
185 : : case fs_param_is_u64:
186 : 0 : ret = kstrtoull(param->string, 0, &result->uint_64);
187 : 0 : goto maybe_okay;
188 : :
189 : : case fs_param_is_enum:
190 [ # # ]: 0 : for (e = desc->enums; e->name[0]; e++) {
191 [ # # # # ]: 0 : if (e->opt == p->opt &&
192 : 0 : strcmp(e->name, param->string) == 0) {
193 : 0 : result->uint_32 = e->value;
194 : 0 : 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 [ # # ]: 0 : if (param->type != fs_value_is_blob)
203 : : goto bad_value;
204 : : goto okay;
205 : :
206 : : case fs_param_is_fd: {
207 [ # # # ]: 0 : switch (param->type) {
208 : : case fs_value_is_string:
209 [ # # ]: 0 : if (!result->has_value)
210 : : goto bad_value;
211 : :
212 : 0 : ret = kstrtouint(param->string, 0, &result->uint_32);
213 : : break;
214 : : case fs_value_is_file:
215 : 0 : result->uint_32 = param->dirfd;
216 : : ret = 0;
217 : : default:
218 : : goto bad_value;
219 : : }
220 : :
221 [ # # ]: 0 : 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 : 0 : BUG();
231 : : }
232 : :
233 : : maybe_okay:
234 [ + - ]: 3107 : if (ret < 0)
235 : : goto bad_value;
236 : : okay:
237 : 4763 : return p->opt;
238 : :
239 : : bad_value:
240 : 0 : 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 : 0 : 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 [ # # # # ]: 0 : switch (param->type) {
264 : : case fs_value_is_string:
265 : 0 : f = getname_kernel(param->string);
266 [ # # ]: 0 : if (IS_ERR(f))
267 : 0 : 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 : 0 : f = param->name;
275 : : put_f = false;
276 : 0 : break;
277 : : default:
278 : 0 : return invalf(fc, "%s: not usable as path", param->key);
279 : : }
280 : :
281 : 0 : f->refcnt++; /* filename_lookup() drops our ref. */
282 : 0 : ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
283 [ # # ]: 0 : if (ret < 0) {
284 : 0 : errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
285 : 0 : goto out;
286 : : }
287 : :
288 [ # # # # ]: 0 : if (want_bdev &&
289 : 0 : !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) {
290 : 0 : path_put(_path);
291 : 0 : _path->dentry = NULL;
292 : 0 : _path->mnt = NULL;
293 : 0 : errorf(fc, "%s: Non-blockdev passed as '%s'",
294 : : param->key, f->name);
295 : : ret = -ENOTBLK;
296 : : }
297 : :
298 : : out:
299 [ # # ]: 0 : if (put_f)
300 : 0 : putname(f);
301 : 0 : 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 */
|