On 17/10/17(Tue) 14:26, Helg Bredow wrote: > [...] > I've split the patch. This one improves argument and option parsing so that > almost all sshfs arguments and options will now parse. It won't recognise > -ocache=no since fuse_opt_match() is incorrect (fuse will treat it the same > as -ocache=yes). I'll submit a patch for that some other time.
Nice. > Running check_sym tells me: > > No dynamic export changes > External reference changes: > added: > atoi > strchr > strsep > strstr > strtoul > > I take that to mean that there is no need to bump the major or minor version > for this patch. Is that correct? > That's correct. Regarding fuse options, I'd suggest writing more tests and fuzzing this code. Complex argument parsing is hard to get right. Comments inline. > Index: fuse_opt.c > =================================================================== > RCS file: /cvs/src/lib/libfuse/fuse_opt.c,v > retrieving revision 1.18 > diff -u -p -u -p -r1.18 fuse_opt.c > --- fuse_opt.c 4 Jan 2017 12:01:22 -0000 1.18 > +++ fuse_opt.c 17 Oct 2017 13:57:35 -0000 > @@ -222,57 +222,70 @@ static int > parse_opt(const struct fuse_opt *o, const char *val, void *data, > fuse_opt_proc_t f, struct fuse_args *arg) > { > - int found, ret, keyval; > + int keyval; > size_t idx; > > - ret = 0; > - found = 0; > - keyval = 0; > +#define DISCARD 0 > +#define KEEP 1 > +#define NEED_ANOTHER_ARG 2 I'd prefix these defines with _FOPT or w/o underscore if they are part of the API and move them on the top of the file since you use them in multiple functions. > > - /* check if it is a key=value entry */ > - idx = strcspn(val, "="); > - if (idx != strlen(val)) { > - idx++; > - keyval = 1; > - } > + if (o == NULL) > + return KEEP; I know it's note your code, but one-letter variable should be avoided. It's hard to understand what they represent. But let's not clutter this diff :) > + > + keyval = 0; > > for(; o->templ; o++) { > - if ((keyval && strncmp(val, o->templ, idx) == 0) || > - (!keyval && strcmp(val, o->templ) == 0)) { > + /* check key=value or -p n */ > + idx = strcspn(o->templ, "= "); > + > + if (strncmp(val, o->templ, idx) == 0) { > + if (o->templ[idx] == '=') { > + keyval = 1; > + val = &val[idx + 1]; How can you be sure that val[idx + 1] is valid? For example: o->temp = { 'k', 'e', 'y', '=', 'v', 'a', 'l', '\0' } val = { 'k', '\0' } This looks like an overflow to me. Well not yet, but when you dereference `val' below. > + } else if (o->templ[idx] == ' ') { > + keyval = 1; > + if (idx == strlen(val)) { > + /* ask for next arg to be included */ > + return NEED_ANOTHER_ARG; > + } else if (strchr(o->templ, '%') != NULL) { > + val = &val[idx]; Same here, how can you be sure that val[idx] is valid? > + } > + } > + > if (o->val == FUSE_OPT_KEY_DISCARD) > - return (1); > + return (DISCARD); > + > + if (o->val == FUSE_OPT_KEY_KEEP) > + return (KEEP); > > if (FUSE_OPT_IS_OPT_KEY(o)) { > - if (keyval) > - ret = f(data, &val[idx], o->val, arg); > - else > - ret = f(data, val, o->val, arg); > - } > + if (f == NULL) > + return KEEP; > > - if (o->off != ULONG_MAX && data && o->val >= 0) { > - ret = f(data, val, o->val, arg); > - int *addr = (int *)(data + o->off); > - *addr = o->val; > - ret = 0; > + return f(data, val, o->val, arg); > + } else if (data == NULL) { > + return -1; > + } else if (strchr(o->templ, '%') == NULL) { > + *((int *)(data + o->off)) = o->val; Are you sure you can simply deference "data + o->off" w/o sanity check? > + } else if (strstr(o->templ, "%u") != NULL) { > + *((unsigned int*)(data + o->off)) = atoi(val); > + } else if (strstr(o->templ, "%lu") != NULL) { > + *((unsigned long*)(data + o->off)) = > + strtoul(val, NULL, 0); > + } else if (strstr(o->templ, "%s") != NULL) { > + *((char **)(data + o->off)) = strdup(val); Same here. > + } else { > + /* TODO other parameterised templates */ > } > > - if (ret == -1) > - return (ret); > - if (ret == 1) > - fuse_opt_add_arg(arg, val); > - found++; > - break; > + return DISCARD; > } > } > > - if (!found) { > - ret = f(data, val, FUSE_OPT_KEY_OPT, arg); > - if (ret == 1) > - fuse_opt_add_arg(arg, val); > - return (ret); > - } > + if (f != NULL) > + return f(data, val, FUSE_OPT_KEY_OPT, arg); > > - return (ret); > + return (KEEP); > } > > /* > @@ -287,11 +300,12 @@ fuse_opt_parse(struct fuse_args *args, v > const struct fuse_opt *opt, fuse_opt_proc_t f) > { > struct fuse_args outargs; > - const char *arg; > - int ret = 0; > + const char *arg, *ap; > + char *optlist, *tofree; > + int ret; > int i; > > - if (!f || !args || !args->argc || !args->argv) > + if (!args || !args->argc || !args->argv) > return (0); > > bzero(&outargs, sizeof(outargs)); > @@ -299,28 +313,66 @@ fuse_opt_parse(struct fuse_args *args, v > > for (i = 1; i < args->argc; i++) { > arg = args->argv[i]; > + ret = 0; > > /* not - and not -- */ > if (arg[0] != '-') { > - ret = f(data, arg, FUSE_OPT_KEY_NONOPT, &outargs); > + if (f == NULL) > + ret = KEEP; > + else > + ret = f(data, arg, FUSE_OPT_KEY_NONOPT, > &outargs); > > - if (ret == 1) > + if (ret == KEEP) > fuse_opt_add_arg(&outargs, arg); > if (ret == -1) > goto err; > } else if (arg[1] == 'o') { > if (arg[2]) > arg += 2; /* -ofoo,bar */ > - else > - arg = args->argv[++i]; > + else { > + if (++i >= args->argc) > + goto err; > > - ret = parse_opt(opt, arg, data, f, &outargs); > + arg = args->argv[i]; > + } > + > + tofree = optlist = strdup(arg); > + if (optlist == NULL) > + goto err; > + > + while ((ap = strsep(&optlist, ",")) != NULL && > + ret != -1) { > + ret = parse_opt(opt, ap, data, f, &outargs); > + if (ret == KEEP) { > + fuse_opt_add_arg(&outargs, "-o"); > + fuse_opt_add_arg(&outargs, ap); > + } > + } > + > + free(tofree); > > if (ret == -1) > goto err; > } else { > ret = parse_opt(opt, arg, data, f, &outargs); > > + if (ret == KEEP) > + fuse_opt_add_arg(&outargs, arg); > + else if (ret == NEED_ANOTHER_ARG) { > + /* arg needs a value */ > + if (++i >= args->argc) > + goto err; > + > + if (asprintf(&tofree, "%s%s", arg, > + args->argv[i]) == -1) > + goto err; > + > + ret = parse_opt(opt, tofree, data, f, &outargs); > + if (ret == KEEP) > + fuse_opt_add_arg(&outargs, tofree); > + free(tofree); > + } > + > if (ret == -1) > goto err; > } > @@ -333,6 +385,8 @@ err: > args->allocated = outargs.allocated; > args->argc = outargs.argc; > args->argv = outargs.argv; > + if (ret != 0) > + ret = -1; > > return (ret); > } >