File Index
Files and directories
Sign in
FILE

fs_parser.c

File preview with syntax highlighting.

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