xref: /openbsd/lib/libfuse/fuse_opt.c (revision 9b7c3dbb)
1 /* $OpenBSD: fuse_opt.c,v 1.16 2016/05/24 20:55:32 okan 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 	for (i = 0; i < argc; i++)
34 		free(argv[i]);
35 	free(argv);
36 }
37 
38 static int
39 alloc_argv(struct fuse_args *args)
40 {
41 	char **argv;
42 	int i;
43 
44 	assert(!args->allocated);
45 
46 	argv = calloc(args->argc, sizeof(*argv));
47 	if (argv == NULL)
48 		return (-1);
49 
50 	if (args->argv) {
51 		for (i = 0; i < args->argc; i++) {
52 			argv[i] = strdup(args->argv[i]);
53 			if (argv[i] == NULL) {
54 				free_argv(argv, i + 1);
55 				return (-1);
56 			}
57 		}
58 	}
59 
60 	args->allocated = 1;
61 	args->argv = argv;
62 
63 	return (0);
64 }
65 
66 static int
67 match_opt(const char *templ, const char *opt)
68 {
69 	const char *o, *t;
70 	char *arg;
71 
72 	arg = strpbrk(templ, " =");
73 
74 	/* verify template */
75 	t = templ;
76 	if (*t == '-') {
77 		t++;
78 		if (*t == '-')
79 			t++;
80 		if (*t == 'o' || *t == '\0')
81 			return (0);
82 	}
83 
84 	/* skip leading -, -o, and -- in option name */
85 	o = opt;
86 	if (*o == '-') {
87 		o++;
88 		if (*o == 'o' || *o == '-')
89 			o++;
90 	}
91 
92 	/* empty option name is invalid */
93 	if (*o == '\0')
94 		return (0);
95 
96 	/* match option name */
97 	while (*t && *o) {
98 		if (*t++ != *o++)
99 			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 }
132 
133 static int
134 add_opt(char **opts, const char *opt)
135 {
136 	char *new_opts;
137 
138 	if (*opts == NULL) {
139 		*opts = strdup(opt);
140 		if (*opts == NULL)
141 			return (-1);
142 		return (0);
143 	}
144 
145 	if (asprintf(&new_opts, "%s,%s", *opts, opt) == -1)
146 		return (-1);
147 
148 	free(*opts);
149 	*opts = new_opts;
150 	return (0);
151 }
152 
153 int
154 fuse_opt_add_opt(char **opts, const char *opt)
155 {
156 	int ret;
157 
158 	if (opt == NULL || opt[0] == '\0')
159 		return (-1);
160 
161 	ret = add_opt(opts, opt);
162 	return (ret);
163 }
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 	if (opt == NULL || opt[0] == '\0')
174 		return (-1);
175 
176 	while (*s) {
177 		/* malloc(size + escaped) overflow check */
178 		if (size >= (SIZE_MAX / 2))
179 			return (-1);
180 
181 		if (*s == ',' || *s == '\\')
182 			escaped++;
183 		s++;
184 		size++;
185 	}
186 
187 	if (escaped > 0) {
188 		escaped_opt = malloc(size + escaped);
189 		if (escaped_opt == NULL)
190 			return (-1);
191 		s = opt;
192 		p = escaped_opt;
193 		while (*s) {
194 			switch (*s) {
195 			case ',':
196 			case '\\':
197 				*p++ = '\\';
198 				/* FALLTHROUGH */
199 			default:
200 				*p++ = *s++;
201 			}
202 		}
203 		*p = '\0';
204 	} else {
205 		escaped_opt = strdup(opt);
206 		if (escaped_opt == NULL)
207 			return (-1);
208 	}
209 
210 	ret = add_opt(opts, escaped_opt);
211 	free(escaped_opt);
212 	return (ret);
213 }
214 
215 int
216 fuse_opt_add_arg(struct fuse_args *args, const char *name)
217 {
218 	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 	if (name == NULL)
348 		return (-1);
349 
350 	if (!args->allocated && alloc_argv(args))
351 		return (-1);
352 
353 	if (p < 0 || p > args->argc)
354 		return (-1);
355 
356 	av = reallocarray(args->argv, args->argc + 2, sizeof(*av));
357 	if (av == NULL)
358 		return (-1);
359 
360 	this_arg = strdup(name);
361 	if (this_arg == NULL) {
362 		free(av);
363 		return (-1);
364 	}
365 
366 	args->argc++;
367 	args->argv = av;
368 	args->argv[args->argc] = NULL;
369 	for (i = p; i < args->argc; i++) {
370 		next_arg = args->argv[i];
371 		args->argv[i] = this_arg;
372 		this_arg = next_arg;
373 	}
374 	return (0);
375 }
376 
377 void
378 fuse_opt_free_args(struct fuse_args *args)
379 {
380 	if (!args->allocated)
381 		return;
382 
383 	free_argv(args->argv, args->argc);
384 	args->argv = 0;
385 	args->argc = 0;
386 	args->allocated = 0;
387 }
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 	if (opt == NULL || opt[0] == '\0')
395 		return (0);
396 
397 	while (this_opt->templ) {
398 		if (match_opt(this_opt->templ, opt))
399 			return (1);
400 		this_opt++;
401 	}
402 
403 	return (0);
404 }
405