summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2018-11-01 19:07:24 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2019-02-28 03:28:53 -0500
commit31d921c7fb9691722ba9503b64153cdc322a7fa8 (patch)
tree9de6597678d16b1058c8f9f2326121651ef9bb57
parentc6b82263f9c6e745eb4c5dfc2578d147c4cd7604 (diff)
vfs: Add configuration parser helpers
Because the new API passes in key,value parameters, match_token() cannot be used with it. Instead, provide three new helpers to aid with parsing: (1) fs_parse(). This takes a parameter and a simple static description of all the parameters and maps the key name to an ID. It returns 1 on a match, 0 on no match if unknowns should be ignored and some other negative error code on a parse error. The parameter description includes a list of key names to IDs, desired parameter types and a list of enumeration name -> ID mappings. [!] Note that for the moment I've required that the key->ID mapping array is expected to be sorted and unterminated. The size of the array is noted in the fsconfig_parser struct. This allows me to use bsearch(), but I'm not sure any performance gain is worth the hassle of requiring people to keep the array sorted. The parameter type array is sized according to the number of parameter IDs and is indexed directly. The optional enum mapping array is an unterminated, unsorted list and the size goes into the fsconfig_parser struct. The function can do some additional things: (a) If it's not ambiguous and no value is given, the prefix "no" on a key name is permitted to indicate that the parameter should be considered negatory. (b) If the desired type is a single simple integer, it will perform an appropriate conversion and store the result in a union in the parse result. (c) If the desired type is an enumeration, {key ID, name} will be looked up in the enumeration list and the matching value will be stored in the parse result union. (d) Optionally generate an error if the key is unrecognised. This is called something like: enum rdt_param { Opt_cdp, Opt_cdpl2, Opt_mba_mpbs, nr__rdt_params }; const struct fs_parameter_spec rdt_param_specs[nr__rdt_params] = { [Opt_cdp] = { fs_param_is_bool }, [Opt_cdpl2] = { fs_param_is_bool }, [Opt_mba_mpbs] = { fs_param_is_bool }, }; const const char *const rdt_param_keys[nr__rdt_params] = { [Opt_cdp] = "cdp", [Opt_cdpl2] = "cdpl2", [Opt_mba_mpbs] = "mba_mbps", }; const struct fs_parameter_description rdt_parser = { .name = "rdt", .nr_params = nr__rdt_params, .keys = rdt_param_keys, .specs = rdt_param_specs, .no_source = true, }; int rdt_parse_param(struct fs_context *fc, struct fs_parameter *param) { struct fs_parse_result parse; struct rdt_fs_context *ctx = rdt_fc2context(fc); int ret; ret = fs_parse(fc, &rdt_parser, param, &parse); if (ret < 0) return ret; switch (parse.key) { case Opt_cdp: ctx->enable_cdpl3 = true; return 0; case Opt_cdpl2: ctx->enable_cdpl2 = true; return 0; case Opt_mba_mpbs: ctx->enable_mba_mbps = true; return 0; } return -EINVAL; } (2) fs_lookup_param(). This takes a { dirfd, path, LOOKUP_EMPTY? } or string value and performs an appropriate path lookup to convert it into a path object, which it will then return. If the desired type was a blockdev, the type of the looked up inode will be checked to make sure it is one. This can be used like: enum foo_param { Opt_source, nr__foo_params }; const struct fs_parameter_spec foo_param_specs[nr__foo_params] = { [Opt_source] = { fs_param_is_blockdev }, }; const char *char foo_param_keys[nr__foo_params] = { [Opt_source] = "source", }; const struct constant_table foo_param_alt_keys[] = { { "device", Opt_source }, }; const struct fs_parameter_description foo_parser = { .name = "foo", .nr_params = nr__foo_params, .nr_alt_keys = ARRAY_SIZE(foo_param_alt_keys), .keys = foo_param_keys, .alt_keys = foo_param_alt_keys, .specs = foo_param_specs, }; int foo_parse_param(struct fs_context *fc, struct fs_parameter *param) { struct fs_parse_result parse; struct foo_fs_context *ctx = foo_fc2context(fc); int ret; ret = fs_parse(fc, &foo_parser, param, &parse); if (ret < 0) return ret; switch (parse.key) { case Opt_source: return fs_lookup_param(fc, &foo_parser, param, &parse, &ctx->source); default: return -EINVAL; } } (3) lookup_constant(). This takes a table of named constants and looks up the given name within it. The table is expected to be sorted such that bsearch() be used upon it. Possibly I should require the table be terminated and just use a for-loop to scan it instead of using bsearch() to reduce hassle. Tables look something like: static const struct constant_table bool_names[] = { { "0", false }, { "1", true }, { "false", false }, { "no", false }, { "true", true }, { "yes", true }, }; and a lookup is done with something like: b = lookup_constant(bool_names, param->string, -1); Additionally, optional validation routines for the parameter description are provided that can be enabled at compile time. A later patch will invoke these when a filesystem is registered. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/Kconfig7
-rw-r--r--fs/Makefile2
-rw-r--r--fs/fs_parser.c447
-rw-r--r--fs/internal.h2
-rw-r--r--fs/namei.c4
-rw-r--r--include/linux/errno.h1
-rw-r--r--include/linux/fs_context.h29
-rw-r--r--include/linux/fs_parser.h151
8 files changed, 640 insertions, 3 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index ac474a61be37..25700b152c75 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -8,6 +8,13 @@ menu "File systems"
8config DCACHE_WORD_ACCESS 8config DCACHE_WORD_ACCESS
9 bool 9 bool
10 10
11config VALIDATE_FS_PARSER
12 bool "Validate filesystem parameter description"
13 default y
14 help
15 Enable this to perform validation of the parameter description for a
16 filesystem when it is registered.
17
11if BLOCK 18if BLOCK
12 19
13config FS_IOMAP 20config FS_IOMAP
diff --git a/fs/Makefile b/fs/Makefile
index 5563cf34f7c2..9a0b8003f069 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -13,7 +13,7 @@ obj-y := open.o read_write.o file_table.o super.o \
13 seq_file.o xattr.o libfs.o fs-writeback.o \ 13 seq_file.o xattr.o libfs.o fs-writeback.o \
14 pnode.o splice.o sync.o utimes.o d_path.o \ 14 pnode.o splice.o sync.o utimes.o d_path.o \
15 stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ 15 stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
16 fs_context.o 16 fs_context.o fs_parser.o
17 17
18ifeq ($(CONFIG_BLOCK),y) 18ifeq ($(CONFIG_BLOCK),y)
19obj-y += buffer.o block_dev.o direct-io.o mpage.o 19obj-y += buffer.o block_dev.o direct-io.o mpage.o
diff --git a/fs/fs_parser.c b/fs/fs_parser.c
new file mode 100644
index 000000000000..842e8f749db6
--- /dev/null
+++ b/fs/fs_parser.c
@@ -0,0 +1,447 @@
1/* Filesystem parameter parser.
2 *
3 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#include <linux/export.h>
13#include <linux/fs_context.h>
14#include <linux/fs_parser.h>
15#include <linux/slab.h>
16#include <linux/security.h>
17#include <linux/namei.h>
18#include "internal.h"
19
20static const struct constant_table bool_names[] = {
21 { "0", false },
22 { "1", true },
23 { "false", false },
24 { "no", false },
25 { "true", true },
26 { "yes", true },
27};
28
29/**
30 * lookup_constant - Look up a constant by name in an ordered table
31 * @tbl: The table of constants to search.
32 * @tbl_size: The size of the table.
33 * @name: The name to look up.
34 * @not_found: The value to return if the name is not found.
35 */
36int __lookup_constant(const struct constant_table *tbl, size_t tbl_size,
37 const char *name, int not_found)
38{
39 unsigned int i;
40
41 for (i = 0; i < tbl_size; i++)
42 if (strcmp(name, tbl[i].name) == 0)
43 return tbl[i].value;
44
45 return not_found;
46}
47EXPORT_SYMBOL(__lookup_constant);
48
49static const struct fs_parameter_spec *fs_lookup_key(
50 const struct fs_parameter_description *desc,
51 const char *name)
52{
53 const struct fs_parameter_spec *p;
54
55 if (!desc->specs)
56 return NULL;
57
58 for (p = desc->specs; p->name; p++)
59 if (strcmp(p->name, name) == 0)
60 return p;
61
62 return NULL;
63}
64
65/*
66 * fs_parse - Parse a filesystem configuration parameter
67 * @fc: The filesystem context to log errors through.
68 * @desc: The parameter description to use.
69 * @param: The parameter.
70 * @result: Where to place the result of the parse
71 *
72 * Parse a filesystem configuration parameter and attempt a conversion for a
73 * simple parameter for which this is requested. If successful, the determined
74 * parameter ID is placed into @result->key, the desired type is indicated in
75 * @result->t and any converted value is placed into an appropriate member of
76 * the union in @result.
77 *
78 * The function returns the parameter number if the parameter was matched,
79 * -ENOPARAM if it wasn't matched and @desc->ignore_unknown indicated that
80 * unknown parameters are okay and -EINVAL if there was a conversion issue or
81 * the parameter wasn't recognised and unknowns aren't okay.
82 */
83int fs_parse(struct fs_context *fc,
84 const struct fs_parameter_description *desc,
85 struct fs_parameter *param,
86 struct fs_parse_result *result)
87{
88 const struct fs_parameter_spec *p;
89 const struct fs_parameter_enum *e;
90 int ret = -ENOPARAM, b;
91
92 result->has_value = !!param->string;
93 result->negated = false;
94 result->uint_64 = 0;
95
96 p = fs_lookup_key(desc, param->key);
97 if (!p) {
98 /* If we didn't find something that looks like "noxxx", see if
99 * "xxx" takes the "no"-form negative - but only if there
100 * wasn't an value.
101 */
102 if (result->has_value)
103 goto unknown_parameter;
104 if (param->key[0] != 'n' || param->key[1] != 'o' || !param->key[2])
105 goto unknown_parameter;
106
107 p = fs_lookup_key(desc, param->key + 2);
108 if (!p)
109 goto unknown_parameter;
110 if (!(p->flags & fs_param_neg_with_no))
111 goto unknown_parameter;
112 result->boolean = false;
113 result->negated = true;
114 }
115
116 if (p->flags & fs_param_deprecated)
117 warnf(fc, "%s: Deprecated parameter '%s'",
118 desc->name, param->key);
119
120 if (result->negated)
121 goto okay;
122
123 /* Certain parameter types only take a string and convert it. */
124 switch (p->type) {
125 case __fs_param_wasnt_defined:
126 return -EINVAL;
127 case fs_param_is_u32:
128 case fs_param_is_u32_octal:
129 case fs_param_is_u32_hex:
130 case fs_param_is_s32:
131 case fs_param_is_u64:
132 case fs_param_is_enum:
133 case fs_param_is_string:
134 if (param->type != fs_value_is_string)
135 goto bad_value;
136 if (!result->has_value) {
137 if (p->flags & fs_param_v_optional)
138 goto okay;
139 goto bad_value;
140 }
141 /* Fall through */
142 default:
143 break;
144 }
145
146 /* Try to turn the type we were given into the type desired by the
147 * parameter and give an error if we can't.
148 */
149 switch (p->type) {
150 case fs_param_is_flag:
151 if (param->type != fs_value_is_flag &&
152 (param->type != fs_value_is_string || result->has_value))
153 return invalf(fc, "%s: Unexpected value for '%s'",
154 desc->name, param->key);
155 result->boolean = true;
156 goto okay;
157
158 case fs_param_is_bool:
159 switch (param->type) {
160 case fs_value_is_flag:
161 result->boolean = true;
162 goto okay;
163 case fs_value_is_string:
164 if (param->size == 0) {
165 result->boolean = true;
166 goto okay;
167 }
168 b = lookup_constant(bool_names, param->string, -1);
169 if (b == -1)
170 goto bad_value;
171 result->boolean = b;
172 goto okay;
173 default:
174 goto bad_value;
175 }
176
177 case fs_param_is_u32:
178 ret = kstrtouint(param->string, 0, &result->uint_32);
179 goto maybe_okay;
180 case fs_param_is_u32_octal:
181 ret = kstrtouint(param->string, 8, &result->uint_32);
182 goto maybe_okay;
183 case fs_param_is_u32_hex:
184 ret = kstrtouint(param->string, 16, &result->uint_32);
185 goto maybe_okay;
186 case fs_param_is_s32:
187 ret = kstrtoint(param->string, 0, &result->int_32);
188 goto maybe_okay;
189 case fs_param_is_u64:
190 ret = kstrtoull(param->string, 0, &result->uint_64);
191 goto maybe_okay;
192
193 case fs_param_is_enum:
194 for (e = desc->enums; e->name[0]; e++) {
195 if (e->opt == p->opt &&
196 strcmp(e->name, param->string) == 0) {
197 result->uint_32 = e->value;
198 goto okay;
199 }
200 }
201 goto bad_value;
202
203 case fs_param_is_string:
204 goto okay;
205 case fs_param_is_blob:
206 if (param->type != fs_value_is_blob)
207 goto bad_value;
208 goto okay;
209
210 case fs_param_is_fd: {
211 if (param->type != fs_value_is_file)
212 goto bad_value;
213 goto okay;
214 }
215
216 case fs_param_is_blockdev:
217 case fs_param_is_path:
218 goto okay;
219 default:
220 BUG();
221 }
222
223maybe_okay:
224 if (ret < 0)
225 goto bad_value;
226okay:
227 return p->opt;
228
229bad_value:
230 return invalf(fc, "%s: Bad value for '%s'", desc->name, param->key);
231unknown_parameter:
232 return -ENOPARAM;
233}
234EXPORT_SYMBOL(fs_parse);
235
236/**
237 * fs_lookup_param - Look up a path referred to by a parameter
238 * @fc: The filesystem context to log errors through.
239 * @param: The parameter.
240 * @want_bdev: T if want a blockdev
241 * @_path: The result of the lookup
242 */
243int fs_lookup_param(struct fs_context *fc,
244 struct fs_parameter *param,
245 bool want_bdev,
246 struct path *_path)
247{
248 struct filename *f;
249 unsigned int flags = 0;
250 bool put_f;
251 int ret;
252
253 switch (param->type) {
254 case fs_value_is_string:
255 f = getname_kernel(param->string);
256 if (IS_ERR(f))
257 return PTR_ERR(f);
258 put_f = true;
259 break;
260 case fs_value_is_filename_empty:
261 flags = LOOKUP_EMPTY;
262 /* Fall through */
263 case fs_value_is_filename:
264 f = param->name;
265 put_f = false;
266 break;
267 default:
268 return invalf(fc, "%s: not usable as path", param->key);
269 }
270
271 ret = filename_lookup(param->dirfd, f, flags, _path, NULL);
272 if (ret < 0) {
273 errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name);
274 goto out;
275 }
276
277 if (want_bdev &&
278 !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) {
279 path_put(_path);
280 _path->dentry = NULL;
281 _path->mnt = NULL;
282 errorf(fc, "%s: Non-blockdev passed as '%s'",
283 param->key, f->name);
284 ret = -ENOTBLK;
285 }
286
287out:
288 if (put_f)
289 putname(f);
290 return ret;
291}
292EXPORT_SYMBOL(fs_lookup_param);
293
294#ifdef CONFIG_VALIDATE_FS_PARSER
295/**
296 * validate_constant_table - Validate a constant table
297 * @name: Name to use in reporting
298 * @tbl: The constant table to validate.
299 * @tbl_size: The size of the table.
300 * @low: The lowest permissible value.
301 * @high: The highest permissible value.
302 * @special: One special permissible value outside of the range.
303 */
304bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
305 int low, int high, int special)
306{
307 size_t i;
308 bool good = true;
309
310 if (tbl_size == 0) {
311 pr_warn("VALIDATE C-TBL: Empty\n");
312 return true;
313 }
314
315 for (i = 0; i < tbl_size; i++) {
316 if (!tbl[i].name) {
317 pr_err("VALIDATE C-TBL[%zu]: Null\n", i);
318 good = false;
319 } else if (i > 0 && tbl[i - 1].name) {
320 int c = strcmp(tbl[i-1].name, tbl[i].name);
321
322 if (c == 0) {
323 pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n",
324 i, tbl[i].name);
325 good = false;
326 }
327 if (c > 0) {
328 pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n",
329 i, tbl[i-1].name, tbl[i].name);
330 good = false;
331 }
332 }
333
334 if (tbl[i].value != special &&
335 (tbl[i].value < low || tbl[i].value > high)) {
336 pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n",
337 i, tbl[i].name, tbl[i].value, low, high);
338 good = false;
339 }
340 }
341
342 return good;
343}
344
345/**
346 * fs_validate_description - Validate a parameter description
347 * @desc: The parameter description to validate.
348 */
349bool fs_validate_description(const struct fs_parameter_description *desc)
350{
351 const struct fs_parameter_spec *param, *p2;
352 const struct fs_parameter_enum *e;
353 const char *name = desc->name;
354 unsigned int nr_params = 0;
355 bool good = true, enums = false;
356
357 pr_notice("*** VALIDATE %s ***\n", name);
358
359 if (!name[0]) {
360 pr_err("VALIDATE Parser: No name\n");
361 name = "Unknown";
362 good = false;
363 }
364
365 if (desc->specs) {
366 for (param = desc->specs; param->name; param++) {
367 enum fs_parameter_type t = param->type;
368
369 /* Check that the type is in range */
370 if (t == __fs_param_wasnt_defined ||
371 t >= nr__fs_parameter_type) {
372 pr_err("VALIDATE %s: PARAM[%s] Bad type %u\n",
373 name, param->name, t);
374 good = false;
375 } else if (t == fs_param_is_enum) {
376 enums = true;
377 }
378
379 /* Check for duplicate parameter names */
380 for (p2 = desc->specs; p2 < param; p2++) {
381 if (strcmp(param->name, p2->name) == 0) {
382 pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n",
383 name, param->name);
384 good = false;
385 }
386 }
387 }
388
389 nr_params = param - desc->specs;
390 }
391
392 if (desc->enums) {
393 if (!nr_params) {
394 pr_err("VALIDATE %s: Enum table but no parameters\n",
395 name);
396 good = false;
397 goto no_enums;
398 }
399 if (!enums) {
400 pr_err("VALIDATE %s: Enum table but no enum-type values\n",
401 name);
402 good = false;
403 goto no_enums;
404 }
405
406 for (e = desc->enums; e->name[0]; e++) {
407 /* Check that all entries in the enum table have at
408 * least one parameter that uses them.
409 */
410 for (param = desc->specs; param->name; param++) {
411 if (param->opt == e->opt &&
412 param->type != fs_param_is_enum) {
413 pr_err("VALIDATE %s: e[%lu] enum val for %s\n",
414 name, e - desc->enums, param->name);
415 good = false;
416 }
417 }
418 }
419
420 /* Check that all enum-type parameters have at least one enum
421 * value in the enum table.
422 */
423 for (param = desc->specs; param->name; param++) {
424 if (param->type != fs_param_is_enum)
425 continue;
426 for (e = desc->enums; e->name[0]; e++)
427 if (e->opt == param->opt)
428 break;
429 if (!e->name[0]) {
430 pr_err("VALIDATE %s: PARAM[%s] enum with no values\n",
431 name, param->name);
432 good = false;
433 }
434 }
435 } else {
436 if (enums) {
437 pr_err("VALIDATE %s: enum-type values, but no enum table\n",
438 name);
439 good = false;
440 goto no_enums;
441 }
442 }
443
444no_enums:
445 return good;
446}
447#endif /* CONFIG_VALIDATE_FS_PARSER */
diff --git a/fs/internal.h b/fs/internal.h
index 8f8d07cc433f..6a8b71643af4 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -61,6 +61,8 @@ extern void fc_drop_locked(struct fs_context *);
61/* 61/*
62 * namei.c 62 * namei.c
63 */ 63 */
64extern int filename_lookup(int dfd, struct filename *name, unsigned flags,
65 struct path *path, struct path *root);
64extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *); 66extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *);
65extern int vfs_path_lookup(struct dentry *, struct vfsmount *, 67extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
66 const char *, unsigned int, struct path *); 68 const char *, unsigned int, struct path *);
diff --git a/fs/namei.c b/fs/namei.c
index 914178cdbe94..a85deb55d0c9 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2333,8 +2333,8 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path
2333 return err; 2333 return err;
2334} 2334}
2335 2335
2336static int filename_lookup(int dfd, struct filename *name, unsigned flags, 2336int filename_lookup(int dfd, struct filename *name, unsigned flags,
2337 struct path *path, struct path *root) 2337 struct path *path, struct path *root)
2338{ 2338{
2339 int retval; 2339 int retval;
2340 struct nameidata nd; 2340 struct nameidata nd;
diff --git a/include/linux/errno.h b/include/linux/errno.h
index 3cba627577d6..d73f597a2484 100644
--- a/include/linux/errno.h
+++ b/include/linux/errno.h
@@ -18,6 +18,7 @@
18#define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */ 18#define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */
19#define EPROBE_DEFER 517 /* Driver requests probe retry */ 19#define EPROBE_DEFER 517 /* Driver requests probe retry */
20#define EOPENSTALE 518 /* open found a stale dentry */ 20#define EOPENSTALE 518 /* open found a stale dentry */
21#define ENOPARAM 519 /* Parameter not supported */
21 22
22/* Defined for the NFSv3 protocol */ 23/* Defined for the NFSv3 protocol */
23#define EBADHANDLE 521 /* Illegal NFS file handle */ 24#define EBADHANDLE 521 /* Illegal NFS file handle */
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index d208cc40b868..899027c94788 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -35,6 +35,35 @@ enum fs_context_purpose {
35}; 35};
36 36
37/* 37/*
38 * Type of parameter value.
39 */
40enum fs_value_type {
41 fs_value_is_undefined,
42 fs_value_is_flag, /* Value not given a value */
43 fs_value_is_string, /* Value is a string */
44 fs_value_is_blob, /* Value is a binary blob */
45 fs_value_is_filename, /* Value is a filename* + dirfd */
46 fs_value_is_filename_empty, /* Value is a filename* + dirfd + AT_EMPTY_PATH */
47 fs_value_is_file, /* Value is a file* */
48};
49
50/*
51 * Configuration parameter.
52 */
53struct fs_parameter {
54 const char *key; /* Parameter name */
55 enum fs_value_type type:8; /* The type of value here */
56 union {
57 char *string;
58 void *blob;
59 struct filename *name;
60 struct file *file;
61 };
62 size_t size;
63 int dirfd;
64};
65
66/*
38 * Filesystem context for holding the parameters used in the creation or 67 * Filesystem context for holding the parameters used in the creation or
39 * reconfiguration of a superblock. 68 * reconfiguration of a superblock.
40 * 69 *
diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h
new file mode 100644
index 000000000000..d966f96ffe62
--- /dev/null
+++ b/include/linux/fs_parser.h
@@ -0,0 +1,151 @@
1/* Filesystem parameter description and parser
2 *
3 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#ifndef _LINUX_FS_PARSER_H
13#define _LINUX_FS_PARSER_H
14
15#include <linux/fs_context.h>
16
17struct path;
18
19struct constant_table {
20 const char *name;
21 int value;
22};
23
24/*
25 * The type of parameter expected.
26 */
27enum fs_parameter_type {
28 __fs_param_wasnt_defined,
29 fs_param_is_flag,
30 fs_param_is_bool,
31 fs_param_is_u32,
32 fs_param_is_u32_octal,
33 fs_param_is_u32_hex,
34 fs_param_is_s32,
35 fs_param_is_u64,
36 fs_param_is_enum,
37 fs_param_is_string,
38 fs_param_is_blob,
39 fs_param_is_blockdev,
40 fs_param_is_path,
41 fs_param_is_fd,
42 nr__fs_parameter_type,
43};
44
45/*
46 * Specification of the type of value a parameter wants.
47 *
48 * Note that the fsparam_flag(), fsparam_string(), fsparam_u32(), ... macros
49 * should be used to generate elements of this type.
50 */
51struct fs_parameter_spec {
52 const char *name;
53 u8 opt; /* Option number (returned by fs_parse()) */
54 enum fs_parameter_type type:8; /* The desired parameter type */
55 unsigned short flags;
56#define fs_param_v_optional 0x0001 /* The value is optional */
57#define fs_param_neg_with_no 0x0002 /* "noxxx" is negative param */
58#define fs_param_neg_with_empty 0x0004 /* "xxx=" is negative param */
59#define fs_param_deprecated 0x0008 /* The param is deprecated */
60};
61
62struct fs_parameter_enum {
63 u8 opt; /* Option number (as fs_parameter_spec::opt) */
64 char name[14];
65 u8 value;
66};
67
68struct fs_parameter_description {
69 const char name[16]; /* Name for logging purposes */
70 const struct fs_parameter_spec *specs; /* List of param specifications */
71 const struct fs_parameter_enum *enums; /* Enum values */
72};
73
74/*
75 * Result of parse.
76 */
77struct fs_parse_result {
78 bool negated; /* T if param was "noxxx" */
79 bool has_value; /* T if value supplied to param */
80 union {
81 bool boolean; /* For spec_bool */
82 int int_32; /* For spec_s32/spec_enum */
83 unsigned int uint_32; /* For spec_u32{,_octal,_hex}/spec_enum */
84 u64 uint_64; /* For spec_u64 */
85 };
86};
87
88extern int fs_parse(struct fs_context *fc,
89 const struct fs_parameter_description *desc,
90 struct fs_parameter *value,
91 struct fs_parse_result *result);
92extern int fs_lookup_param(struct fs_context *fc,
93 struct fs_parameter *param,
94 bool want_bdev,
95 struct path *_path);
96
97extern int __lookup_constant(const struct constant_table tbl[], size_t tbl_size,
98 const char *name, int not_found);
99#define lookup_constant(t, n, nf) __lookup_constant(t, ARRAY_SIZE(t), (n), (nf))
100
101#ifdef CONFIG_VALIDATE_FS_PARSER
102extern bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
103 int low, int high, int special);
104extern bool fs_validate_description(const struct fs_parameter_description *desc);
105#else
106static inline bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size,
107 int low, int high, int special)
108{ return true; }
109static inline bool fs_validate_description(const struct fs_parameter_description *desc)
110{ return true; }
111#endif
112
113/*
114 * Parameter type, name, index and flags element constructors. Use as:
115 *
116 * fsparam_xxxx("foo", Opt_foo)
117 *
118 * If existing helpers are not enough, direct use of __fsparam() would
119 * work, but any such case is probably a sign that new helper is needed.
120 * Helpers will remain stable; low-level implementation may change.
121 */
122#define __fsparam(TYPE, NAME, OPT, FLAGS) \
123 { \
124 .name = NAME, \
125 .opt = OPT, \
126 .type = TYPE, \
127 .flags = FLAGS \
128 }
129
130#define fsparam_flag(NAME, OPT) __fsparam(fs_param_is_flag, NAME, OPT, 0)
131#define fsparam_flag_no(NAME, OPT) \
132 __fsparam(fs_param_is_flag, NAME, OPT, \
133 fs_param_neg_with_no)
134#define fsparam_bool(NAME, OPT) __fsparam(fs_param_is_bool, NAME, OPT, 0)
135#define fsparam_u32(NAME, OPT) __fsparam(fs_param_is_u32, NAME, OPT, 0)
136#define fsparam_u32oct(NAME, OPT) \
137 __fsparam(fs_param_is_u32_octal, NAME, OPT, 0)
138#define fsparam_u32hex(NAME, OPT) \
139 __fsparam(fs_param_is_u32_hex, NAME, OPT, 0)
140#define fsparam_s32(NAME, OPT) __fsparam(fs_param_is_s32, NAME, OPT, 0)
141#define fsparam_u64(NAME, OPT) __fsparam(fs_param_is_u64, NAME, OPT, 0)
142#define fsparam_enum(NAME, OPT) __fsparam(fs_param_is_enum, NAME, OPT, 0)
143#define fsparam_string(NAME, OPT) \
144 __fsparam(fs_param_is_string, NAME, OPT, 0)
145#define fsparam_blob(NAME, OPT) __fsparam(fs_param_is_blob, NAME, OPT, 0)
146#define fsparam_bdev(NAME, OPT) __fsparam(fs_param_is_blockdev, NAME, OPT, 0)
147#define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0)
148#define fsparam_fd(NAME, OPT) __fsparam(fs_param_is_fd, NAME, OPT, 0)
149
150
151#endif /* _LINUX_FS_PARSER_H */