jcs's openbsd hax
openbsd
1/* $OpenBSD: fuse_opt.c,v 1.28 2025/12/08 06:37:05 helg 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
32static void
33free_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
42static int
43alloc_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 */
73static size_t
74match_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
98static int
99add_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
118int
119fuse_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}
129DEF(fuse_opt_add_opt);
130
131int
132fuse_opt_add_opt_escaped(char **opts, const char *opt)
133{
134 size_t size = 0, escaped = 0;
135 const char *s = opt;
136 char *escaped_opt, *p;
137 int ret;
138
139 if (opt == NULL || opt[0] == '\0')
140 return (-1);
141
142 while (*s) {
143 /* malloc(size + escaped) overflow check */
144 if (size >= (SIZE_MAX / 2))
145 return (-1);
146
147 if (*s == ',' || *s == '\\')
148 escaped++;
149 s++;
150 size++;
151 }
152 size++; /* trailing NUL */
153
154 if (escaped > 0) {
155 escaped_opt = malloc(size + escaped);
156 if (escaped_opt == NULL)
157 return (-1);
158 s = opt;
159 p = escaped_opt;
160 while (*s) {
161 switch (*s) {
162 case ',':
163 case '\\':
164 *p++ = '\\';
165 /* FALLTHROUGH */
166 default:
167 *p++ = *s++;
168 }
169 }
170 *p = '\0';
171 } else {
172 escaped_opt = strdup(opt);
173 if (escaped_opt == NULL)
174 return (-1);
175 }
176
177 ret = add_opt(opts, escaped_opt);
178 free(escaped_opt);
179 return (ret);
180}
181DEF(fuse_opt_add_opt_escaped);
182
183int
184fuse_opt_add_arg(struct fuse_args *args, const char *name)
185{
186 return (fuse_opt_insert_arg(args, args->argc, name));
187}
188DEF(fuse_opt_add_arg);
189
190static int
191parse_opt(const struct fuse_opt *o, const char *opt, void *data,
192 fuse_opt_proc_t f, struct fuse_args *arg)
193{
194 const char *val;
195 int ret, found;
196 size_t sep;
197
198 found = 0;
199
200 for(; o != NULL && o->templ; o++) {
201 sep = match_opt(o->templ, opt);
202 if (sep == 0)
203 continue;
204
205 found = 1;
206 val = opt;
207
208 /* check key=value or -p n */
209 if (o->templ[sep] == '=')
210 val = &opt[sep + 1];
211 else if (o->templ[sep] == ' ') {
212 if (sep == strlen(opt)) {
213 /* ask for next arg to be included */
214 return (IFUSE_OPT_NEED_ANOTHER_ARG);
215 } else if (strchr(o->templ, '%') != NULL) {
216 val = &opt[sep];
217 }
218 }
219
220 if (o->val == FUSE_OPT_KEY_DISCARD)
221 ret = IFUSE_OPT_DISCARD;
222 else if (o->val == FUSE_OPT_KEY_KEEP)
223 ret = IFUSE_OPT_KEEP;
224 else if (FUSE_OPT_IS_OPT_KEY(o)) {
225 if (f == NULL)
226 return (IFUSE_OPT_KEEP);
227
228 ret = f(data, val, o->val, arg);
229 } else if (data == NULL) {
230 return (-1);
231 } else if (strchr(o->templ, '%') == NULL) {
232 *((int *)(data + o->off)) = o->val;
233 ret = IFUSE_OPT_DISCARD;
234 } else if (strstr(o->templ, "%s") != NULL) {
235 *((char **)(data + o->off)) = strdup(val);
236 ret = IFUSE_OPT_DISCARD;
237 } else {
238 /* All other templates, let sscanf deal with them. */
239 if (sscanf(opt, o->templ, data + o->off) != 1) {
240 fprintf(stderr, "fuse: Invalid value %s for "
241 "option %s\n", val, o->templ);
242 return (-1);
243 }
244 ret = IFUSE_OPT_DISCARD;
245 }
246 }
247
248 if (found)
249 return (ret);
250
251 if (f != NULL)
252 return f(data, opt, FUSE_OPT_KEY_OPT, arg);
253
254 return (IFUSE_OPT_KEEP);
255}
256
257/*
258 * this code is not very sexy but we are forced to follow
259 * the fuse api.
260 *
261 * when f() returns 1 we need to keep the arg
262 * when f() returns 0 we need to discard the arg
263 */
264int
265fuse_opt_parse(struct fuse_args *args, void *data,
266 const struct fuse_opt *opt, fuse_opt_proc_t f)
267{
268 struct fuse_args outargs;
269 const char *arg, *ap;
270 char *optlist, *tofree;
271 int ret;
272 int i;
273
274 if (!args || !args->argc || !args->argv)
275 return (0);
276
277 memset(&outargs, 0, sizeof(outargs));
278 fuse_opt_add_arg(&outargs, args->argv[0]);
279
280 for (i = 1; i < args->argc; i++) {
281 arg = args->argv[i];
282 ret = 0;
283
284 /* not - and not -- */
285 if (arg[0] != '-') {
286 if (f == NULL)
287 ret = IFUSE_OPT_KEEP;
288 else
289 ret = f(data, arg, FUSE_OPT_KEY_NONOPT, &outargs);
290
291 if (ret == IFUSE_OPT_KEEP)
292 fuse_opt_add_arg(&outargs, arg);
293 if (ret == -1)
294 goto err;
295 } else if (arg[1] == 'o') {
296 if (arg[2])
297 arg += 2; /* -ofoo,bar */
298 else {
299 if (++i >= args->argc)
300 goto err;
301
302 arg = args->argv[i];
303 }
304
305 tofree = optlist = strdup(arg);
306 if (optlist == NULL)
307 goto err;
308
309 while ((ap = strsep(&optlist, ",")) != NULL &&
310 ret != -1) {
311 ret = parse_opt(opt, ap, data, f, &outargs);
312 if (ret == IFUSE_OPT_KEEP) {
313 fuse_opt_add_arg(&outargs, "-o");
314 fuse_opt_add_arg(&outargs, ap);
315 }
316 }
317
318 free(tofree);
319
320 if (ret == -1)
321 goto err;
322 } else {
323 ret = parse_opt(opt, arg, data, f, &outargs);
324
325 if (ret == IFUSE_OPT_KEEP)
326 fuse_opt_add_arg(&outargs, arg);
327 else if (ret == IFUSE_OPT_NEED_ANOTHER_ARG) {
328 /* arg needs a value */
329 if (++i >= args->argc) {
330 fprintf(stderr, "fuse: missing argument after %s\n", arg);
331 goto err;
332 }
333
334 if (asprintf(&tofree, "%s%s", arg,
335 args->argv[i]) == -1)
336 goto err;
337
338 ret = parse_opt(opt, tofree, data, f, &outargs);
339 if (ret == IFUSE_OPT_KEEP)
340 fuse_opt_add_arg(&outargs, tofree);
341 free(tofree);
342 }
343
344 if (ret == -1)
345 goto err;
346 }
347 }
348 ret = 0;
349
350err:
351 /* Update args */
352 fuse_opt_free_args(args);
353 args->allocated = outargs.allocated;
354 args->argc = outargs.argc;
355 args->argv = outargs.argv;
356 if (ret != 0)
357 ret = -1;
358
359 return (ret);
360}
361DEF(fuse_opt_parse);
362
363int
364fuse_opt_insert_arg(struct fuse_args *args, int p, const char *name)
365{
366 char **av;
367 char *this_arg, *next_arg;
368 int i;
369
370 if (name == NULL)
371 return (-1);
372
373 if (!args->allocated && alloc_argv(args))
374 return (-1);
375
376 if (p < 0 || p > args->argc)
377 return (-1);
378
379 av = reallocarray(args->argv, args->argc + 2, sizeof(*av));
380 if (av == NULL)
381 return (-1);
382
383 this_arg = strdup(name);
384 if (this_arg == NULL) {
385 free(av);
386 return (-1);
387 }
388
389 args->argc++;
390 args->argv = av;
391 args->argv[args->argc] = NULL;
392 for (i = p; i < args->argc; i++) {
393 next_arg = args->argv[i];
394 args->argv[i] = this_arg;
395 this_arg = next_arg;
396 }
397 return (0);
398}
399DEF(fuse_opt_insert_arg);
400
401void
402fuse_opt_free_args(struct fuse_args *args)
403{
404 if (!args->allocated)
405 return;
406
407 free_argv(args->argv, args->argc);
408 args->argv = 0;
409 args->argc = 0;
410 args->allocated = 0;
411}
412DEF(fuse_opt_free_args);
413
414int
415fuse_opt_match(const struct fuse_opt *opts, const char *opt)
416{
417 const struct fuse_opt *this_opt = opts;
418
419 if (opt == NULL || opt[0] == '\0')
420 return (0);
421
422 while (this_opt->templ) {
423 if (match_opt(this_opt->templ, opt))
424 return (1);
425 this_opt++;
426 }
427
428 return (0);
429}
430DEF(fuse_opt_match);