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
proc(void * data,const char * arg,int key,struct fuse_args * args)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
test_null_args(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
test_null_opts(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
test_null_proc(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
test_all_args(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
main(void)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