jcs's openbsd hax
openbsd
at jcs 430 lines 8.6 kB view raw
1/* $OpenBSD: fuse_opt.c,v 1.28 2025/12/08 06:37:05 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 32static void 33free_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 42static int 43alloc_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 */ 73static size_t 74match_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 98static int 99add_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 118int 119fuse_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} 129DEF(fuse_opt_add_opt); 130 131int 132fuse_opt_add_opt_escaped(char **opts, const char *opt) 133{ 134 size_t size = 0, escaped = 0; 135 const char *s = opt; 136 char *escaped_opt, *p; 137 int ret; 138 139 if (opt == NULL || opt[0] == '\0') 140 return (-1); 141 142 while (*s) { 143 /* malloc(size + escaped) overflow check */ 144 if (size >= (SIZE_MAX / 2)) 145 return (-1); 146 147 if (*s == ',' || *s == '\\') 148 escaped++; 149 s++; 150 size++; 151 } 152 size++; /* trailing NUL */ 153 154 if (escaped > 0) { 155 escaped_opt = malloc(size + escaped); 156 if (escaped_opt == NULL) 157 return (-1); 158 s = opt; 159 p = escaped_opt; 160 while (*s) { 161 switch (*s) { 162 case ',': 163 case '\\': 164 *p++ = '\\'; 165 /* FALLTHROUGH */ 166 default: 167 *p++ = *s++; 168 } 169 } 170 *p = '\0'; 171 } else { 172 escaped_opt = strdup(opt); 173 if (escaped_opt == NULL) 174 return (-1); 175 } 176 177 ret = add_opt(opts, escaped_opt); 178 free(escaped_opt); 179 return (ret); 180} 181DEF(fuse_opt_add_opt_escaped); 182 183int 184fuse_opt_add_arg(struct fuse_args *args, const char *name) 185{ 186 return (fuse_opt_insert_arg(args, args->argc, name)); 187} 188DEF(fuse_opt_add_arg); 189 190static int 191parse_opt(const struct fuse_opt *o, const char *opt, void *data, 192 fuse_opt_proc_t f, struct fuse_args *arg) 193{ 194 const char *val; 195 int ret, found; 196 size_t sep; 197 198 found = 0; 199 200 for(; o != NULL && o->templ; o++) { 201 sep = match_opt(o->templ, opt); 202 if (sep == 0) 203 continue; 204 205 found = 1; 206 val = opt; 207 208 /* check key=value or -p n */ 209 if (o->templ[sep] == '=') 210 val = &opt[sep + 1]; 211 else if (o->templ[sep] == ' ') { 212 if (sep == strlen(opt)) { 213 /* ask for next arg to be included */ 214 return (IFUSE_OPT_NEED_ANOTHER_ARG); 215 } else if (strchr(o->templ, '%') != NULL) { 216 val = &opt[sep]; 217 } 218 } 219 220 if (o->val == FUSE_OPT_KEY_DISCARD) 221 ret = IFUSE_OPT_DISCARD; 222 else if (o->val == FUSE_OPT_KEY_KEEP) 223 ret = IFUSE_OPT_KEEP; 224 else if (FUSE_OPT_IS_OPT_KEY(o)) { 225 if (f == NULL) 226 return (IFUSE_OPT_KEEP); 227 228 ret = f(data, val, o->val, arg); 229 } else if (data == NULL) { 230 return (-1); 231 } else if (strchr(o->templ, '%') == NULL) { 232 *((int *)(data + o->off)) = o->val; 233 ret = IFUSE_OPT_DISCARD; 234 } else if (strstr(o->templ, "%s") != NULL) { 235 *((char **)(data + o->off)) = strdup(val); 236 ret = IFUSE_OPT_DISCARD; 237 } else { 238 /* All other templates, let sscanf deal with them. */ 239 if (sscanf(opt, o->templ, data + o->off) != 1) { 240 fprintf(stderr, "fuse: Invalid value %s for " 241 "option %s\n", val, o->templ); 242 return (-1); 243 } 244 ret = IFUSE_OPT_DISCARD; 245 } 246 } 247 248 if (found) 249 return (ret); 250 251 if (f != NULL) 252 return f(data, opt, FUSE_OPT_KEY_OPT, arg); 253 254 return (IFUSE_OPT_KEEP); 255} 256 257/* 258 * this code is not very sexy but we are forced to follow 259 * the fuse api. 260 * 261 * when f() returns 1 we need to keep the arg 262 * when f() returns 0 we need to discard the arg 263 */ 264int 265fuse_opt_parse(struct fuse_args *args, void *data, 266 const struct fuse_opt *opt, fuse_opt_proc_t f) 267{ 268 struct fuse_args outargs; 269 const char *arg, *ap; 270 char *optlist, *tofree; 271 int ret; 272 int i; 273 274 if (!args || !args->argc || !args->argv) 275 return (0); 276 277 memset(&outargs, 0, sizeof(outargs)); 278 fuse_opt_add_arg(&outargs, args->argv[0]); 279 280 for (i = 1; i < args->argc; i++) { 281 arg = args->argv[i]; 282 ret = 0; 283 284 /* not - and not -- */ 285 if (arg[0] != '-') { 286 if (f == NULL) 287 ret = IFUSE_OPT_KEEP; 288 else 289 ret = f(data, arg, FUSE_OPT_KEY_NONOPT, &outargs); 290 291 if (ret == IFUSE_OPT_KEEP) 292 fuse_opt_add_arg(&outargs, arg); 293 if (ret == -1) 294 goto err; 295 } else if (arg[1] == 'o') { 296 if (arg[2]) 297 arg += 2; /* -ofoo,bar */ 298 else { 299 if (++i >= args->argc) 300 goto err; 301 302 arg = args->argv[i]; 303 } 304 305 tofree = optlist = strdup(arg); 306 if (optlist == NULL) 307 goto err; 308 309 while ((ap = strsep(&optlist, ",")) != NULL && 310 ret != -1) { 311 ret = parse_opt(opt, ap, data, f, &outargs); 312 if (ret == IFUSE_OPT_KEEP) { 313 fuse_opt_add_arg(&outargs, "-o"); 314 fuse_opt_add_arg(&outargs, ap); 315 } 316 } 317 318 free(tofree); 319 320 if (ret == -1) 321 goto err; 322 } else { 323 ret = parse_opt(opt, arg, data, f, &outargs); 324 325 if (ret == IFUSE_OPT_KEEP) 326 fuse_opt_add_arg(&outargs, arg); 327 else if (ret == IFUSE_OPT_NEED_ANOTHER_ARG) { 328 /* arg needs a value */ 329 if (++i >= args->argc) { 330 fprintf(stderr, "fuse: missing argument after %s\n", arg); 331 goto err; 332 } 333 334 if (asprintf(&tofree, "%s%s", arg, 335 args->argv[i]) == -1) 336 goto err; 337 338 ret = parse_opt(opt, tofree, data, f, &outargs); 339 if (ret == IFUSE_OPT_KEEP) 340 fuse_opt_add_arg(&outargs, tofree); 341 free(tofree); 342 } 343 344 if (ret == -1) 345 goto err; 346 } 347 } 348 ret = 0; 349 350err: 351 /* Update args */ 352 fuse_opt_free_args(args); 353 args->allocated = outargs.allocated; 354 args->argc = outargs.argc; 355 args->argv = outargs.argv; 356 if (ret != 0) 357 ret = -1; 358 359 return (ret); 360} 361DEF(fuse_opt_parse); 362 363int 364fuse_opt_insert_arg(struct fuse_args *args, int p, const char *name) 365{ 366 char **av; 367 char *this_arg, *next_arg; 368 int i; 369 370 if (name == NULL) 371 return (-1); 372 373 if (!args->allocated && alloc_argv(args)) 374 return (-1); 375 376 if (p < 0 || p > args->argc) 377 return (-1); 378 379 av = reallocarray(args->argv, args->argc + 2, sizeof(*av)); 380 if (av == NULL) 381 return (-1); 382 383 this_arg = strdup(name); 384 if (this_arg == NULL) { 385 free(av); 386 return (-1); 387 } 388 389 args->argc++; 390 args->argv = av; 391 args->argv[args->argc] = NULL; 392 for (i = p; i < args->argc; i++) { 393 next_arg = args->argv[i]; 394 args->argv[i] = this_arg; 395 this_arg = next_arg; 396 } 397 return (0); 398} 399DEF(fuse_opt_insert_arg); 400 401void 402fuse_opt_free_args(struct fuse_args *args) 403{ 404 if (!args->allocated) 405 return; 406 407 free_argv(args->argv, args->argc); 408 args->argv = 0; 409 args->argc = 0; 410 args->allocated = 0; 411} 412DEF(fuse_opt_free_args); 413 414int 415fuse_opt_match(const struct fuse_opt *opts, const char *opt) 416{ 417 const struct fuse_opt *this_opt = opts; 418 419 if (opt == NULL || opt[0] == '\0') 420 return (0); 421 422 while (this_opt->templ) { 423 if (match_opt(this_opt->templ, opt)) 424 return (1); 425 this_opt++; 426 } 427 428 return (0); 429} 430DEF(fuse_opt_match);