File: | src/lib/libfuse/fuse_opt.c |
Warning: | line 395, column 10 Potential leak of memory pointed to by 'this_arg' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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_DISCARD0 0 | |||
29 | #define IFUSE_OPT_KEEP1 1 | |||
30 | #define IFUSE_OPT_NEED_ANOTHER_ARG2 2 | |||
31 | ||||
32 | static void | |||
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 | |||
43 | alloc_argv(struct fuse_args *args) | |||
44 | { | |||
45 | char **argv; | |||
46 | int i; | |||
47 | ||||
48 | assert(!args->allocated)((!args->allocated) ? (void)0 : __assert2("/usr/src/lib/libfuse/fuse_opt.c" , 48, __func__, "!args->allocated")); | |||
49 | ||||
50 | argv = calloc(args->argc, sizeof(*argv)); | |||
51 | if (argv == NULL((void *)0)) | |||
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((void *)0)) { | |||
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 | |||
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 | |||
99 | add_opt(char **opts, const char *opt) | |||
100 | { | |||
101 | char *new_opts; | |||
102 | ||||
103 | if (*opts == NULL((void *)0)) { | |||
104 | *opts = strdup(opt); | |||
105 | if (*opts == NULL((void *)0)) | |||
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 | |||
119 | fuse_opt_add_opt(char **opts, const char *opt) | |||
120 | { | |||
121 | int ret; | |||
122 | ||||
123 | if (opt == NULL((void *)0) || opt[0] == '\0') | |||
124 | return (-1); | |||
125 | ||||
126 | ret = add_opt(opts, opt); | |||
127 | return (ret); | |||
128 | } | |||
129 | ||||
130 | int | |||
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((void *)0) || opt[0] == '\0') | |||
139 | return (-1); | |||
140 | ||||
141 | while (*s) { | |||
142 | /* malloc(size + escaped) overflow check */ | |||
143 | if (size >= (SIZE_MAX0xffffffffffffffffUL / 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((void *)0)) | |||
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((void *)0)) | |||
173 | return (-1); | |||
174 | } | |||
175 | ||||
176 | ret = add_opt(opts, escaped_opt); | |||
177 | free(escaped_opt); | |||
178 | return (ret); | |||
179 | } | |||
180 | ||||
181 | int | |||
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)__asm__(".global " "fuse_opt_add_arg" " ; " "fuse_opt_add_arg" " = " "__fuse_opt_add_arg"); | |||
187 | ||||
188 | static int | |||
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((void *)0) && 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_ARG2); | |||
213 | } else if (strchr(o->templ, '%') != NULL((void *)0)) { | |||
214 | val = &opt[sep]; | |||
215 | } | |||
216 | } | |||
217 | ||||
218 | if (o->val == FUSE_OPT_KEY_DISCARD-4) | |||
219 | ret = IFUSE_OPT_DISCARD0; | |||
220 | else if (o->val == FUSE_OPT_KEY_KEEP-3) | |||
221 | ret = IFUSE_OPT_KEEP1; | |||
222 | else if (FUSE_OPT_IS_OPT_KEY(o)(o->off == (unsigned long)-1)) { | |||
223 | if (f == NULL((void *)0)) | |||
224 | return (IFUSE_OPT_KEEP1); | |||
225 | ||||
226 | ret = f(data, val, o->val, arg); | |||
227 | } else if (data == NULL((void *)0)) { | |||
228 | return (-1); | |||
229 | } else if (strchr(o->templ, '%') == NULL((void *)0)) { | |||
230 | *((int *)(data + o->off)) = o->val; | |||
231 | ret = IFUSE_OPT_DISCARD0; | |||
232 | } else if (strstr(o->templ, "%s") != NULL((void *)0)) { | |||
233 | *((char **)(data + o->off)) = strdup(val); | |||
234 | ret = IFUSE_OPT_DISCARD0; | |||
235 | } else { | |||
236 | /* All other templates, let sscanf deal with them. */ | |||
237 | if (sscanf(opt, o->templ, data + o->off) != 1) { | |||
238 | fprintf(stderr(&__sF[2]), "fuse: Invalid value %s for " | |||
239 | "option %s\n", val, o->templ); | |||
240 | return (-1); | |||
241 | } | |||
242 | ret = IFUSE_OPT_DISCARD0; | |||
243 | } | |||
244 | } | |||
245 | ||||
246 | if (found) | |||
247 | return (ret); | |||
248 | ||||
249 | if (f != NULL((void *)0)) | |||
250 | return f(data, opt, FUSE_OPT_KEY_OPT-1, arg); | |||
251 | ||||
252 | return (IFUSE_OPT_KEEP1); | |||
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 | |||
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((void *)0)) | |||
285 | ret = IFUSE_OPT_KEEP1; | |||
286 | else | |||
287 | ret = f(data, arg, FUSE_OPT_KEY_NONOPT-2, &outargs); | |||
288 | ||||
289 | if (ret == IFUSE_OPT_KEEP1) | |||
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((void *)0)) | |||
305 | goto err; | |||
306 | ||||
307 | while ((ap = strsep(&optlist, ",")) != NULL((void *)0) && | |||
308 | ret != -1) { | |||
309 | ret = parse_opt(opt, ap, data, f, &outargs); | |||
310 | if (ret == IFUSE_OPT_KEEP1) { | |||
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_KEEP1) | |||
324 | fuse_opt_add_arg(&outargs, arg); | |||
325 | else if (ret == IFUSE_OPT_NEED_ANOTHER_ARG2) { | |||
326 | /* arg needs a value */ | |||
327 | if (++i >= args->argc) { | |||
328 | fprintf(stderr(&__sF[2]), "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_KEEP1) | |||
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)__asm__(".global " "fuse_opt_parse" " ; " "fuse_opt_parse" " = " "__fuse_opt_parse"); | |||
360 | ||||
361 | int | |||
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
| |||
369 | return (-1); | |||
370 | ||||
371 | if (!args->allocated && alloc_argv(args)) | |||
372 | return (-1); | |||
373 | ||||
374 | if (p < 0 || p
| |||
375 | return (-1); | |||
376 | ||||
377 | av = reallocarray(args->argv, args->argc + 2, sizeof(*av)); | |||
378 | if (av == NULL((void *)0)) | |||
379 | return (-1); | |||
380 | ||||
381 | this_arg = strdup(name); | |||
382 | if (this_arg == NULL((void *)0)) { | |||
383 | free(av); | |||
384 | return (-1); | |||
385 | } | |||
386 | ||||
387 | args->argc++; | |||
388 | args->argv = av; | |||
389 | args->argv[args->argc] = NULL((void *)0); | |||
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)__asm__(".global " "fuse_opt_insert_arg" " ; " "fuse_opt_insert_arg" " = " "__fuse_opt_insert_arg"); | |||
398 | ||||
399 | void | |||
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)__asm__(".global " "fuse_opt_free_args" " ; " "fuse_opt_free_args" " = " "__fuse_opt_free_args"); | |||
411 | ||||
412 | int | |||
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((void *)0) || 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)__asm__(".global " "fuse_opt_match" " ; " "fuse_opt_match" " = " "__fuse_opt_match"); |