GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libfuse/fuse_opt.c Lines: 99 188 52.7 %
Date: 2017-11-13 Branches: 69 175 39.4 %

Line Branch Exec Source
1
/* $OpenBSD: fuse_opt.c,v 1.18 2017/01/04 12:01:22 stsp Exp $ */
2
/*
3
 * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
4
 * Copyright (c) 2013 Stefan Sperling <stsp@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <assert.h>
20
#include <stdint.h>
21
#include <stdlib.h>
22
#include <string.h>
23
24
#include "debug.h"
25
#include "fuse_opt.h"
26
#include "fuse_private.h"
27
28
static void
29
free_argv(char **argv, int argc)
30
{
31
	int i;
32
33
90
	for (i = 0; i < argc; i++)
34
36
		free(argv[i]);
35
6
	free(argv);
36
6
}
37
38
static int
39
alloc_argv(struct fuse_args *args)
40
{
41
	char **argv;
42
	int i;
43
44
12
	assert(!args->allocated);
45
46
6
	argv = calloc(args->argc, sizeof(*argv));
47
6
	if (argv == NULL)
48
		return (-1);
49
50
6
	if (args->argv) {
51
24
		for (i = 0; i < args->argc; i++) {
52
6
			argv[i] = strdup(args->argv[i]);
53
6
			if (argv[i] == NULL) {
54
				free_argv(argv, i + 1);
55
				return (-1);
56
			}
57
		}
58
	}
59
60
6
	args->allocated = 1;
61
6
	args->argv = argv;
62
63
6
	return (0);
64
6
}
65
66
static int
67
match_opt(const char *templ, const char *opt)
68
{
69
	const char *o, *t;
70
	char *arg;
71
72
180
	arg = strpbrk(templ, " =");
73
74
	/* verify template */
75
	t = templ;
76
90
	if (*t == '-') {
77
90
		t++;
78
90
		if (*t == '-')
79
18
			t++;
80

180
		if (*t == 'o' || *t == '\0')
81
			return (0);
82
	}
83
84
	/* skip leading -, -o, and -- in option name */
85
	o = opt;
86
90
	if (*o == '-') {
87
60
		o++;
88

120
		if (*o == 'o' || *o == '-')
89
30
			o++;
90
	}
91
92
	/* empty option name is invalid */
93
90
	if (*o == '\0')
94
		return (0);
95
96
	/* match option name */
97

270
	while (*t && *o) {
98
90
		if (*t++ != *o++)
99
90
			return (0);
100
		if (arg && t == arg) {
101
			if (*o != ' ' && *o != '=')
102
				return (0);
103
			o++; /* o now points at argument */
104
			if (*o == '\0')
105
				return (0);
106
			break;
107
		}
108
	}
109
110
	/* match argument */
111
	if (arg) {
112
		if (t != arg)
113
			return (0);
114
		t++;
115
		/* does template have an argument? */
116
		if (*t != '%' && *t != '\0')
117
			return (0);
118
		if (*t == '%' && t[1] == '\0')
119
			return (0);
120
		/* yes it does, consume argument in opt */
121
		while (*o && *o != ' ')
122
			o++;
123
	} else if (*t != '\0')
124
		return (0);
125
126
	/* we should have consumed entire opt string */
127
	if (*o != '\0')
128
		return (0);
129
130
	return (1);
131
90
}
132
133
static int
134
add_opt(char **opts, const char *opt)
135
{
136
72
	char *new_opts;
137
138
36
	if (*opts == NULL) {
139
6
		*opts = strdup(opt);
140
6
		if (*opts == NULL)
141
			return (-1);
142
6
		return (0);
143
	}
144
145
30
	if (asprintf(&new_opts, "%s,%s", *opts, opt) == -1)
146
		return (-1);
147
148
30
	free(*opts);
149
30
	*opts = new_opts;
150
30
	return (0);
151
36
}
152
153
int
154
fuse_opt_add_opt(char **opts, const char *opt)
155
{
156
	int ret;
157
158

78
	if (opt == NULL || opt[0] == '\0')
159
6
		return (-1);
160
161
21
	ret = add_opt(opts, opt);
162
21
	return (ret);
163
27
}
164
165
int
166
fuse_opt_add_opt_escaped(char **opts, const char *opt)
167
{
168
	size_t size = 0, escaped = 0;
169
	const char *s = opt;
170
	char *escaped_opt, *p;
171
	int ret;
172
173

60
	if (opt == NULL || opt[0] == '\0')
174
6
		return (-1);
175
176
186
	while (*s) {
177
		/* malloc(size + escaped) overflow check */
178
78
		if (size >= (SIZE_MAX / 2))
179
			return (-1);
180
181

129
		if (*s == ',' || *s == '\\')
182
39
			escaped++;
183
78
		s++;
184
78
		size++;
185
	}
186
187
15
	if (escaped > 0) {
188
6
		escaped_opt = malloc(size + escaped);
189
6
		if (escaped_opt == NULL)
190
			return (-1);
191
		s = opt;
192
		p = escaped_opt;
193
108
		while (*s) {
194
87
			switch (*s) {
195
			case ',':
196
			case '\\':
197
39
				*p++ = '\\';
198
				/* FALLTHROUGH */
199
			default:
200
48
				*p++ = *s++;
201
			}
202
		}
203
6
		*p = '\0';
204
6
	} else {
205
9
		escaped_opt = strdup(opt);
206
9
		if (escaped_opt == NULL)
207
			return (-1);
208
	}
209
210
15
	ret = add_opt(opts, escaped_opt);
211
15
	free(escaped_opt);
212
15
	return (ret);
213
21
}
214
215
int
216
fuse_opt_add_arg(struct fuse_args *args, const char *name)
217
{
218
36
	return (fuse_opt_insert_arg(args, args->argc, name));
219
}
220
221
static int
222
parse_opt(const struct fuse_opt *o, const char *val, void *data,
223
    fuse_opt_proc_t f, struct fuse_args *arg)
