qemu/tools/virtiofsd/fuse_opt.c
<<
>>
Prefs
   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}
 447