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 : static const struct constant_table *
27 836240 : __lookup_constant(const struct constant_table *tbl, const char *name)
28 : {
29 4497226 : for ( ; tbl->name; tbl++)
30 3690043 : if (strcmp(name, tbl->name) == 0)
31 29057 : return tbl;
32 : return NULL;
33 : }
34 :
35 : /**
36 : * lookup_constant - Look up a constant by name in an ordered table
37 : * @tbl: The table of constants to search.
38 : * @name: The name to look up.
39 : * @not_found: The value to return if the name is not found.
40 : */
41 807190 : int lookup_constant(const struct constant_table *tbl, const char *name, int not_found)
42 : {
43 807190 : const struct constant_table *p = __lookup_constant(tbl, name);
44 :
45 807216 : return p ? p->value : not_found;
46 : }
47 : EXPORT_SYMBOL(lookup_constant);
48 :
49 : static inline bool is_flag(const struct fs_parameter_spec *p)
50 : {
51 602583 : return p->type == NULL;
52 : }
53 :
54 396623 : static const struct fs_parameter_spec *fs_lookup_key(
55 : const struct fs_parameter_spec *desc,
56 : struct fs_parameter *param, bool *negated)
57 : {
58 396623 : const struct fs_parameter_spec *p, *other = NULL;
59 396623 : const char *name = param->key;
60 396623 : bool want_flag = param->type == fs_value_is_flag;
61 :
62 396623 : *negated = false;
63 7932416 : for (p = desc; p->name; p++) {
64 7836948 : if (strcmp(p->name, name) != 0)
65 7535698 : continue;
66 301250 : if (likely(is_flag(p) == want_flag))
67 301155 : return p;
68 : other = p;
69 : }
70 95468 : if (want_flag) {
71 50 : if (name[0] == 'n' && name[1] == 'o' && name[2]) {
72 1394 : for (p = desc; p->name; p++) {
73 1369 : if (strcmp(p->name, name + 2) != 0)
74 1368 : continue;
75 1 : if (!(p->flags & fs_param_neg_with_no))
76 1 : continue;
77 0 : *negated = true;
78 0 : return p;
79 : }
80 : }
81 : }
82 : return other;
83 : }
84 :
85 : /*
86 : * fs_parse - Parse a filesystem configuration parameter
87 : * @fc: The filesystem context to log errors through.
88 : * @desc: The parameter description to use.
89 : * @param: The parameter.
90 : * @result: Where to place the result of the parse
91 : *
92 : * Parse a filesystem configuration parameter and attempt a conversion for a
93 : * simple parameter for which this is requested. If successful, the determined
94 : * parameter ID is placed into @result->key, the desired type is indicated in
95 : * @result->t and any converted value is placed into an appropriate member of
96 : * the union in @result.
97 : *
98 : * The function returns the parameter number if the parameter was matched,
99 : * -ENOPARAM if it wasn't matched and @desc->ignore_unknown indicated that
100 : * unknown parameters are okay and -EINVAL if there was a conversion issue or
101 : * the parameter wasn't recognised and unknowns aren't okay.
102 : */
103 396676 : int __fs_parse(struct p_log *log,
104 : const struct fs_parameter_spec *desc,
105 : struct fs_parameter *param,
106 : struct fs_parse_result *result)
107 : {
108 396676 : const struct fs_parameter_spec *p;
109 :
110 396676 : result->uint_64 = 0;
111 :
112 396676 : p = fs_lookup_key(desc, param, &result->negated);
113 396629 : if (!p)
114 : return -ENOPARAM;
115 :
116 301163 : if (p->flags & fs_param_deprecated)
117 0 : warn_plog(log, "Deprecated parameter '%s'", param->key);
118 :
119 : /* Try to turn the type we were given into the type desired by the
120 : * parameter and give an error if we can't.
121 : */
122 301163 : if (is_flag(p)) {
123 143600 : if (param->type != fs_value_is_flag)
124 0 : return inval_plog(log, "Unexpected value for '%s'",
125 : param->key);
126 143600 : result->boolean = !result->negated;
127 : } else {
128 157563 : int ret = p->type(log, p, param, result);
129 157562 : if (ret)
130 : return ret;
131 : }
132 301140 : return p->opt;
133 : }
134 : EXPORT_SYMBOL(__fs_parse);
135 :
136 : /**
137 : * fs_lookup_param - Look up a path referred to by a parameter
138 : * @fc: The filesystem context to log errors through.
139 : * @param: The parameter.
140 : * @want_bdev: T if want a blockdev
141 : * @flags: Pathwalk flags passed to filename_lookup()
142 : * @_path: The result of the lookup
143 : */
144 4 : int fs_lookup_param(struct fs_context *fc,
145 : struct fs_parameter *param,
146 : bool want_bdev,
147 : unsigned int flags,
148 : struct path *_path)
149 : {
150 4 : struct filename *f;
151 4 : bool put_f;
152 4 : int ret;
153 :
154 4 : switch (param->type) {
155 4 : case fs_value_is_string:
156 4 : f = getname_kernel(param->string);
157 4 : if (IS_ERR(f))
158 0 : return PTR_ERR(f);
159 : put_f = true;
160 : break;
161 0 : case fs_value_is_filename:
162 0 : f = param->name;
163 0 : put_f = false;
164 0 : break;
165 0 : default:
166 0 : return invalf(fc, "%s: not usable as path", param->key);
167 : }
168 :
169 4 : ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
170 4 : if (ret < 0) {
171 2 : errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
172 2 : goto out;
173 : }
174 :
175 2 : if (want_bdev &&
176 2 : !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) {
177 0 : path_put(_path);
178 0 : _path->dentry = NULL;
179 0 : _path->mnt = NULL;
180 0 : errorf(fc, "%s: Non-blockdev passed as '%s'",
181 : param->key, f->name);
182 0 : ret = -ENOTBLK;
183 : }
184 :
185 2 : out:
186 4 : if (put_f)
187 4 : putname(f);
188 : return ret;
189 : }
190 : EXPORT_SYMBOL(fs_lookup_param);
191 :
192 22 : static int fs_param_bad_value(struct p_log *log, struct fs_parameter *param)
193 : {
194 22 : return inval_plog(log, "Bad value for '%s'", param->key);
195 : }
196 :
197 0 : int fs_param_is_bool(struct p_log *log, const struct fs_parameter_spec *p,
198 : struct fs_parameter *param, struct fs_parse_result *result)
199 : {
200 0 : int b;
201 0 : if (param->type != fs_value_is_string)
202 0 : return fs_param_bad_value(log, param);
203 0 : if (!*param->string && (p->flags & fs_param_can_be_empty))
204 : return 0;
205 0 : b = lookup_constant(bool_names, param->string, -1);
206 0 : if (b == -1)
207 0 : return fs_param_bad_value(log, param);
208 0 : result->boolean = b;
209 0 : return 0;
210 : }
211 : EXPORT_SYMBOL(fs_param_is_bool);
212 :
213 2556 : int fs_param_is_u32(struct p_log *log, const struct fs_parameter_spec *p,
214 : struct fs_parameter *param, struct fs_parse_result *result)
215 : {
216 2556 : int base = (unsigned long)p->data;
217 2556 : if (param->type != fs_value_is_string)
218 0 : return fs_param_bad_value(log, param);
219 2556 : if (!*param->string && (p->flags & fs_param_can_be_empty))
220 : return 0;
221 2556 : if (kstrtouint(param->string, base, &result->uint_32) < 0)
222 16 : return fs_param_bad_value(log, param);
223 : return 0;
224 : }
225 : EXPORT_SYMBOL(fs_param_is_u32);
226 :
227 0 : int fs_param_is_s32(struct p_log *log, const struct fs_parameter_spec *p,
228 : struct fs_parameter *param, struct fs_parse_result *result)
229 : {
230 0 : if (param->type != fs_value_is_string)
231 0 : return fs_param_bad_value(log, param);
232 0 : if (!*param->string && (p->flags & fs_param_can_be_empty))
233 : return 0;
234 0 : if (kstrtoint(param->string, 0, &result->int_32) < 0)
235 0 : return fs_param_bad_value(log, param);
236 : return 0;
237 : }
238 : EXPORT_SYMBOL(fs_param_is_s32);
239 :
240 0 : int fs_param_is_u64(struct p_log *log, const struct fs_parameter_spec *p,
241 : struct fs_parameter *param, struct fs_parse_result *result)
242 : {
243 0 : if (param->type != fs_value_is_string)
244 0 : return fs_param_bad_value(log, param);
245 0 : if (!*param->string && (p->flags & fs_param_can_be_empty))
246 : return 0;
247 0 : if (kstrtoull(param->string, 0, &result->uint_64) < 0)
248 0 : return fs_param_bad_value(log, param);
249 : return 0;
250 : }
251 : EXPORT_SYMBOL(fs_param_is_u64);
252 :
253 29067 : int fs_param_is_enum(struct p_log *log, const struct fs_parameter_spec *p,
254 : struct fs_parameter *param, struct fs_parse_result *result)
255 : {
256 29067 : const struct constant_table *c;
257 29067 : if (param->type != fs_value_is_string)
258 0 : return fs_param_bad_value(log, param);
259 29067 : if (!*param->string && (p->flags & fs_param_can_be_empty))
260 : return 0;
261 29067 : c = __lookup_constant(p->data, param->string);
262 29057 : if (!c)
263 0 : return fs_param_bad_value(log, param);
264 29057 : result->uint_32 = c->value;
265 29057 : return 0;
266 : }
267 : EXPORT_SYMBOL(fs_param_is_enum);
268 :
269 125942 : int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p,
270 : struct fs_parameter *param, struct fs_parse_result *result)
271 : {
272 125942 : if (param->type != fs_value_is_string ||
273 125942 : (!*param->string && !(p->flags & fs_param_can_be_empty)))
274 6 : return fs_param_bad_value(log, param);
275 : return 0;
276 : }
277 : EXPORT_SYMBOL(fs_param_is_string);
278 :
279 0 : int fs_param_is_blob(struct p_log *log, const struct fs_parameter_spec *p,
280 : struct fs_parameter *param, struct fs_parse_result *result)
281 : {
282 0 : if (param->type != fs_value_is_blob)
283 0 : return fs_param_bad_value(log, param);
284 : return 0;
285 : }
286 : EXPORT_SYMBOL(fs_param_is_blob);
287 :
288 0 : int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p,
289 : struct fs_parameter *param, struct fs_parse_result *result)
290 : {
291 0 : switch (param->type) {
292 0 : case fs_value_is_string:
293 0 : if ((!*param->string && !(p->flags & fs_param_can_be_empty)) ||
294 0 : kstrtouint(param->string, 0, &result->uint_32) < 0)
295 : break;
296 0 : if (result->uint_32 <= INT_MAX)
297 : return 0;
298 : break;
299 0 : case fs_value_is_file:
300 0 : result->uint_32 = param->dirfd;
301 0 : if (result->uint_32 <= INT_MAX)
302 : return 0;
303 : break;
304 : default:
305 : break;
306 : }
307 0 : return fs_param_bad_value(log, param);
308 : }
309 : EXPORT_SYMBOL(fs_param_is_fd);
310 :
311 8 : int fs_param_is_blockdev(struct p_log *log, const struct fs_parameter_spec *p,
312 : struct fs_parameter *param, struct fs_parse_result *result)
313 : {
314 8 : return 0;
315 : }
316 : EXPORT_SYMBOL(fs_param_is_blockdev);
317 :
318 0 : int fs_param_is_path(struct p_log *log, const struct fs_parameter_spec *p,
319 : struct fs_parameter *param, struct fs_parse_result *result)
320 : {
321 0 : return 0;
322 : }
323 : EXPORT_SYMBOL(fs_param_is_path);
324 :
325 : #ifdef CONFIG_VALIDATE_FS_PARSER
326 : /**
327 : * validate_constant_table - Validate a constant table
328 : * @tbl: The constant table to validate.
329 : * @tbl_size: The size of the table.
330 : * @low: The lowest permissible value.
331 : * @high: The highest permissible value.
332 : * @special: One special permissible value outside of the range.
333 : */
334 0 : bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
335 : int low, int high, int special)
336 : {
337 0 : size_t i;
338 0 : bool good = true;
339 :
340 0 : if (tbl_size == 0) {
341 0 : pr_warn("VALIDATE C-TBL: Empty\n");
342 0 : return true;
343 : }
344 :
345 0 : for (i = 0; i < tbl_size; i++) {
346 0 : if (!tbl[i].name) {
347 0 : pr_err("VALIDATE C-TBL[%zu]: Null\n", i);
348 0 : good = false;
349 0 : } else if (i > 0 && tbl[i - 1].name) {
350 0 : int c = strcmp(tbl[i-1].name, tbl[i].name);
351 :
352 0 : if (c == 0) {
353 0 : pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n",
354 : i, tbl[i].name);
355 0 : good = false;
356 : }
357 0 : if (c > 0) {
358 0 : pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n",
359 : i, tbl[i-1].name, tbl[i].name);
360 0 : good = false;
361 : }
362 : }
363 :
364 0 : if (tbl[i].value != special &&
365 0 : (tbl[i].value < low || tbl[i].value > high)) {
366 0 : pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n",
367 : i, tbl[i].name, tbl[i].value, low, high);
368 0 : good = false;
369 : }
370 : }
371 :
372 : return good;
373 : }
374 :
375 : /**
376 : * fs_validate_description - Validate a parameter description
377 : * @name: The parameter name to search for.
378 : * @desc: The parameter description to validate.
379 : */
380 74 : bool fs_validate_description(const char *name,
381 : const struct fs_parameter_spec *desc)
382 : {
383 74 : const struct fs_parameter_spec *param, *p2;
384 74 : bool good = true;
385 :
386 4140 : for (param = desc; param->name; param++) {
387 : /* Check for duplicate parameter names */
388 128730 : for (p2 = desc; p2 < param; p2++) {
389 124664 : if (strcmp(param->name, p2->name) == 0) {
390 170 : if (is_flag(param) != is_flag(p2))
391 170 : continue;
392 0 : pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n",
393 : name, param->name);
394 0 : good = false;
395 : }
396 : }
397 : }
398 74 : return good;
399 : }
400 : #endif /* CONFIG_VALIDATE_FS_PARSER */
|