xref: /openbsd-src/lib/libfuse/fuse_opt.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /* $OpenBSD: fuse_opt.c,v 1.10 2014/05/20 13:22:06 syl 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 <stdlib.h>
21 #include <string.h>
22 
23 #include "debug.h"
24 #include "fuse_opt.h"
25 #include "fuse_private.h"
26 
27 static void
28 free_argv(char **argv, int argc)
29 {
30 	int i;
31 
32 	for (i = 0; i < argc; i++)
33 		free(argv[i]);
34 	free(argv);
35 }
36 
37 static int
38 alloc_argv(struct fuse_args *args)
39 {
40 	char **argv;
41 	int i;
42 
43 	assert(!args->allocated);
44 
45 	argv = calloc(args->argc, sizeof(*argv));
46 	if (argv == NULL)
47 		return (-1);
48 
49 	if (args->argv) {
50 		for (i = 0; i < args->argc; i++) {
51 			argv[i] = strdup(args->argv[i]);
52 			if (argv[i] == NULL) {
53 				free_argv(argv, i + 1);
54 				return (-1);
55 			}
56 		}
57 	}
58 
59 	args->allocated = 1;
60 	args->argv = argv;
61 
62 	return (0);
63 }
64 
65 static int
66 match_opt(const char *templ, const char *opt)
67 {
68 	const char *o, *t;
69 	char *arg;
70 
71 	arg = strpbrk(templ, " =");
72 
73 	/* verify template */
74 	t = templ;
75 	if (*t == '-') {
76 		t++;
77 		if (*t == '-')
78 			t++;
79 		if (*t == 'o' || t == '\0')
80 			return (0);
81 	}
82 
83 	/* skip leading -, -o, and -- in option name */
84 	o = opt;
85 	if (*o == '-') {
86 		o++;
87 		if (*o == 'o' || *o == '-')
88 			o++;
89 	}
90 
91 	/* empty option name is invalid */
92 	if (*o == '\0')
93 		return (0);
94 
95 	/* match option name */
96 	while (*t && *o) {
97 		if (*t++ != *o++)
98 			return (0);
99 		if (arg && t == arg) {
100 			if (*o != ' ' && *o != '=')
101 				return (0);
102 			o++; /* o now points at argument */
103 			if (*o == '\0')
104 				return (0);
105 			break;
106 		}
107 	}
108 
109 	/* match argument */
110 	if (arg) {
111 		if (t != arg)
112 			return (0);
113 		t++;
114 		/* does template have an argument? */
115 		if (*t != '%' && *t != '\0')
116 			return (0);
117 		if (*t == '%' && t[1] == '\0')
118 			return (0);
119 		/* yes it does, consume argument in opt */
120 		while (*o && *o != ' ')
121 			o++;
122 	} else if (*t != '\0')
123 		return (0);
124 
125 	/* we should have consumed entire opt string */
126 	if (*o != '\0')
127 		return (0);
128 
129 	return (1);
130 }
131 
132 static int
133 add_opt(char **opts, const char *opt)
134 {
135 	char *new_opts;
136 
137 	if (*opts == NULL) {
138 		*opts = strdup(opt);
139 		if (*opts == NULL)
140 			return (-1);
141 		return (0);
142 	}
143 
144 	if (asprintf(&new_opts, "%s,%s", *opts, opt) == -1)
145 		return (-1);
146 
147 	free(*opts);
148 	*opts = new_opts;
149 	return (0);
150 }
151 
152 int
153 fuse_opt_add_opt(char **opts, const char *opt)
154 {
155 	int ret;
156 
157 	if (opt == NULL || opt[0] == '\0')
158 		return (-1);
159 
160 	ret = add_opt(opts, opt);
161 	return (ret);
162 }
163 
164 int
165 fuse_opt_add_opt_escaped(char **opts, const char *opt)
166 {
167 	size_t size = 0, escaped = 0;
168 	const char *s = opt;
169 	char *escaped_opt, *p;
170 	int ret;
171 
172 	if (opt == NULL || opt[0] == '\0')
173 		return (-1);
174 
175 	while (*s) {
176 		/* malloc(size + escaped) overflow check */
177 		if (size >= (SIZE_T_MAX / 2))
178 			return (-1);
179 
180 		if (*s == ',' || *s == '\\')
181 			escaped++;
182 		s++;
183 		size++;
184 	}
185 
186 	if (escaped > 0) {
187 		escaped_opt = malloc(size + escaped);
188 		if (escaped_opt == NULL)
189 			return (-1);
190 		s = opt;
191 		p = escaped_opt;
192 		while (*s) {
193 			switch (*s) {
194 			case ',':
195 			case '\\':
196 				*p++ = '\\';
197 				/* FALLTHROUGH */
198 			default:
199 				*p++ = *s++;
200 			}
201 		}
202 		*p = '\0';
203 	} else {
204 		escaped_opt = strdup(opt);
205 		if (escaped_opt == NULL)
206 			return (-1);
207 	}
208 
209 	ret = add_opt(opts, escaped_opt);
210 	free(escaped_opt);
211 	return (ret);
212 }
213 
214 int
215 fuse_opt_add_arg(struct fuse_args *args, const char *name)
216 {
217 	return (fuse_opt_insert_arg(args, args->argc, name));
218 }
219 
220 static int
221 parse_opt(const struct fuse_opt *o, const char *val, void *data,
222     fuse_opt_proc_t f, struct fuse_args *arg)
223 {
224 	int found, ret, keyval;
225 	size_t idx;
226 
227 	ret = 0;
228 	found = 0;
229 	keyval = 0;
230 
231 	/* check if it is a key=value entry */
232 	idx = strcspn(val, "=");
233 	if (idx != strlen(val)) {
234 		idx++;
235 		keyval = 1;
236 	}
237 
238 	for(; o->templ; o++) {
239 		if ((keyval && strncmp(val, o->templ, idx) == 0) ||
240 		    (!keyval && strcmp(val, o->templ) == 0)) {
241 			if (o->val == FUSE_OPT_KEY_DISCARD)
242 				return (1);
243 
244 			if (FUSE_OPT_IS_OPT_KEY(o)) {
245 				if (keyval)
246 					ret = f(data, &val[idx], o->val, arg);
247 				else
248 					ret = f(data, val, o->val, arg);
249 			}
250 
251 			if (ret == -1)
252 				return (ret);
253 			if (ret == 1)
254 				fuse_opt_add_arg(arg, val);
255 			found++;
256 			break;
257 		}
258 	}
259 
260 	if (!found) {
261 		printf("fuse: unknown option %s\n", val);
262 		return (-1);
263 	}
264 
265 	return (ret);
266 }
267 
268 /*
269  * this code is not very sexy but we are forced to follow
270  * the fuse api.
271  *
272  * when f() returns 1 we need to keep the arg
273  * when f() returns 0 we need to discard the arg
274  */
275 int
276 fuse_opt_parse(struct fuse_args *args, void *data,
277     const struct fuse_opt *opt, fuse_opt_proc_t f)
278 {
279 	struct fuse_args outargs;
280 	const char *arg;
281 	int ret = 0;
282 	int i;
283 
284 	if (!f || !args || !args->argc || !args->argv)
285 		return (0);
286 
287 	bzero(&outargs, sizeof(outargs));
288 	fuse_opt_add_arg(&outargs, args->argv[0]);
289 
290 	for (i = 1; i < args->argc; i++) {
291 		arg = args->argv[i];
292 
293 		/* not - and not -- */
294 		if (arg[0] != '-') {
295 			ret = f(data, arg, FUSE_OPT_KEY_NONOPT, 0);
296 
297 			if (ret == 1)
298 				fuse_opt_add_arg(&outargs, arg);
299 			if (ret == -1)
300 				goto err;
301 		} else if (arg[1] == 'o') {
302 			if (arg[2])
303 				arg += 2;	/* -ofoo,bar */
304 			else
305 				arg = args->argv[++i];
306 
307 			ret = parse_opt(opt, arg, data, f, &outargs);
308 
309 			if (ret == -1)
310 				goto err;
311 		} else {
312 			ret = parse_opt(opt, arg, data, f, &outargs);
313 
314 			if (ret == -1)
315 				goto err;
316 		}
317 	}
318 	ret = 0;
319 
320 err:
321 	/* Update args */
322 	fuse_opt_free_args(args);
323 	args->allocated = outargs.allocated;
324 	args->argc = outargs.argc;
325 	args->argv = outargs.argv;
326 
327 	return (ret);
328 }
329 
330 int
331 fuse_opt_insert_arg(struct fuse_args *args, int p, const char *name)
332 {
333 	char **av;
334 	char *this_arg, *next_arg;
335 	int i;
336 
337 	if (name == NULL)
338 		return (-1);
339 
340 	if (!args->allocated && alloc_argv(args))
341 		return (-1);
342 
343 	if (p < 0 || p > args->argc)
344 		return (-1);
345 
346 	av = realloc(args->argv, (args->argc + 1) * sizeof(*av));
347 	if (av == NULL)
348 		return (-1);
349 
350 	this_arg = strdup(name);
351 	if (this_arg == NULL) {
352 		free(av);
353 		return (-1);
354 	}
355 
356 	args->argc++;
357 	args->argv = av;
358 	for (i = p; i < args->argc; i++) {
359 		next_arg = args->argv[i];
360 		args->argv[i] = this_arg;
361 		this_arg = next_arg;
362 	}
363 	return (0);
364 }
365 
366 void
367 fuse_opt_free_args(struct fuse_args *args)
368 {
369 	if (!args->allocated)
370 		return;
371 
372 	free_argv(args->argv, args->argc);
373 	args->argv = 0;
374 	args->argc = 0;
375 	args->allocated = 0;
376 }
377 
378 int
379 fuse_opt_match(const struct fuse_opt *opts, const char *opt)
380 {
381 	const struct fuse_opt *this_opt = opts;
382 
383 	if (opt == NULL || opt[0] == '\0')
384 		return (0);
385 
386 	while (this_opt->templ) {
387 		if (match_opt(this_opt->templ, opt))
388 			return (1);
389 		this_opt++;
390 	}
391 
392 	return (0);
393 }
394