1987da915Sopenharmony_ci/* 2987da915Sopenharmony_ci FUSE: Filesystem in Userspace 3987da915Sopenharmony_ci Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> 4987da915Sopenharmony_ci 5987da915Sopenharmony_ci This program can be distributed under the terms of the GNU LGPLv2. 6987da915Sopenharmony_ci See the file COPYING.LIB 7987da915Sopenharmony_ci*/ 8987da915Sopenharmony_ci 9987da915Sopenharmony_ci#include "config.h" 10987da915Sopenharmony_ci#include "fuse_opt.h" 11987da915Sopenharmony_ci 12987da915Sopenharmony_ci#include <stdio.h> 13987da915Sopenharmony_ci#include <stdlib.h> 14987da915Sopenharmony_ci#include <string.h> 15987da915Sopenharmony_ci#include <assert.h> 16987da915Sopenharmony_ci 17987da915Sopenharmony_cistruct fuse_opt_context { 18987da915Sopenharmony_ci void *data; 19987da915Sopenharmony_ci const struct fuse_opt *opt; 20987da915Sopenharmony_ci fuse_opt_proc_t proc; 21987da915Sopenharmony_ci int argctr; 22987da915Sopenharmony_ci int argc; 23987da915Sopenharmony_ci char **argv; 24987da915Sopenharmony_ci struct fuse_args outargs; 25987da915Sopenharmony_ci char *opts; 26987da915Sopenharmony_ci int nonopt; 27987da915Sopenharmony_ci}; 28987da915Sopenharmony_ci 29987da915Sopenharmony_civoid fuse_opt_free_args(struct fuse_args *args) 30987da915Sopenharmony_ci{ 31987da915Sopenharmony_ci if (args) { 32987da915Sopenharmony_ci if (args->argv && args->allocated) { 33987da915Sopenharmony_ci int i; 34987da915Sopenharmony_ci for (i = 0; i < args->argc; i++) 35987da915Sopenharmony_ci free(args->argv[i]); 36987da915Sopenharmony_ci free(args->argv); 37987da915Sopenharmony_ci } 38987da915Sopenharmony_ci args->argc = 0; 39987da915Sopenharmony_ci args->argv = NULL; 40987da915Sopenharmony_ci args->allocated = 0; 41987da915Sopenharmony_ci } 42987da915Sopenharmony_ci} 43987da915Sopenharmony_ci 44987da915Sopenharmony_cistatic int alloc_failed(void) 45987da915Sopenharmony_ci{ 46987da915Sopenharmony_ci fprintf(stderr, "fuse: memory allocation failed\n"); 47987da915Sopenharmony_ci return -1; 48987da915Sopenharmony_ci} 49987da915Sopenharmony_ci 50987da915Sopenharmony_ciint fuse_opt_add_arg(struct fuse_args *args, const char *arg) 51987da915Sopenharmony_ci{ 52987da915Sopenharmony_ci char **newargv; 53987da915Sopenharmony_ci char *newarg; 54987da915Sopenharmony_ci 55987da915Sopenharmony_ci assert(!args->argv || args->allocated); 56987da915Sopenharmony_ci 57987da915Sopenharmony_ci newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); 58987da915Sopenharmony_ci newarg = newargv ? strdup(arg) : NULL; 59987da915Sopenharmony_ci if (!newargv || !newarg) 60987da915Sopenharmony_ci return alloc_failed(); 61987da915Sopenharmony_ci 62987da915Sopenharmony_ci args->argv = newargv; 63987da915Sopenharmony_ci args->allocated = 1; 64987da915Sopenharmony_ci args->argv[args->argc++] = newarg; 65987da915Sopenharmony_ci args->argv[args->argc] = NULL; 66987da915Sopenharmony_ci return 0; 67987da915Sopenharmony_ci} 68987da915Sopenharmony_ci 69987da915Sopenharmony_ciint fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) 70987da915Sopenharmony_ci{ 71987da915Sopenharmony_ci assert(pos <= args->argc); 72987da915Sopenharmony_ci if (fuse_opt_add_arg(args, arg) == -1) 73987da915Sopenharmony_ci return -1; 74987da915Sopenharmony_ci 75987da915Sopenharmony_ci if (pos != args->argc - 1) { 76987da915Sopenharmony_ci char *newarg = args->argv[args->argc - 1]; 77987da915Sopenharmony_ci memmove(&args->argv[pos + 1], &args->argv[pos], 78987da915Sopenharmony_ci sizeof(char *) * (args->argc - pos - 1)); 79987da915Sopenharmony_ci args->argv[pos] = newarg; 80987da915Sopenharmony_ci } 81987da915Sopenharmony_ci return 0; 82987da915Sopenharmony_ci} 83987da915Sopenharmony_ci 84987da915Sopenharmony_cistatic int next_arg(struct fuse_opt_context *ctx, const char *opt) 85987da915Sopenharmony_ci{ 86987da915Sopenharmony_ci if (ctx->argctr + 1 >= ctx->argc) { 87987da915Sopenharmony_ci fprintf(stderr, "fuse: missing argument after `%s'\n", opt); 88987da915Sopenharmony_ci return -1; 89987da915Sopenharmony_ci } 90987da915Sopenharmony_ci ctx->argctr++; 91987da915Sopenharmony_ci return 0; 92987da915Sopenharmony_ci} 93987da915Sopenharmony_ci 94987da915Sopenharmony_cistatic int add_arg(struct fuse_opt_context *ctx, const char *arg) 95987da915Sopenharmony_ci{ 96987da915Sopenharmony_ci return fuse_opt_add_arg(&ctx->outargs, arg); 97987da915Sopenharmony_ci} 98987da915Sopenharmony_ci 99987da915Sopenharmony_ciint fuse_opt_add_opt(char **opts, const char *opt) 100987da915Sopenharmony_ci{ 101987da915Sopenharmony_ci char *newopts; 102987da915Sopenharmony_ci if (!*opts) 103987da915Sopenharmony_ci newopts = strdup(opt); 104987da915Sopenharmony_ci else { 105987da915Sopenharmony_ci unsigned oldlen = strlen(*opts); 106987da915Sopenharmony_ci newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1); 107987da915Sopenharmony_ci if (newopts) { 108987da915Sopenharmony_ci newopts[oldlen] = ','; 109987da915Sopenharmony_ci strcpy(newopts + oldlen + 1, opt); 110987da915Sopenharmony_ci } 111987da915Sopenharmony_ci } 112987da915Sopenharmony_ci if (!newopts) 113987da915Sopenharmony_ci return alloc_failed(); 114987da915Sopenharmony_ci 115987da915Sopenharmony_ci *opts = newopts; 116987da915Sopenharmony_ci return 0; 117987da915Sopenharmony_ci} 118987da915Sopenharmony_ci 119987da915Sopenharmony_cistatic int add_opt(struct fuse_opt_context *ctx, const char *opt) 120987da915Sopenharmony_ci{ 121987da915Sopenharmony_ci return fuse_opt_add_opt(&ctx->opts, opt); 122987da915Sopenharmony_ci} 123987da915Sopenharmony_ci 124987da915Sopenharmony_cistatic int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, 125987da915Sopenharmony_ci int iso) 126987da915Sopenharmony_ci{ 127987da915Sopenharmony_ci if (key == FUSE_OPT_KEY_DISCARD) 128987da915Sopenharmony_ci return 0; 129987da915Sopenharmony_ci 130987da915Sopenharmony_ci if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { 131987da915Sopenharmony_ci int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); 132987da915Sopenharmony_ci if (res == -1 || !res) 133987da915Sopenharmony_ci return res; 134987da915Sopenharmony_ci } 135987da915Sopenharmony_ci if (iso) 136987da915Sopenharmony_ci return add_opt(ctx, arg); 137987da915Sopenharmony_ci else 138987da915Sopenharmony_ci return add_arg(ctx, arg); 139987da915Sopenharmony_ci} 140987da915Sopenharmony_ci 141987da915Sopenharmony_cistatic int match_template(const char *t, const char *arg, unsigned *sepp) 142987da915Sopenharmony_ci{ 143987da915Sopenharmony_ci int arglen = strlen(arg); 144987da915Sopenharmony_ci const char *sep = strchr(t, '='); 145987da915Sopenharmony_ci sep = sep ? sep : strchr(t, ' '); 146987da915Sopenharmony_ci if (sep && (!sep[1] || sep[1] == '%')) { 147987da915Sopenharmony_ci int tlen = sep - t; 148987da915Sopenharmony_ci if (sep[0] == '=') 149987da915Sopenharmony_ci tlen ++; 150987da915Sopenharmony_ci if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { 151987da915Sopenharmony_ci *sepp = sep - t; 152987da915Sopenharmony_ci return 1; 153987da915Sopenharmony_ci } 154987da915Sopenharmony_ci } 155987da915Sopenharmony_ci if (strcmp(t, arg) == 0) { 156987da915Sopenharmony_ci *sepp = 0; 157987da915Sopenharmony_ci return 1; 158987da915Sopenharmony_ci } 159987da915Sopenharmony_ci return 0; 160987da915Sopenharmony_ci} 161987da915Sopenharmony_ci 162987da915Sopenharmony_cistatic const struct fuse_opt *find_opt(const struct fuse_opt *opt, 163987da915Sopenharmony_ci const char *arg, unsigned *sepp) 164987da915Sopenharmony_ci{ 165987da915Sopenharmony_ci for (; opt && opt->templ; opt++) 166987da915Sopenharmony_ci if (match_template(opt->templ, arg, sepp)) 167987da915Sopenharmony_ci return opt; 168987da915Sopenharmony_ci return NULL; 169987da915Sopenharmony_ci} 170987da915Sopenharmony_ci 171987da915Sopenharmony_ciint fuse_opt_match(const struct fuse_opt *opts, const char *opt) 172987da915Sopenharmony_ci{ 173987da915Sopenharmony_ci unsigned dummy; 174987da915Sopenharmony_ci return find_opt(opts, opt, &dummy) ? 1 : 0; 175987da915Sopenharmony_ci} 176987da915Sopenharmony_ci 177987da915Sopenharmony_cistatic int process_opt_param(void *var, const char *format, const char *param, 178987da915Sopenharmony_ci const char *arg) 179987da915Sopenharmony_ci{ 180987da915Sopenharmony_ci assert(format[0] == '%'); 181987da915Sopenharmony_ci if (format[1] == 's') { 182987da915Sopenharmony_ci char *copy = strdup(param); 183987da915Sopenharmony_ci if (!copy) 184987da915Sopenharmony_ci return alloc_failed(); 185987da915Sopenharmony_ci 186987da915Sopenharmony_ci *(char **) var = copy; 187987da915Sopenharmony_ci } else { 188987da915Sopenharmony_ci if (sscanf(param, format, var) != 1) { 189987da915Sopenharmony_ci fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg); 190987da915Sopenharmony_ci return -1; 191987da915Sopenharmony_ci } 192987da915Sopenharmony_ci } 193987da915Sopenharmony_ci return 0; 194987da915Sopenharmony_ci} 195987da915Sopenharmony_ci 196987da915Sopenharmony_cistatic int process_opt(struct fuse_opt_context *ctx, 197987da915Sopenharmony_ci const struct fuse_opt *opt, unsigned sep, 198987da915Sopenharmony_ci const char *arg, int iso) 199987da915Sopenharmony_ci{ 200987da915Sopenharmony_ci if (opt->offset == -1U) { 201987da915Sopenharmony_ci if (call_proc(ctx, arg, opt->value, iso) == -1) 202987da915Sopenharmony_ci return -1; 203987da915Sopenharmony_ci } else { 204987da915Sopenharmony_ci void *var = (char *)ctx->data + opt->offset; 205987da915Sopenharmony_ci if (sep && opt->templ[sep + 1]) { 206987da915Sopenharmony_ci const char *param = arg + sep; 207987da915Sopenharmony_ci if (opt->templ[sep] == '=') 208987da915Sopenharmony_ci param ++; 209987da915Sopenharmony_ci if (process_opt_param(var, opt->templ + sep + 1, 210987da915Sopenharmony_ci param, arg) == -1) 211987da915Sopenharmony_ci return -1; 212987da915Sopenharmony_ci } else 213987da915Sopenharmony_ci *(int *)var = opt->value; 214987da915Sopenharmony_ci } 215987da915Sopenharmony_ci return 0; 216987da915Sopenharmony_ci} 217987da915Sopenharmony_ci 218987da915Sopenharmony_cistatic int process_opt_sep_arg(struct fuse_opt_context *ctx, 219987da915Sopenharmony_ci const struct fuse_opt *opt, unsigned sep, 220987da915Sopenharmony_ci const char *arg, int iso) 221987da915Sopenharmony_ci{ 222987da915Sopenharmony_ci int res; 223987da915Sopenharmony_ci char *newarg; 224987da915Sopenharmony_ci char *param; 225987da915Sopenharmony_ci 226987da915Sopenharmony_ci if (next_arg(ctx, arg) == -1) 227987da915Sopenharmony_ci return -1; 228987da915Sopenharmony_ci 229987da915Sopenharmony_ci param = ctx->argv[ctx->argctr]; 230987da915Sopenharmony_ci newarg = malloc(sep + strlen(param) + 1); 231987da915Sopenharmony_ci if (!newarg) 232987da915Sopenharmony_ci return alloc_failed(); 233987da915Sopenharmony_ci 234987da915Sopenharmony_ci memcpy(newarg, arg, sep); 235987da915Sopenharmony_ci strcpy(newarg + sep, param); 236987da915Sopenharmony_ci res = process_opt(ctx, opt, sep, newarg, iso); 237987da915Sopenharmony_ci free(newarg); 238987da915Sopenharmony_ci 239987da915Sopenharmony_ci return res; 240987da915Sopenharmony_ci} 241987da915Sopenharmony_ci 242987da915Sopenharmony_cistatic int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) 243987da915Sopenharmony_ci{ 244987da915Sopenharmony_ci unsigned sep; 245987da915Sopenharmony_ci const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); 246987da915Sopenharmony_ci if (opt) { 247987da915Sopenharmony_ci for (; opt; opt = find_opt(opt + 1, arg, &sep)) { 248987da915Sopenharmony_ci int res; 249987da915Sopenharmony_ci if (sep && opt->templ[sep] == ' ' && !arg[sep]) 250987da915Sopenharmony_ci res = process_opt_sep_arg(ctx, opt, sep, arg, iso); 251987da915Sopenharmony_ci else 252987da915Sopenharmony_ci res = process_opt(ctx, opt, sep, arg, iso); 253987da915Sopenharmony_ci if (res == -1) 254987da915Sopenharmony_ci return -1; 255987da915Sopenharmony_ci } 256987da915Sopenharmony_ci return 0; 257987da915Sopenharmony_ci } else 258987da915Sopenharmony_ci return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); 259987da915Sopenharmony_ci} 260987da915Sopenharmony_ci 261987da915Sopenharmony_cistatic int process_real_option_group(struct fuse_opt_context *ctx, char *opts) 262987da915Sopenharmony_ci{ 263987da915Sopenharmony_ci char *sep; 264987da915Sopenharmony_ci 265987da915Sopenharmony_ci do { 266987da915Sopenharmony_ci int res; 267987da915Sopenharmony_ci#ifdef __SOLARIS__ 268987da915Sopenharmony_ci /* 269987da915Sopenharmony_ci * On Solaris, the device name contains commas, so the 270987da915Sopenharmony_ci * option "fsname" has to be the last one and its commas 271987da915Sopenharmony_ci * should not be interpreted as option separators. 272987da915Sopenharmony_ci * This had to be hardcoded because the option "fsname" 273987da915Sopenharmony_ci * may be found though not present in option list. 274987da915Sopenharmony_ci */ 275987da915Sopenharmony_ci if (!strncmp(opts,"fsname=",7)) 276987da915Sopenharmony_ci sep = (char*)NULL; 277987da915Sopenharmony_ci else 278987da915Sopenharmony_ci#endif /* __SOLARIS__ */ 279987da915Sopenharmony_ci { 280987da915Sopenharmony_ci sep = strchr(opts, ','); 281987da915Sopenharmony_ci if (sep) 282987da915Sopenharmony_ci *sep = '\0'; 283987da915Sopenharmony_ci } 284987da915Sopenharmony_ci res = process_gopt(ctx, opts, 1); 285987da915Sopenharmony_ci if (res == -1) 286987da915Sopenharmony_ci return -1; 287987da915Sopenharmony_ci opts = sep + 1; 288987da915Sopenharmony_ci } while (sep); 289987da915Sopenharmony_ci 290987da915Sopenharmony_ci return 0; 291987da915Sopenharmony_ci} 292987da915Sopenharmony_ci 293987da915Sopenharmony_cistatic int process_option_group(struct fuse_opt_context *ctx, const char *opts) 294987da915Sopenharmony_ci{ 295987da915Sopenharmony_ci int res; 296987da915Sopenharmony_ci char *copy; 297987da915Sopenharmony_ci const char *sep = strchr(opts, ','); 298987da915Sopenharmony_ci if (!sep) 299987da915Sopenharmony_ci return process_gopt(ctx, opts, 1); 300987da915Sopenharmony_ci 301987da915Sopenharmony_ci copy = strdup(opts); 302987da915Sopenharmony_ci if (!copy) { 303987da915Sopenharmony_ci fprintf(stderr, "fuse: memory allocation failed\n"); 304987da915Sopenharmony_ci return -1; 305987da915Sopenharmony_ci } 306987da915Sopenharmony_ci res = process_real_option_group(ctx, copy); 307987da915Sopenharmony_ci free(copy); 308987da915Sopenharmony_ci return res; 309987da915Sopenharmony_ci} 310987da915Sopenharmony_ci 311987da915Sopenharmony_cistatic int process_one(struct fuse_opt_context *ctx, const char *arg) 312987da915Sopenharmony_ci{ 313987da915Sopenharmony_ci if (ctx->nonopt || arg[0] != '-') 314987da915Sopenharmony_ci return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); 315987da915Sopenharmony_ci else if (arg[1] == 'o') { 316987da915Sopenharmony_ci if (arg[2]) 317987da915Sopenharmony_ci return process_option_group(ctx, arg + 2); 318987da915Sopenharmony_ci else { 319987da915Sopenharmony_ci if (next_arg(ctx, arg) == -1) 320987da915Sopenharmony_ci return -1; 321987da915Sopenharmony_ci 322987da915Sopenharmony_ci return process_option_group(ctx, ctx->argv[ctx->argctr]); 323987da915Sopenharmony_ci } 324987da915Sopenharmony_ci } else if (arg[1] == '-' && !arg[2]) { 325987da915Sopenharmony_ci if (add_arg(ctx, arg) == -1) 326987da915Sopenharmony_ci return -1; 327987da915Sopenharmony_ci ctx->nonopt = ctx->outargs.argc; 328987da915Sopenharmony_ci return 0; 329987da915Sopenharmony_ci } else 330987da915Sopenharmony_ci return process_gopt(ctx, arg, 0); 331987da915Sopenharmony_ci} 332987da915Sopenharmony_ci 333987da915Sopenharmony_cistatic int opt_parse(struct fuse_opt_context *ctx) 334987da915Sopenharmony_ci{ 335987da915Sopenharmony_ci if (ctx->argc) { 336987da915Sopenharmony_ci if (add_arg(ctx, ctx->argv[0]) == -1) 337987da915Sopenharmony_ci return -1; 338987da915Sopenharmony_ci } 339987da915Sopenharmony_ci 340987da915Sopenharmony_ci for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) 341987da915Sopenharmony_ci if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) 342987da915Sopenharmony_ci return -1; 343987da915Sopenharmony_ci 344987da915Sopenharmony_ci if (ctx->opts) { 345987da915Sopenharmony_ci if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || 346987da915Sopenharmony_ci fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) 347987da915Sopenharmony_ci return -1; 348987da915Sopenharmony_ci } 349987da915Sopenharmony_ci if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) { 350987da915Sopenharmony_ci free(ctx->outargs.argv[ctx->outargs.argc - 1]); 351987da915Sopenharmony_ci ctx->outargs.argv[--ctx->outargs.argc] = NULL; 352987da915Sopenharmony_ci } 353987da915Sopenharmony_ci 354987da915Sopenharmony_ci return 0; 355987da915Sopenharmony_ci} 356987da915Sopenharmony_ci 357987da915Sopenharmony_ciint fuse_opt_parse(struct fuse_args *args, void *data, 358987da915Sopenharmony_ci const struct fuse_opt opts[], fuse_opt_proc_t proc) 359987da915Sopenharmony_ci{ 360987da915Sopenharmony_ci int res; 361987da915Sopenharmony_ci struct fuse_opt_context ctx = { 362987da915Sopenharmony_ci .data = data, 363987da915Sopenharmony_ci .opt = opts, 364987da915Sopenharmony_ci .proc = proc, 365987da915Sopenharmony_ci }; 366987da915Sopenharmony_ci 367987da915Sopenharmony_ci if (!args || !args->argv || !args->argc) 368987da915Sopenharmony_ci return 0; 369987da915Sopenharmony_ci 370987da915Sopenharmony_ci ctx.argc = args->argc; 371987da915Sopenharmony_ci ctx.argv = args->argv; 372987da915Sopenharmony_ci 373987da915Sopenharmony_ci res = opt_parse(&ctx); 374987da915Sopenharmony_ci if (res != -1) { 375987da915Sopenharmony_ci struct fuse_args tmp = *args; 376987da915Sopenharmony_ci *args = ctx.outargs; 377987da915Sopenharmony_ci ctx.outargs = tmp; 378987da915Sopenharmony_ci } 379987da915Sopenharmony_ci free(ctx.opts); 380987da915Sopenharmony_ci fuse_opt_free_args(&ctx.outargs); 381987da915Sopenharmony_ci return res; 382987da915Sopenharmony_ci} 383