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