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