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