224
{
225
	int found, ret, keyval;
226
	size_t idx;
227
228
	ret = 0;
229
	found = 0;
230
	keyval = 0;
231
232
	/* check if it is a key=value entry */
233
	idx = strcspn(val, "=");
234
	if (idx != strlen(val)) {
235
		idx++;
236
		keyval = 1;
237
	}
238
239
	for(; o->templ; o++) {
240
		if ((keyval && strncmp(val, o->templ, idx) == 0) ||
241
		    (!keyval && strcmp(val, o->templ) == 0)) {
242
			if (o->val == FUSE_OPT_KEY_DISCARD)
243
				return (1);
244
245
			if (FUSE_OPT_IS_OPT_KEY(o)) {
246
				if (keyval)
247
					ret = f(data, &val[idx], o->val, arg);
248
				else
249
					ret = f(data, val, o->val, arg);
250
			}
251
252
			if (o->off != ULONG_MAX && data && o->val >= 0) {
253
				ret = f(data, val, o->val, arg);
254
				int *addr = (int *)(data + o->off);
255
				*addr = o->val;
256
				ret = 0;
257
			}
258
259
			if (ret == -1)
260
				return (ret);
261
			if (ret == 1)
262
				fuse_opt_add_arg(arg, val);
263
			found++;
264
			break;
265
		}
266
	}
267
268
	if (!found) {
269
		ret = f(data, val, FUSE_OPT_KEY_OPT, arg);
270
		if (ret == 1)
271
			fuse_opt_add_arg(arg, val);
272
		return (ret);
273
	}
274
275
	return (ret);
276
}
277
278
/*
279
 * this code is not very sexy but we are forced to follow
280
 * the fuse api.
281
 *
282
 * when f() returns 1 we need to keep the arg
283
 * when f() returns 0 we need to discard the arg
284
 */
285
int
286
fuse_opt_parse(struct fuse_args *args, void *data,
287
    const struct fuse_opt *opt, fuse_opt_proc_t f)
288
{
289
	struct fuse_args outargs;
290
	const char *arg;
291
	int ret = 0;
292
	int i;
293
294
	if (!f || !args || !args->argc || !args->argv)
295
		return (0);
296
297
	bzero(&outargs, sizeof(outargs));
298
	fuse_opt_add_arg(&outargs, args->argv[0]);
299
300
	for (i = 1; i < args->argc; i++) {
301
		arg = args->argv[i];
302
303
		/* not - and not -- */
304
		if (arg[0] != '-') {
305
			ret = f(data, arg, FUSE_OPT_KEY_NONOPT, &outargs);
306
307
			if (ret == 1)
308
				fuse_opt_add_arg(&outargs, arg);
309
			if (ret == -1)
310
				goto err;
311
		} else if (arg[1] == 'o') {
312
			if (arg[2])
313
				arg += 2;	/* -ofoo,bar */
314
			else
315
				arg = args->argv[++i];
316
317
			ret = parse_opt(opt, arg, data, f, &outargs);
318
319
			if (ret == -1)
320
				goto err;
321
		} else {
322
			ret = parse_opt(opt, arg, data, f, &outargs);
323
324
			if (ret == -1)
325
				goto err;
326
		}
327
	}
328
	ret = 0;
329
330
err:
331
	/* Update args */
332
	fuse_opt_free_args(args);
333
	args->allocated = outargs.allocated;
334
	args->argc = outargs.argc;
335
	args->argv = outargs.argv;
336
337
	return (ret);
338
}
339
340
int
341
fuse_opt_insert_arg(struct fuse_args *args, int p, const char *name)
342
{
343
	char **av;
344
	char *this_arg, *next_arg;
345
	int i;
346
347
84
	if (name == NULL)
348
6
		return (-1);
349
350

42
	if (!args->allocated && alloc_argv(args))
351
		return (-1);
352
353

69
	if (p < 0 || p > args->argc)
354
6
		return (-1);
355
356
30
	av = reallocarray(args->argv, args->argc + 2, sizeof(*av));
357
30
	if (av == NULL)
358
		return (-1);
359
360
30
	this_arg = strdup(name);
361
30
	if (this_arg == NULL) {
362
		free(av);
363
		return (-1);
364
	}
365
366
30
	args->argc++;
367
30
	args->argv = av;
368
30
	args->argv[args->argc] = NULL;
369
138
	for (i = p; i < args->argc; i++) {
370
39
		next_arg = args->argv[i];
371
39
		args->argv[i] = this_arg;
372
		this_arg = next_arg;
373
	}
374
30
	return (0);
375
42
}
376
377
void
378
fuse_opt_free_args(struct fuse_args *args)
379
{
380
12
	if (!args->allocated)
381
		return;
382
383
6
	free_argv(args->argv, args->argc);
384
6
	args->argv = 0;
385
6
	args->argc = 0;
386
6
	args->allocated = 0;
387
12
}
388
389
int
390
fuse_opt_match(const struct fuse_opt *opts, const char *opt)
391
{
392
	const struct fuse_opt *this_opt = opts;
393
394

120
	if (opt == NULL || opt[0] == '\0')
395
6
		return (0);
396
397
252
	while (this_opt->templ) {
398
90
		if (match_opt(this_opt->templ, opt))
399
			return (1);
400
90
		this_opt++;
401
	}
402
403
36
	return (0);
404
42
}