Merge tag 'for-5.4-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[platform/kernel/linux-rpi.git] / fs / fs_parser.c
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 */