fs_parse: get rid of ->enums
[linux-2.6-microblaze.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->negated = false;
89         result->uint_64 = 0;
90
91         p = fs_lookup_key(desc, param->key);
92         if (!p) {
93                 /* If we didn't find something that looks like "noxxx", see if
94                  * "xxx" takes the "no"-form negative - but only if there
95                  * wasn't an value.
96                  */
97                 if (param->type != fs_value_is_flag)
98                         goto unknown_parameter;
99                 if (param->key[0] != 'n' || param->key[1] != 'o' || !param->key[2])
100                         goto unknown_parameter;
101
102                 p = fs_lookup_key(desc, param->key + 2);
103                 if (!p)
104                         goto unknown_parameter;
105                 if (!(p->flags & fs_param_neg_with_no))
106                         goto unknown_parameter;
107                 result->boolean = false;
108                 result->negated = true;
109         }
110
111         if (p->flags & fs_param_deprecated)
112                 warnf(fc, "%s: Deprecated parameter '%s'",
113                       desc->name, param->key);
114
115         if (result->negated)
116                 goto okay;
117
118         /* Certain parameter types only take a string and convert it. */
119         switch (p->type) {
120         case __fs_param_wasnt_defined:
121                 return -EINVAL;
122         case fs_param_is_u32:
123         case fs_param_is_u32_octal:
124         case fs_param_is_u32_hex:
125         case fs_param_is_s32:
126         case fs_param_is_u64:
127         case fs_param_is_enum:
128         case fs_param_is_string:
129                 if (param->type == fs_value_is_string) {
130                         if (p->flags & fs_param_v_optional)
131                                 break;
132                         if (!*param->string)
133                                 goto bad_value;
134                         break;
135                 }
136                 if (param->type == fs_value_is_flag) {
137                         if (p->flags & fs_param_v_optional)
138                                 goto okay;
139                 }
140                 goto bad_value;
141         default:
142                 break;
143         }
144
145         /* Try to turn the type we were given into the type desired by the
146          * parameter and give an error if we can't.
147          */
148         switch (p->type) {
149         case fs_param_is_flag:
150                 if (param->type != fs_value_is_flag)
151                         return invalf(fc, "%s: Unexpected value for '%s'",
152                                       desc->name, param->key);
153                 result->boolean = true;
154                 goto okay;
155
156         case fs_param_is_bool:
157                 switch (param->type) {
158                 case fs_value_is_flag:
159                         result->boolean = true;
160                         goto okay;
161                 case fs_value_is_string:
162                         if (param->size == 0) {
163                                 result->boolean = true;
164                                 goto okay;
165                         }
166                         b = lookup_constant(bool_names, param->string, -1);
167                         if (b == -1)
168                                 goto bad_value;
169                         result->boolean = b;
170                         goto okay;
171                 default:
172                         goto bad_value;
173                 }
174
175         case fs_param_is_u32:
176                 ret = kstrtouint(param->string, 0, &result->uint_32);
177                 goto maybe_okay;
178         case fs_param_is_u32_octal:
179                 ret = kstrtouint(param->string, 8, &result->uint_32);
180                 goto maybe_okay;
181         case fs_param_is_u32_hex:
182                 ret = kstrtouint(param->string, 16, &result->uint_32);
183                 goto maybe_okay;
184         case fs_param_is_s32:
185                 ret = kstrtoint(param->string, 0, &result->int_32);
186                 goto maybe_okay;
187         case fs_param_is_u64:
188                 ret = kstrtoull(param->string, 0, &result->uint_64);
189                 goto maybe_okay;
190
191         case fs_param_is_enum:
192                 for (e = p->data; e->name; e++) {
193                         if (strcmp(e->name, param->string) == 0) {
194                                 result->uint_32 = e->value;
195                                 goto okay;
196                         }
197                 }
198                 goto bad_value;
199
200         case fs_param_is_string:
201                 goto okay;
202         case fs_param_is_blob:
203                 if (param->type != fs_value_is_blob)
204                         goto bad_value;
205                 goto okay;
206
207         case fs_param_is_fd: {
208                 switch (param->type) {
209                 case fs_value_is_string:
210                         ret = kstrtouint(param->string, 0, &result->uint_32);
211                         break;
212                 case fs_value_is_file:
213                         result->uint_32 = param->dirfd;
214                         ret = 0;
215                 default:
216                         goto bad_value;
217                 }
218
219                 if (result->uint_32 > INT_MAX)
220                         goto bad_value;
221                 goto maybe_okay;
222         }
223
224         case fs_param_is_blockdev:
225         case fs_param_is_path:
226                 goto okay;
227         default:
228                 BUG();
229         }
230
231 maybe_okay:
232         if (ret < 0)
233                 goto bad_value;
234 okay:
235         return p->opt;
236
237 bad_value:
238         return invalf(fc, "%s: Bad value for '%s'", desc->name, param->key);
239 unknown_parameter:
240         return -ENOPARAM;
241 }
242 EXPORT_SYMBOL(fs_parse);
243
244 /**
245  * fs_lookup_param - Look up a path referred to by a parameter
246  * @fc: The filesystem context to log errors through.
247  * @param: The parameter.
248  * @want_bdev: T if want a blockdev
249  * @_path: The result of the lookup
250  */
251 int fs_lookup_param(struct fs_context *fc,
252                     struct fs_parameter *param,
253                     bool want_bdev,
254                     struct path *_path)
255 {
256         struct filename *f;
257         unsigned int flags = 0;
258         bool put_f;
259         int ret;
260
261         switch (param->type) {
262         case fs_value_is_string:
263                 f = getname_kernel(param->string);
264                 if (IS_ERR(f))
265                         return PTR_ERR(f);
266                 put_f = true;
267                 break;
268         case fs_value_is_filename_empty:
269                 flags = LOOKUP_EMPTY;
270                 /* Fall through */
271         case fs_value_is_filename:
272                 f = param->name;
273                 put_f = false;
274                 break;
275         default:
276                 return invalf(fc, "%s: not usable as path", param->key);
277         }
278
279         f->refcnt++; /* filename_lookup() drops our ref. */
280         ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
281         if (ret < 0) {
282                 errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
283                 goto out;
284         }
285
286         if (want_bdev &&
287             !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) {
288                 path_put(_path);
289                 _path->dentry = NULL;
290                 _path->mnt = NULL;
291                 errorf(fc, "%s: Non-blockdev passed as '%s'",
292                        param->key, f->name);
293                 ret = -ENOTBLK;
294         }
295
296 out:
297         if (put_f)
298                 putname(f);
299         return ret;
300 }
301 EXPORT_SYMBOL(fs_lookup_param);
302
303 #ifdef CONFIG_VALIDATE_FS_PARSER
304 /**
305  * validate_constant_table - Validate a constant table
306  * @name: Name to use in reporting
307  * @tbl: The constant table to validate.
308  * @tbl_size: The size of the table.
309  * @low: The lowest permissible value.
310  * @high: The highest permissible value.
311  * @special: One special permissible value outside of the range.
312  */
313 bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
314                              int low, int high, int special)
315 {
316         size_t i;
317         bool good = true;
318
319         if (tbl_size == 0) {
320                 pr_warn("VALIDATE C-TBL: Empty\n");
321                 return true;
322         }
323
324         for (i = 0; i < tbl_size; i++) {
325                 if (!tbl[i].name) {
326                         pr_err("VALIDATE C-TBL[%zu]: Null\n", i);
327                         good = false;
328                 } else if (i > 0 && tbl[i - 1].name) {
329                         int c = strcmp(tbl[i-1].name, tbl[i].name);
330
331                         if (c == 0) {
332                                 pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n",
333                                        i, tbl[i].name);
334                                 good = false;
335                         }
336                         if (c > 0) {
337                                 pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n",
338                                        i, tbl[i-1].name, tbl[i].name);
339                                 good = false;
340                         }
341                 }
342
343                 if (tbl[i].value != special &&
344                     (tbl[i].value < low || tbl[i].value > high)) {
345                         pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n",
346                                i, tbl[i].name, tbl[i].value, low, high);
347                         good = false;
348                 }
349         }
350
351         return good;
352 }
353
354 /**
355  * fs_validate_description - Validate a parameter description
356  * @desc: The parameter description to validate.
357  */
358 bool fs_validate_description(const struct fs_parameter_description *desc)
359 {
360         const struct fs_parameter_spec *param, *p2;
361         const char *name = desc->name;
362         bool good = true;
363
364         pr_notice("*** VALIDATE %s ***\n", name);
365
366         if (!name[0]) {
367                 pr_err("VALIDATE Parser: No name\n");
368                 name = "Unknown";
369                 good = false;
370         }
371
372         if (desc->specs) {
373                 for (param = desc->specs; param->name; param++) {
374                         enum fs_parameter_type t = param->type;
375
376                         /* Check that the type is in range */
377                         if (t == __fs_param_wasnt_defined ||
378                             t >= nr__fs_parameter_type) {
379                                 pr_err("VALIDATE %s: PARAM[%s] Bad type %u\n",
380                                        name, param->name, t);
381                                 good = false;
382                         } else if (t == fs_param_is_enum) {
383                                 const struct fs_parameter_enum *e = param->data;
384                                 if (!e || !e->name) {
385                                         pr_err("VALIDATE %s: PARAM[%s] enum with no values\n",
386                                                name, param->name);
387                                         good = false;
388                                 }
389                         }
390
391                         /* Check for duplicate parameter names */
392                         for (p2 = desc->specs; p2 < param; p2++) {
393                                 if (strcmp(param->name, p2->name) == 0) {
394                                         pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n",
395                                                name, param->name);
396                                         good = false;
397                                 }
398                         }
399                 }
400         }
401         return good;
402 }
403 #endif /* CONFIG_VALIDATE_FS_PARSER */