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_DISCARD 0 29 #define IFUSE_OPT_KEEP 1 30 #define IFUSE_OPT_NEED_ANOTHER_ARG 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); 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 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) { 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 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 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 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 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 && 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_ARG); 216 } else if (strchr(o->templ, '%') != NULL) { 217 val = &opt[sep]; 218 } 219 } 220 221 if (o->val == FUSE_OPT_KEY_DISCARD) 222 ret = IFUSE_OPT_DISCARD; 223 else if (o->val == FUSE_OPT_KEY_KEEP) 224 ret = IFUSE_OPT_KEEP; 225 else if (FUSE_OPT_IS_OPT_KEY(o)) { 226 if (f == NULL) 227 return (IFUSE_OPT_KEEP); 228 229 ret = f(data, val, o->val, arg); 230 } else if (data == NULL) { 231 return (-1); 232 } else if (strchr(o->templ, '%') == NULL) { 233 *((int *)(data + o->off)) = o->val; 234 ret = IFUSE_OPT_DISCARD; 235 } else if (strstr(o->templ, "%s") != NULL) { 236 *((char **)(data + o->off)) = strdup(val); 237 ret = IFUSE_OPT_DISCARD; 238 } else { 239 /* All other templates, let sscanf deal with them. */ 240 if (sscanf(opt, o->templ, data + o->off) != 1) { 241 fprintf(stderr, "fuse: Invalid value %s for " 242 "option %s\n", val, o->templ); 243 return (-1); 244 } 245 ret = IFUSE_OPT_DISCARD; 246 } 247 } 248 249 if (found) 250 return (ret); 251 252 if (f != NULL) 253 return f(data, opt, FUSE_OPT_KEY_OPT, arg); 254 255 return (IFUSE_OPT_KEEP); 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) 288 ret = IFUSE_OPT_KEEP; 289 else 290 ret = f(data, arg, FUSE_OPT_KEY_NONOPT, &outargs); 291 292 if (ret == IFUSE_OPT_KEEP) 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) 308 goto err; 309 310 while ((ap = strsep(&optlist, ",")) != NULL && 311 ret != -1) { 312 ret = parse_opt(opt, ap, data, f, &outargs); 313 if (ret == IFUSE_OPT_KEEP) { 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_KEEP) 327 fuse_opt_add_arg(&outargs, arg); 328 else if (ret == IFUSE_OPT_NEED_ANOTHER_ARG) { 329 /* arg needs a value */ 330 if (++i >= args->argc) { 331 fprintf(stderr, "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_KEEP) 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); 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 == NULL) 372 return (-1); 373 374 if (!args->allocated && alloc_argv(args)) 375 return (-1); 376 377 if (p < 0 || p > args->argc) 378 return (-1); 379 380 av = reallocarray(args->argv, args->argc + 2, sizeof(*av)); 381 if (av == NULL) 382 return (-1); 383 384 this_arg = strdup(name); 385 if (this_arg == NULL) { 386 free(av); 387 return (-1); 388 } 389 390 args->argc++; 391 args->argv = av; 392 args->argv[args->argc] = NULL; 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); 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); 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 || 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); 432