File: | src/lib/libfuse/fuse_opt.c |
Warning: | line 398, 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.26 2018/05/15 11:57:32 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_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 keyval, ret, found; | |||
194 | size_t sep; | |||
195 | ||||
196 | keyval = 0; | |||
197 | found = 0; | |||
198 | ||||
199 | for(; o != NULL((void *)0) && o->templ; o++) { | |||
200 | sep = match_opt(o->templ, opt); | |||
201 | if (sep == 0) | |||
202 | continue; | |||
203 | ||||
204 | found = 1; | |||
205 | val = opt; | |||
206 | ||||
207 | /* check key=value or -p n */ | |||
208 | if (o->templ[sep] == '=') { | |||
209 | keyval = 1; | |||
210 | val = &opt[sep + 1]; | |||
211 | } else if (o->templ[sep] == ' ') { | |||
212 | keyval = 1; | |||
213 | if (sep == strlen(opt)) { | |||
214 | /* ask for next arg to be included */ | |||
215 | return (IFUSE_OPT_NEED_ANOTHER_ARG2); | |||
216 | } else if (strchr(o->templ, '%') != NULL((void *)0)) { | |||
217 | val = &opt[sep]; | |||
218 | } | |||
219 | } | |||
220 | ||||
221 | if (o->val == FUSE_OPT_KEY_DISCARD-4) | |||
222 | ret = IFUSE_OPT_DISCARD0; | |||
223 | else if (o->val == FUSE_OPT_KEY_KEEP-3) | |||
224 | ret = IFUSE_OPT_KEEP1; | |||
225 | else if (FUSE_OPT_IS_OPT_KEY(o)(o->off == (unsigned long)-1)) { | |||
226 | if (f == NULL((void *)0)) | |||
227 | return (IFUSE_OPT_KEEP1); | |||
228 | ||||
229 | ret = f(data, val, o->val, arg); | |||
230 | } else if (data == NULL((void *)0)) { | |||
231 | return (-1); | |||
232 | } else if (strchr(o->templ, '%') == NULL((void *)0)) { | |||
233 | *((int *)(data + o->off)) = o->val; | |||
234 | ret = IFUSE_OPT_DISCARD0; | |||
235 | } else if (strstr(o->templ, "%s") != NULL((void *)0)) { | |||
236 | *((char **)(data + o->off)) = strdup(val); | |||
237 | ret = IFUSE_OPT_DISCARD0; | |||
238 | } else { | |||
239 | /* All other templates, let sscanf deal with them. */ | |||
240 | if (sscanf(opt, o->templ, data + o->off) != 1) { | |||
241 | fprintf(stderr(&__sF[2]), "fuse: Invalid value %s for " | |||
242 | "option %s\n", val, o->templ); | |||
243 | return (-1); | |||
244 | } | |||
245 | ret = IFUSE_OPT_DISCARD0; | |||
246 | } | |||
247 | } | |||
248 | ||||
249 | if (found) | |||
250 | return (ret); | |||
251 | ||||
252 | if (f != NULL((void *)0)) | |||
253 | return f(data, opt, FUSE_OPT_KEY_OPT-1, arg); | |||
254 | ||||
255 | return (IFUSE_OPT_KEEP1); | |||
256 | } | |||
257 | ||||
258 | /* | |||
259 | * this code is not very sexy but we are forced to follow | |||
260 | * the fuse api. | |||
261 | * | |||
262 | * when f() returns 1 we need to keep the arg | |||
263 | * when f() returns 0 we need to discard the arg | |||
264 | */ | |||
265 | int | |||
266 | fuse_opt_parse(struct fuse_args *args, void *data, | |||
267 | const struct fuse_opt *opt, fuse_opt_proc_t f) | |||
268 | { | |||
269 | struct fuse_args outargs; | |||
270 | const char *arg, *ap; | |||
271 | char *optlist, *tofree; | |||
272 | int ret; | |||
273 | int i; | |||
274 | ||||
275 | if (!args || !args->argc || !args->argv) | |||
| ||||
276 | return (0); | |||
277 | ||||
278 | memset(&outargs, 0, sizeof(outargs)); | |||
279 | fuse_opt_add_arg(&outargs, args->argv[0]); | |||
280 | ||||
281 | for (i = 1; i < args->argc; i++) { | |||
282 | arg = args->argv[i]; | |||
283 | ret = 0; | |||
284 | ||||
285 | /* not - and not -- */ | |||
286 | if (arg[0] != '-') { | |||
287 | if (f == NULL((void *)0)) | |||
288 | ret = IFUSE_OPT_KEEP1; | |||
289 | else | |||
290 | ret = f(data, arg, FUSE_OPT_KEY_NONOPT-2, &outargs); | |||
291 | ||||
292 | if (ret == IFUSE_OPT_KEEP1) | |||
293 | fuse_opt_add_arg(&outargs, arg); | |||
294 | if (ret == -1) | |||
295 | goto err; | |||
296 | } else if (arg[1] == 'o') { | |||
297 | if (arg[2]) | |||
298 | arg += 2; /* -ofoo,bar */ | |||
299 | else { | |||
300 | if (++i >= args->argc) | |||
301 | goto err; | |||
302 | ||||
303 | arg = args->argv[i]; | |||
304 | } | |||
305 | ||||
306 | tofree = optlist = strdup(arg); | |||
307 | if (optlist == NULL((void *)0)) | |||
308 | goto err; | |||
309 | ||||
310 | while ((ap = strsep(&optlist, ",")) != NULL((void *)0) && | |||
311 | ret != -1) { | |||
312 | ret = parse_opt(opt, ap, data, f, &outargs); | |||
313 | if (ret == IFUSE_OPT_KEEP1) { | |||
314 | fuse_opt_add_arg(&outargs, "-o"); | |||
315 | fuse_opt_add_arg(&outargs, ap); | |||
316 | } | |||
317 | } | |||
318 | ||||
319 | free(tofree); | |||
320 | ||||
321 | if (ret == -1) | |||
322 | goto err; | |||
323 | } else { | |||
324 | ret = parse_opt(opt, arg, data, f, &outargs); | |||
325 | ||||
326 | if (ret == IFUSE_OPT_KEEP1) | |||
327 | fuse_opt_add_arg(&outargs, arg); | |||
328 | else if (ret == IFUSE_OPT_NEED_ANOTHER_ARG2) { | |||
329 | /* arg needs a value */ | |||
330 | if (++i >= args->argc) { | |||
331 | fprintf(stderr(&__sF[2]), "fuse: missing argument after %s\n", arg); | |||
332 | goto err; | |||
333 | } | |||
334 | ||||
335 | if (asprintf(&tofree, "%s%s", arg, | |||
336 | args->argv[i]) == -1) | |||
337 | goto err; | |||
338 | ||||
339 | ret = parse_opt(opt, tofree, data, f, &outargs); | |||
340 | if (ret == IFUSE_OPT_KEEP1) | |||
341 | fuse_opt_add_arg(&outargs, tofree); | |||
342 | free(tofree); | |||
343 | } | |||
344 | ||||
345 | if (ret == -1) | |||
346 | goto err; | |||
347 | } | |||
348 | } | |||
349 | ret = 0; | |||
350 | ||||
351 | err: | |||
352 | /* Update args */ | |||
353 | fuse_opt_free_args(args); | |||
354 | args->allocated = outargs.allocated; | |||
355 | args->argc = outargs.argc; | |||
356 | args->argv = outargs.argv; | |||
357 | if (ret != 0) | |||
358 | ret = -1; | |||
359 | ||||
360 | return (ret); | |||
361 | } | |||
362 | DEF(fuse_opt_parse)__asm__(".global " "fuse_opt_parse" " ; " "fuse_opt_parse" " = " "__fuse_opt_parse"); | |||
363 | ||||
364 | int | |||
365 | fuse_opt_insert_arg(struct fuse_args *args, int p, const char *name) | |||
366 | { | |||
367 | char **av; | |||
368 | char *this_arg, *next_arg; | |||
369 | int i; | |||
370 | ||||
371 | if (name
| |||
372 | return (-1); | |||
373 | ||||
374 | if (!args->allocated && alloc_argv(args)) | |||
375 | return (-1); | |||
376 | ||||
377 | if (p < 0 || p
| |||
378 | return (-1); | |||
379 | ||||
380 | av = reallocarray(args->argv, args->argc + 2, sizeof(*av)); | |||
381 | if (av == NULL((void *)0)) | |||
382 | return (-1); | |||
383 | ||||
384 | this_arg = strdup(name); | |||
385 | if (this_arg == NULL((void *)0)) { | |||
386 | free(av); | |||
387 | return (-1); | |||
388 | } | |||
389 | ||||
390 | args->argc++; | |||
391 | args->argv = av; | |||
392 | args->argv[args->argc] = NULL((void *)0); | |||
393 | for (i = p; i < args->argc; i++) { | |||
394 | next_arg = args->argv[i]; | |||
395 | args->argv[i] = this_arg; | |||
396 | this_arg = next_arg; | |||
397 | } | |||
398 | return (0); | |||
| ||||
399 | } | |||
400 | DEF(fuse_opt_insert_arg)__asm__(".global " "fuse_opt_insert_arg" " ; " "fuse_opt_insert_arg" " = " "__fuse_opt_insert_arg"); | |||
401 | ||||
402 | void | |||
403 | fuse_opt_free_args(struct fuse_args *args) | |||
404 | { | |||
405 | if (!args->allocated) | |||
406 | return; | |||
407 | ||||
408 | free_argv(args->argv, args->argc); | |||
409 | args->argv = 0; | |||
410 | args->argc = 0; | |||
411 | args->allocated = 0; | |||
412 | } | |||
413 | DEF(fuse_opt_free_args)__asm__(".global " "fuse_opt_free_args" " ; " "fuse_opt_free_args" " = " "__fuse_opt_free_args"); | |||
414 | ||||
415 | int | |||
416 | fuse_opt_match(const struct fuse_opt *opts, const char *opt) | |||
417 | { | |||
418 | const struct fuse_opt *this_opt = opts; | |||
419 | ||||
420 | if (opt == NULL((void *)0) || opt[0] == '\0') | |||
421 | return (0); | |||
422 | ||||
423 | while (this_opt->templ) { | |||
424 | if (match_opt(this_opt->templ, opt)) | |||
425 | return (1); | |||
426 | this_opt++; | |||
427 | } | |||
428 | ||||
429 | return (0); | |||
430 | } | |||
431 | DEF(fuse_opt_match)__asm__(".global " "fuse_opt_match" " ; " "fuse_opt_match" " = " "__fuse_opt_match"); |