1 /* $OpenBSD: fuse_opt.c,v 1.27 2022/01/16 20:06:18 naddy 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 #define IFUSE_OPT_DISCARD 0
29 #define IFUSE_OPT_KEEP 1
30 #define IFUSE_OPT_NEED_ANOTHER_ARG 2
31
32 static void
free_argv(char ** argv,int argc)33 free_argv(char **argv, int argc)
34 {
35 int i;
36
37 for (i = 0; i < argc; i++)
38 free(argv[i]);
39 free(argv);
40 }
41
42 static int
alloc_argv(struct fuse_args * args)43 alloc_argv(struct fuse_args *args)
44 {
45 char **argv;
46 int i;
47
48 assert(!args->allocated);
49
50 argv = calloc(args->argc, sizeof(*argv));
51 if (argv == NULL)
52 return (-1);
53
54 if (args->argv) {
55 for (i = 0; i < args->argc; i++) {
56 argv[i] = strdup(args->argv[i]);
57 if (argv[i] == NULL) {
58 free_argv(argv, i + 1);
59 return (-1);
60 }
61 }
62 }
63
64 args->allocated = 1;
65 args->argv = argv;
66
67 return (0);
68 }
69
70 /*
71 * Returns the number of characters that matched for bounds checking later.
72 */
73 static size_t
match_opt(const char * templ,const char * opt)74 match_opt(const char *templ, const char *opt)
75 {
76 size_t sep, len;
77
78 len = strlen(templ);
79 sep = strcspn(templ, "=");
80
81 if (sep == len)
82 sep = strcspn(templ, " ");
83
84 /* key=, key=%, "-k ", -k % */
85 if (sep < len && (templ[sep + 1] == '\0' || templ[sep + 1] == '%')) {
86 if (strncmp(opt, templ, sep) == 0)
87 return (sep);
88 else
89 return (0);
90 }
91
92 if (strcmp(opt, templ) == 0)
93 return (len);
94
95 return (0);
96 }
97
98 static int
add_opt(char ** opts,const char * opt)99 add_opt(char **opts, const char *opt)
100 {
101 char *new_opts;
102
103 if (*opts == NULL) {
104 *opts = strdup(opt);
105 if (*opts == NULL)
106 return (-1);
107 return (0);
108 }
109
110 if (asprintf(&new_opts, "%s,%s", *opts, opt) == -1)
111 return (-1);
112
113 free(*opts);
114 *opts = new_opts;
115 return (0);
116 }
117
118 int
fuse_opt_add_opt(char ** opts,const char * opt)119 fuse_opt_add_opt(char **opts, const char *opt)
120 {
121 int ret;
122
123 if (opt == NULL || opt[0] == '\0')
124 return (-1);
125
126 ret = add_opt(opts, opt);
127 return (ret);
128 }
129
130 int
fuse_opt_add_opt_escaped(char ** opts,const char * opt)131 fuse_opt_add_opt_escaped(char **opts, const char *opt)
132 {
133 size_t size = 0, escaped = 0;
134 const char *s = opt;
135 char *escaped_opt, *p;
136 int ret;
137
138 if (opt == NULL || opt[0] == '\0')
139 return (-1);
140
141 while (*s) {
142 /* malloc(size + escaped) overflow check */
143 if (size >= (SIZE_MAX / 2))
144 return (-1);
145
146 if (*s == ',' || *s == '\\')
147 escaped++;
148 s++;
149 size++;
150 }
151 size++; /* trailing NUL */
152
153 if (escaped > 0) {
154 escaped_opt = malloc(size + escaped);
155 if (escaped_opt == NULL)
156 return (-1);
157 s = opt;
158 p = escaped_opt;
159 while (*s) {
160 switch (*s) {
161 case ',':
162 case '\\':
163 *p++ = '\\';
164 /* FALLTHROUGH */
165 default:
166 *p++ = *s++;
167 }
168 }
169 *p = '\0';
170 } else {
171 escaped_opt = strdup(opt);
172 if (escaped_opt == NULL)
173 return (-1);
174 }
175
176 ret = add_opt(opts, escaped_opt);
177 free(escaped_opt);
178 return (ret);
179 }
180
181 int
fuse_opt_add_arg(struct fuse_args * args,const char * name)182 fuse_opt_add_arg(struct fuse_args *args, const char *name)
183 {
184 return (fuse_opt_insert_arg(args, args->argc, name));
185 }
186 DEF(fuse_opt_add_arg);
187
188 static int
parse_opt(const struct fuse_opt * o,const char * opt,void * data,fuse_opt_proc_t f,struct fuse_args * arg)189 parse_opt(const struct fuse_opt *o, const char *opt, void *data,
190 fuse_opt_proc_t f, struct fuse_args *arg)
191 {
192 const char *val;
193 int ret, found;
194 size_t sep;
195
196 found = 0;
197
198 for(; o != NULL && o->templ; o++) {
199 sep = match_opt(o->templ, opt);
200 if (sep == 0)
201 continue;
202
203 found = 1;
204 val = opt;
205
206 /* check key=value or -p n */
207 if (o->templ[sep] == '=')
208 val = &opt[sep + 1];
209 else if (o->templ[sep] == ' ') {
210 if (sep == strlen(opt)) {
211 /* ask for next arg to be included */
212 return (IFUSE_OPT_NEED_ANOTHER_ARG);
213 } else if (strchr(o->templ, '%') != NULL) {
214 val = &opt[sep];
215 }
216 }
217
218 if (o->val == FUSE_OPT_KEY_DISCARD)
219 ret = IFUSE_OPT_DISCARD;
220 else if (o->val == FUSE_OPT_KEY_KEEP)
221 ret = IFUSE_OPT_KEEP;
222 else if (FUSE_OPT_IS_OPT_KEY(o)) {
223 if (f == NULL)
224 return (IFUSE_OPT_KEEP);
225
226 ret = f(data, val, o->val, arg);
227 } else if (data == NULL) {
228 return (-1);
229 } else if (strchr(o->templ, '%') == NULL) {
230 *((int *)(data + o->off)) = o->val;
231 ret = IFUSE_OPT_DISCARD;
232 } else if (strstr(o->templ, "%s") != NULL) {
233 *((char **)(data + o->off)) = strdup(val);
234 ret = IFUSE_OPT_DISCARD;
235 } else {
236 /* All other templates, let sscanf deal with them. */
237 if (sscanf(opt, o->templ, data + o->off) != 1) {
238 fprintf(stderr, "fuse: Invalid value %s for "
239 "option %s\n", val, o->templ);
240 return (-1);
241 }
242 ret = IFUSE_OPT_DISCARD;
243 }
244 }
245
246 if (found)
247 return (ret);
248
249 if (f != NULL)
250 return f(data, opt, FUSE_OPT_KEY_OPT, arg);
251
252 return (IFUSE_OPT_KEEP);
253 }
254
255 /*
256 * this code is not very sexy but we are forced to follow
257 * the fuse api.
258 *
259 * when f() returns 1 we need to keep the arg
260 * when f() returns 0 we need to discard the arg
261 */
262 int
fuse_opt_parse(struct fuse_args * args,void * data,const struct fuse_opt * opt,fuse_opt_proc_t f)263 fuse_opt_parse(struct fuse_args *args, void *data,
264 const struct fuse_opt *opt, fuse_opt_proc_t f)
265 {
266 struct fuse_args outargs;
267 const char *arg, *ap;
268 char *optlist, *tofree;
269 int ret;
270 int i;
271
272 if (!args || !args->argc || !args->argv)
273 return (0);
274
275 memset(&outargs, 0, sizeof(outargs));
276 fuse_opt_add_arg(&outargs, args->argv[0]);
277
278 for (i = 1; i < args->argc; i++) {
279 arg = args->argv[i];
280 ret = 0;
281
282 /* not - and not -- */
283 if (arg[0] != '-') {
284 if (f == NULL)
285 ret = IFUSE_OPT_KEEP;
286 else
287 ret = f(data, arg, FUSE_OPT_KEY_NONOPT, &outargs);
288
289 if (ret == IFUSE_OPT_KEEP)
290 fuse_opt_add_arg(&outargs, arg);
291 if (ret == -1)
292 goto err;
293 } else if (arg[1] == 'o') {
294 if (arg[2])
295 arg += 2; /* -ofoo,bar */
296 else {
297 if (++i >= args->argc)
298 goto err;
299
300 arg = args->argv[i];
301 }
302
303 tofree = optlist = strdup(arg);
304 if (optlist == NULL)
305 goto err;
306
307 while ((ap = strsep(&optlist, ",")) != NULL &&
308 ret != -1) {
309 ret = parse_opt(opt, ap, data, f, &outargs);
310 if (ret == IFUSE_OPT_KEEP) {
311 fuse_opt_add_arg(&outargs, "-o");
312 fuse_opt_add_arg(&outargs, ap);
313 }
314 }
315
316 free(tofree);
317
318 if (ret == -1)
319 goto err;
320 } else {
321 ret = parse_opt(opt, arg, data, f, &outargs);
322
323 if (ret == IFUSE_OPT_KEEP)
324 fuse_opt_add_arg(&outargs, arg);
325 else if (ret == IFUSE_OPT_NEED_ANOTHER_ARG) {
326 /* arg needs a value */
327 if (++i >= args->argc) {
328 fprintf(stderr, "fuse: missing argument after %s\n", arg);
329 goto err;
330 }
331
332 if (asprintf(&tofree, "%s%s", arg,
333 args->argv[i]) == -1)
334 goto err;
335
336 ret = parse_opt(opt, tofree, data, f, &outargs);
337 if (ret == IFUSE_OPT_KEEP)
338 fuse_opt_add_arg(&outargs, tofree);
339 free(tofree);
340 }
341
342 if (ret == -1)
343 goto err;
344 }
345 }
346 ret = 0;
347
348 err:
349 /* Update args */
350 fuse_opt_free_args(args);
351 args->allocated = outargs.allocated;
352 args->argc = outargs.argc;
353 args->argv = outargs.argv;
354 if (ret != 0)
355 ret = -1;
356
357 return (ret);
358 }
359 DEF(fuse_opt_parse);
360
361 int
fuse_opt_insert_arg(struct fuse_args * args,int p,const char * name)362 fuse_opt_insert_arg(struct fuse_args *args, int p, const char *name)
363 {
364 char **av;
365 char *this_arg, *next_arg;
366 int i;
367
368 if (name == NULL)
369 return (-1);
370
371 if (!args->allocated && alloc_argv(args))
372 return (-1);
373
374 if (p < 0 || p > args->argc)
375 return (-1);
376
377 av = reallocarray(args->argv, args->argc + 2, sizeof(*av));
378 if (av == NULL)
379 return (-1);
380
381 this_arg = strdup(name);
382 if (this_arg == NULL) {
383 free(av);
384 return (-1);
385 }
386
387 args->argc++;
388 args->argv = av;
389 args->argv[args->argc] = NULL;
390 for (i = p; i < args->argc; i++) {
391 next_arg = args->argv[i];
392 args->argv[i] = this_arg;
393 this_arg = next_arg;
394 }
395 return (0);
396 }
397 DEF(fuse_opt_insert_arg);
398
399 void
fuse_opt_free_args(struct fuse_args * args)400 fuse_opt_free_args(struct fuse_args *args)
401 {
402 if (!args->allocated)
403 return;
404
405 free_argv(args->argv, args->argc);
406 args->argv = 0;
407 args->argc = 0;
408 args->allocated = 0;
409 }
410 DEF(fuse_opt_free_args);
411
412 int
fuse_opt_match(const struct fuse_opt * opts,const char * opt)413 fuse_opt_match(const struct fuse_opt *opts, const char *opt)
414 {
415 const struct fuse_opt *this_opt = opts;
416
417 if (opt == NULL || opt[0] == '\0')
418 return (0);
419
420 while (this_opt->templ) {
421 if (match_opt(this_opt->templ, opt))
422 return (1);
423 this_opt++;
424 }
425
426 return (0);
427 }
428 DEF(fuse_opt_match);
429