1 /* $OpenBSD: fuse-opt-parse.c,v 1.4 2018/07/20 12:05:08 helg Exp $ */ 2 /* 3 * Copyright (c) 2017 Helg Bredow <helg@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <assert.h> 19 #include <fuse_opt.h> 20 #include <stddef.h> 21 #include <stdlib.h> 22 #include <string.h> 23 24 struct data { 25 int port; 26 char *fsname; 27 char *x; 28 char *optstring; 29 int debug; 30 int foreground; 31 int noatime; 32 int ssh_ver; 33 int count; 34 int cache; 35 int set_gid; 36 int gid; 37 int bad_opt; 38 }; 39 40 enum { 41 KEY_DEBUG, 42 KEY_NOATIME, 43 KEY_PORT 44 }; 45 46 #define DATA_OPT(o,m,v) {o, offsetof(struct data, m), v} 47 48 struct fuse_opt opts[] = { 49 FUSE_OPT_KEY("noatime", KEY_NOATIME ), 50 51 DATA_OPT("optstring=%s", optstring, 0), 52 DATA_OPT("-f=%s", fsname, 0), 53 DATA_OPT("-x %s", x, 0), 54 DATA_OPT("--count=%u", count, 0), 55 DATA_OPT("-1", ssh_ver, 5), 56 DATA_OPT("cache=yes", cache, 1), 57 DATA_OPT("cache=no", cache, 0), 58 DATA_OPT("debug", debug, 1), 59 DATA_OPT("debug", foreground, 1), 60 FUSE_OPT_KEY("debug", KEY_DEBUG ), 61 FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), 62 DATA_OPT("-p", port, 25), 63 FUSE_OPT_KEY("-p ", KEY_PORT ), 64 DATA_OPT("gid=", set_gid, 1), 65 DATA_OPT("gid=%o", gid, 1), 66 67 FUSE_OPT_END 68 }; 69 70 int 71 proc(void *data, const char *arg, int key, struct fuse_args *args) 72 { 73 struct data *conf = data; 74 75 if (conf == NULL) 76 return (1); 77 78 switch (key) 79 { 80 case KEY_PORT: 81 conf->port = atoi(&arg[2]); 82 return (0); 83 case KEY_DEBUG: 84 conf->debug = 2; 85 return (0); 86 case KEY_NOATIME: 87 conf->noatime = 1; 88 return (1); 89 } 90 91 if (strcmp("bad_opt", arg) == 0) { 92 conf->bad_opt = 1; 93 return (0); 94 } 95 96 return (1); 97 } 98 99 /* 100 * A NULL 'args' is equivalent to an empty argument vector. 101 */ 102 void 103 test_null_args(void) { 104 struct data data; 105 struct fuse_args args; 106 107 bzero(&data, sizeof(data)); 108 109 assert(fuse_opt_parse(NULL, &data, opts, proc) == 0); 110 111 assert(data.port == 0); 112 assert(data.fsname == NULL); 113 assert(data.x == NULL); 114 assert(data.optstring == NULL); 115 assert(data.debug == 0); 116 assert(data.noatime == 0); 117 assert(data.ssh_ver == 0); 118 assert(data.count == 0); 119 assert(data.cache == 0); 120 assert(data.bad_opt == 0); 121 } 122 123 /* 124 * A NULL 'opts' is equivalent to an 'opts' array containing a single 125 * end marker. 126 */ 127 void 128 test_null_opts(void) 129 { 130 struct data data; 131 struct fuse_args args; 132 133 char *argv_null_opts[] = { 134 "progname", 135 "/mnt", 136 "bad_opt" 137 }; 138 139 args.argc = sizeof(argv_null_opts) / sizeof(argv_null_opts[0]); 140 args.argv = argv_null_opts; 141 args.allocated = 0; 142 143 bzero(&data, sizeof(data)); 144 145 assert(fuse_opt_parse(&args, &data, NULL, proc) == 0); 146 147 assert(data.port == 0); 148 assert(data.fsname == NULL); 149 assert(data.x == NULL); 150 assert(data.optstring == NULL); 151 assert(data.debug == 0); 152 assert(data.noatime == 0); 153 assert(data.ssh_ver == 0); 154 assert(data.count == 0); 155 assert(data.cache == 0); 156 assert(data.set_gid == 0); 157 assert(data.gid == 0); 158 assert(data.bad_opt == 1); 159 160 assert(args.argc == 2); 161 assert(strcmp(args.argv[0], "progname") == 0); 162 assert(strcmp(args.argv[1], "/mnt") == 0); 163 assert(args.allocated); 164 165 fuse_opt_free_args(&args); 166 } 167 168 /* 169 * A NULL 'proc' is equivalent to a processing function always returning '1'. 170 */ 171 void 172 test_null_proc(void) 173 { 174 struct data data; 175 struct fuse_args args; 176 177 char *argv_null_proc[] = { 178 "progname", 179 "-odebug,noatime,gid=077", 180 "-d", 181 "-p", "22", 182 "/mnt", 183 "-f=filename", 184 "-1", 185 "-x", "xanadu", 186 "-o", "optstring=", 187 "-o", "optstring=optstring", 188 "--count=10", 189 "bad_opt" 190 }; 191 192 args.argc = sizeof(argv_null_proc) / sizeof(argv_null_proc[0]); 193 args.argv = argv_null_proc; 194 args.allocated = 0; 195 196 bzero(&data, sizeof(data)); 197 198 assert(fuse_opt_parse(&args, &data, opts, NULL) == 0); 199 200 assert(data.port == 25); 201 assert(strcmp(data.fsname, "filename") == 0); 202 assert(strcmp(data.x, "xanadu") == 0); 203 assert(strcmp(data.optstring, "optstring") == 0); 204 assert(data.debug == 1); 205 assert(data.noatime == 0); 206 assert(data.ssh_ver == 5); 207 assert(data.count == 10); 208 assert(data.cache == 0); 209 assert(data.set_gid == 1); 210 assert(data.gid == 077); 211 assert(data.bad_opt == 0); 212 213 assert(args.argc == 9); 214 assert(strcmp(args.argv[0], "progname") == 0); 215 assert(strcmp(args.argv[1], "-o") == 0); 216 assert(strcmp(args.argv[2], "debug") == 0); 217 assert(strcmp(args.argv[3], "-o") == 0); 218 assert(strcmp(args.argv[4], "noatime") == 0); 219 assert(strcmp(args.argv[5], "-d") == 0); 220 assert(strcmp(args.argv[6], "-p22") == 0); 221 assert(strcmp(args.argv[7], "/mnt") == 0); 222 assert(strcmp(args.argv[8], "bad_opt") == 0); 223 assert(args.allocated); 224 225 fuse_opt_free_args(&args); 226 } 227 228 /* 229 * Test with all args supplied to fuse_opt_parse. 230 */ 231 void 232 test_all_args(void) 233 { 234 struct data data; 235 struct fuse_args args; 236 237 char *argv[] = { 238 "progname", 239 "-odebug,noatime,gid=077", 240 "-d", 241 "-p", "22", 242 "/mnt", 243 "-f=filename", 244 "-1", 245 "-x", "xanadu", 246 "-o", "optstring=optstring,cache=no", 247 "--count=10", 248 "bad_opt" 249 }; 250 251 args.argc = sizeof(argv) / sizeof(argv[0]); 252 args.argv = argv; 253 args.allocated = 0; 254 255 bzero(&data, sizeof(data)); 256 257 assert(fuse_opt_parse(&args, &data, opts, proc) == 0); 258 259 assert(data.port == 22); 260 assert(strcmp(data.fsname, "filename") == 0); 261 assert(strcmp(data.x, "xanadu") == 0); 262 assert(strcmp(data.optstring, "optstring") == 0); 263 assert(data.debug == 2); 264 assert(data.noatime == 1); 265 assert(data.ssh_ver == 5); 266 assert(data.count == 10); 267 assert(data.cache == 0); 268 assert(data.set_gid == 1); 269 assert(data.gid == 077); 270 assert(data.bad_opt == 1); 271 272 assert(args.argc == 7); 273 assert(strcmp(args.argv[0], "progname") == 0); 274 assert(strcmp(args.argv[1], "-o") == 0); 275 assert(strcmp(args.argv[2], "debug") == 0); 276 assert(strcmp(args.argv[3], "-o") == 0); 277 assert(strcmp(args.argv[4], "noatime") == 0); 278 assert(strcmp(args.argv[5], "-d") == 0); 279 assert(strcmp(args.argv[6], "/mnt") == 0); 280 assert(args.allocated); 281 282 fuse_opt_free_args(&args); 283 } 284 285 int 286 main(void) 287 { 288 test_null_opts(); 289 test_null_args(); 290 test_null_proc(); 291 test_all_args(); 292 293 return (0); 294 } 295