fuse_opt.c (10822B)
1/* 2 * FUSE: Filesystem in Userspace 3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> 4 * 5 * Implementation of option parsing routines (dealing with `struct 6 * fuse_args`). 7 * 8 * This program can be distributed under the terms of the GNU LGPLv2. 9 * See the file COPYING.LIB 10 */ 11 12#include "qemu/osdep.h" 13#include "fuse_opt.h" 14#include "fuse_i.h" 15#include "fuse_misc.h" 16 17 18struct fuse_opt_context { 19 void *data; 20 const struct fuse_opt *opt; 21 fuse_opt_proc_t proc; 22 int argctr; 23 int argc; 24 char **argv; 25 struct fuse_args outargs; 26 char *opts; 27 int nonopt; 28}; 29 30void fuse_opt_free_args(struct fuse_args *args) 31{ 32 if (args) { 33 if (args->argv && args->allocated) { 34 int i; 35 for (i = 0; i < args->argc; i++) { 36 free(args->argv[i]); 37 } 38 free(args->argv); 39 } 40 args->argc = 0; 41 args->argv = NULL; 42 args->allocated = 0; 43 } 44} 45 46static int alloc_failed(void) 47{ 48 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); 49 return -1; 50} 51 52int fuse_opt_add_arg(struct fuse_args *args, const char *arg) 53{ 54 char **newargv; 55 char *newarg; 56 57 assert(!args->argv || args->allocated); 58 59 newarg = strdup(arg); 60 if (!newarg) { 61 return alloc_failed(); 62 } 63 64 newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); 65 if (!newargv) { 66 free(newarg); 67 return alloc_failed(); 68 } 69 70 args->argv = newargv; 71 args->allocated = 1; 72 args->argv[args->argc++] = newarg; 73 args->argv[args->argc] = NULL; 74 return 0; 75} 76 77static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos, 78 const char *arg) 79{ 80 assert(pos <= args->argc); 81 if (fuse_opt_add_arg(args, arg) == -1) { 82 return -1; 83 } 84 85 if (pos != args->argc - 1) { 86 char *newarg = args->argv[args->argc - 1]; 87 memmove(&args->argv[pos + 1], &args->argv[pos], 88 sizeof(char *) * (args->argc - pos - 1)); 89 args->argv[pos] = newarg; 90 } 91 return 0; 92} 93 94int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) 95{ 96 return fuse_opt_insert_arg_common(args, pos, arg); 97} 98 99static int next_arg(struct fuse_opt_context *ctx, const char *opt) 100{ 101 if (ctx->argctr + 1 >= ctx->argc) { 102 fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); 103 return -1; 104 } 105 ctx->argctr++; 106 return 0; 107} 108 109static int add_arg(struct fuse_opt_context *ctx, const char *arg) 110{ 111 return fuse_opt_add_arg(&ctx->outargs, arg); 112} 113 114static int add_opt_common(char **opts, const char *opt, int esc) 115{ 116 unsigned oldlen = *opts ? strlen(*opts) : 0; 117 char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); 118 119 if (!d) { 120 return alloc_failed(); 121 } 122 123 *opts = d; 124 if (oldlen) { 125 d += oldlen; 126 *d++ = ','; 127 } 128 129 for (; *opt; opt++) { 130 if (esc && (*opt == ',' || *opt == '\\')) { 131 *d++ = '\\'; 132 } 133 *d++ = *opt; 134 } 135 *d = '\0'; 136 137 return 0; 138} 139 140int fuse_opt_add_opt(char **opts, const char *opt) 141{ 142 return add_opt_common(opts, opt, 0); 143} 144 145int fuse_opt_add_opt_escaped(char **opts, const char *opt) 146{ 147 return add_opt_common(opts, opt, 1); 148} 149 150static int add_opt(struct fuse_opt_context *ctx, const char *opt) 151{ 152 return add_opt_common(&ctx->opts, opt, 1); 153} 154 155static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, 156 int iso) 157{ 158 if (key == FUSE_OPT_KEY_DISCARD) { 159 return 0; 160 } 161 162 if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { 163 int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); 164 if (res == -1 || !res) { 165 return res; 166 } 167 } 168 if (iso) { 169 return add_opt(ctx, arg); 170 } else { 171 return add_arg(ctx, arg); 172 } 173} 174 175static int match_template(const char *t, const char *arg, unsigned *sepp) 176{ 177 int arglen = strlen(arg); 178 const char *sep = strchr(t, '='); 179 sep = sep ? sep : strchr(t, ' '); 180 if (sep && (!sep[1] || sep[1] == '%')) { 181 int tlen = sep - t; 182 if (sep[0] == '=') { 183 tlen++; 184 } 185 if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { 186 *sepp = sep - t; 187 return 1; 188 } 189 } 190 if (strcmp(t, arg) == 0) { 191 *sepp = 0; 192 return 1; 193 } 194 return 0; 195} 196 197static const struct fuse_opt *find_opt(const struct fuse_opt *opt, 198 const char *arg, unsigned *sepp) 199{ 200 for (; opt && opt->templ; opt++) { 201 if (match_template(opt->templ, arg, sepp)) { 202 return opt; 203 } 204 } 205 return NULL; 206} 207 208int fuse_opt_match(const struct fuse_opt *opts, const char *opt) 209{ 210 unsigned dummy; 211 return find_opt(opts, opt, &dummy) ? 1 : 0; 212} 213 214static int process_opt_param(void *var, const char *format, const char *param, 215 const char *arg) 216{ 217 assert(format[0] == '%'); 218 if (format[1] == 's') { 219 char **s = var; 220 char *copy = strdup(param); 221 if (!copy) { 222 return alloc_failed(); 223 } 224 225 free(*s); 226 *s = copy; 227 } else { 228 if (sscanf(param, format, var) != 1) { 229 fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", 230 arg); 231 return -1; 232 } 233 } 234 return 0; 235} 236 237static int process_opt(struct fuse_opt_context *ctx, const struct fuse_opt *opt, 238 unsigned sep, const char *arg, int iso) 239{ 240 if (opt->offset == -1U) { 241 if (call_proc(ctx, arg, opt->value, iso) == -1) { 242 return -1; 243 } 244 } else { 245 void *var = (char *)ctx->data + opt->offset; 246 if (sep && opt->templ[sep + 1]) { 247 const char *param = arg + sep; 248 if (opt->templ[sep] == '=') { 249 param++; 250 } 251 if (process_opt_param(var, opt->templ + sep + 1, param, arg) == 252 -1) { 253 return -1; 254 } 255 } else { 256 *(int *)var = opt->value; 257 } 258 } 259 return 0; 260} 261 262static int process_opt_sep_arg(struct fuse_opt_context *ctx, 263 const struct fuse_opt *opt, unsigned sep, 264 const char *arg, int iso) 265{ 266 int res; 267 char *newarg; 268 char *param; 269 270 if (next_arg(ctx, arg) == -1) { 271 return -1; 272 } 273 274 param = ctx->argv[ctx->argctr]; 275 newarg = g_try_malloc(sep + strlen(param) + 1); 276 if (!newarg) { 277 return alloc_failed(); 278 } 279 280 memcpy(newarg, arg, sep); 281 strcpy(newarg + sep, param); 282 res = process_opt(ctx, opt, sep, newarg, iso); 283 g_free(newarg); 284 285 return res; 286} 287 288static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) 289{ 290 unsigned sep; 291 const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); 292 if (opt) { 293 for (; opt; opt = find_opt(opt + 1, arg, &sep)) { 294 int res; 295 if (sep && opt->templ[sep] == ' ' && !arg[sep]) { 296 res = process_opt_sep_arg(ctx, opt, sep, arg, iso); 297 } else { 298 res = process_opt(ctx, opt, sep, arg, iso); 299 } 300 if (res == -1) { 301 return -1; 302 } 303 } 304 return 0; 305 } else { 306 return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); 307 } 308} 309 310static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) 311{ 312 char *s = opts; 313 char *d = s; 314 int end = 0; 315 316 while (!end) { 317 if (*s == '\0') { 318 end = 1; 319 } 320 if (*s == ',' || end) { 321 int res; 322 323 *d = '\0'; 324 res = process_gopt(ctx, opts, 1); 325 if (res == -1) { 326 return -1; 327 } 328 d = opts; 329 } else { 330 if (s[0] == '\\' && s[1] != '\0') { 331 s++; 332 if (s[0] >= '0' && s[0] <= '3' && s[1] >= '0' && s[1] <= '7' && 333 s[2] >= '0' && s[2] <= '7') { 334 *d++ = (s[0] - '0') * 0100 + (s[1] - '0') * 0010 + 335 (s[2] - '0'); 336 s += 2; 337 } else { 338 *d++ = *s; 339 } 340 } else { 341 *d++ = *s; 342 } 343 } 344 s++; 345 } 346 347 return 0; 348} 349 350static int process_option_group(struct fuse_opt_context *ctx, const char *opts) 351{ 352 int res; 353 char *copy = strdup(opts); 354 355 if (!copy) { 356 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); 357 return -1; 358 } 359 res = process_real_option_group(ctx, copy); 360 free(copy); 361 return res; 362} 363 364static int process_one(struct fuse_opt_context *ctx, const char *arg) 365{ 366 if (ctx->nonopt || arg[0] != '-') { 367 return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); 368 } else if (arg[1] == 'o') { 369 if (arg[2]) { 370 return process_option_group(ctx, arg + 2); 371 } else { 372 if (next_arg(ctx, arg) == -1) { 373 return -1; 374 } 375 376 return process_option_group(ctx, ctx->argv[ctx->argctr]); 377 } 378 } else if (arg[1] == '-' && !arg[2]) { 379 if (add_arg(ctx, arg) == -1) { 380 return -1; 381 } 382 ctx->nonopt = ctx->outargs.argc; 383 return 0; 384 } else { 385 return process_gopt(ctx, arg, 0); 386 } 387} 388 389static int opt_parse(struct fuse_opt_context *ctx) 390{ 391 if (ctx->argc) { 392 if (add_arg(ctx, ctx->argv[0]) == -1) { 393 return -1; 394 } 395 } 396 397 for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) { 398 if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) { 399 return -1; 400 } 401 } 402 403 if (ctx->opts) { 404 if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || 405 fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) { 406 return -1; 407 } 408 } 409 410 /* If option separator ("--") is the last argument, remove it */ 411 if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && 412 strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { 413 free(ctx->outargs.argv[ctx->outargs.argc - 1]); 414 ctx->outargs.argv[--ctx->outargs.argc] = NULL; 415 } 416 417 return 0; 418} 419 420int fuse_opt_parse(struct fuse_args *args, void *data, 421 const struct fuse_opt opts[], fuse_opt_proc_t proc) 422{ 423 int res; 424 struct fuse_opt_context ctx = { 425 .data = data, 426 .opt = opts, 427 .proc = proc, 428 }; 429 430 if (!args || !args->argv || !args->argc) { 431 return 0; 432 } 433 434 ctx.argc = args->argc; 435 ctx.argv = args->argv; 436 437 res = opt_parse(&ctx); 438 if (res != -1) { 439 struct fuse_args tmp = *args; 440 *args = ctx.outargs; 441 ctx.outargs = tmp; 442 } 443 free(ctx.opts); 444 fuse_opt_free_args(&ctx.outargs); 445 return res; 446}