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);
>  }
> 

Reply via email to