xref: /openbsd-src/lib/libfuse/fuse.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: fuse.c,v 1.28 2016/05/24 19:24:46 okan Exp $ */
2 /*
3  * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
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 <sys/wait.h>
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 
22 #include <miscfs/fuse/fusefs.h>
23 
24 #include <errno.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 
30 #include "fuse_opt.h"
31 #include "fuse_private.h"
32 #include "debug.h"
33 
34 static struct fuse_session *sigse;
35 static struct fuse_context *ictx = NULL;
36 static int max_read = FUSEBUFMAXSIZE;
37 
38 enum {
39 	KEY_HELP,
40 	KEY_HELP_WITHOUT_HEADER,
41 	KEY_VERSION,
42 	KEY_MAXREAD,
43 	KEY_STUB
44 };
45 
46 static struct fuse_opt fuse_core_opts[] = {
47 	FUSE_OPT_KEY("-h",			KEY_HELP),
48 	FUSE_OPT_KEY("--help",			KEY_HELP),
49 	FUSE_OPT_KEY("-ho",			KEY_HELP_WITHOUT_HEADER),
50 	FUSE_OPT_KEY("-V",			KEY_VERSION),
51 	FUSE_OPT_KEY("--version",		KEY_VERSION),
52 	FUSE_OPT_KEY("max_read=",		KEY_MAXREAD),
53 	FUSE_OPT_KEY("debug",			KEY_STUB),
54 	FUSE_OPT_KEY("-d",			KEY_STUB),
55 	FUSE_OPT_KEY("-f",			KEY_STUB),
56 	FUSE_OPT_KEY("-s",			KEY_STUB),
57 	FUSE_OPT_KEY("use_ino",			KEY_STUB),
58 	FUSE_OPT_KEY("big_writes",		KEY_STUB),
59 	FUSE_OPT_KEY("default_permissions",	KEY_STUB),
60 	FUSE_OPT_KEY("fsname=",			KEY_STUB),
61 	FUSE_OPT_END
62 };
63 
64 int
65 fuse_loop(struct fuse *fuse)
66 {
67 	struct fusebuf fbuf;
68 	struct fuse_context ctx;
69 	struct fb_ioctl_xch ioexch;
70 	struct kevent ev;
71 	ssize_t n;
72 	int ret;
73 
74 	fuse->fc->kq = kqueue();
75 	if (fuse->fc->kq == -1)
76 		return (-1);
77 
78 	EV_SET(&fuse->fc->event, fuse->fc->fd, EVFILT_READ, EV_ADD |
79 	    EV_ENABLE, 0, 0, 0);
80 
81 	while (!fuse->fc->dead) {
82 		ret = kevent(fuse->fc->kq, &fuse->fc->event, 1, &ev, 1, NULL);
83 		if (ret == -1)
84 			DPERROR(__func__);
85 		else if (ret > 0) {
86 			n = read(fuse->fc->fd, &fbuf, sizeof(fbuf));
87 			if (n != sizeof(fbuf)) {
88 				fprintf(stderr, "%s: bad fusebuf read\n",
89 				    __func__);
90 				return (-1);
91 			}
92 
93 			/* check if there is data something present */
94 			if (fbuf.fb_len) {
95 				fbuf.fb_dat = malloc(fbuf.fb_len);
96 				if (fbuf.fb_dat == NULL)
97 					return (-1);
98 				ioexch.fbxch_uuid = fbuf.fb_uuid;
99 				ioexch.fbxch_len = fbuf.fb_len;
100 				ioexch.fbxch_data = fbuf.fb_dat;
101 
102 				if (ioctl(fuse->fc->fd, FIOCGETFBDAT,
103 				    &ioexch)) {
104 					free(fbuf.fb_dat);
105 					return (-1);
106 				}
107 			}
108 
109 			ctx.fuse = fuse;
110 			ctx.uid = fuse->conf.uid;
111 			ctx.gid = fuse->conf.gid;
112 			ctx.pid = fuse->conf.pid;
113 			ctx.umask = fuse->conf.umask;
114 			ctx.private_data = fuse->private_data;
115 			ictx = &ctx;
116 
117 			ret = ifuse_exec_opcode(fuse, &fbuf);
118 			if (ret) {
119 				ictx = NULL;
120 				return (-1);
121 			}
122 
123 			n = write(fuse->fc->fd, &fbuf, sizeof(fbuf));
124 			if (fbuf.fb_len) {
125 				if (fbuf.fb_dat == NULL) {
126 					fprintf(stderr, "%s: fb_dat is Null\n",
127 					    __func__);
128 					return (-1);
129 				}
130 				ioexch.fbxch_uuid = fbuf.fb_uuid;
131 				ioexch.fbxch_len = fbuf.fb_len;
132 				ioexch.fbxch_data = fbuf.fb_dat;
133 
134 				if (ioctl(fuse->fc->fd, FIOCSETFBDAT, &ioexch)) {
135 					free(fbuf.fb_dat);
136 					return (-1);
137 				}
138 				free(fbuf.fb_dat);
139 			}
140 			ictx = NULL;
141 
142 			if (n != FUSEBUFSIZE) {
143 				errno = EINVAL;
144 				return (-1);
145 			}
146 		}
147 	}
148 
149 	return (0);
150 }
151 
152 struct fuse_chan *
153 fuse_mount(const char *dir, unused struct fuse_args *args)
154 {
155 	struct fusefs_args fargs;
156 	struct fuse_chan *fc;
157 	const char *errcause;
158 
159 	fc = calloc(1, sizeof(*fc));
160 	if (fc == NULL)
161 		return (NULL);
162 
163 	fc->dir = realpath(dir, NULL);
164 	if (fc->dir == NULL)
165 		goto bad;
166 
167 	if ((fc->fd = open("/dev/fuse0", O_RDWR)) == -1) {
168 		perror(__func__);
169 		goto bad;
170 	}
171 
172 	fargs.fd = fc->fd;
173 	fargs.max_read = max_read;
174 	if (mount(MOUNT_FUSEFS, fc->dir, 0, &fargs)) {
175 		switch (errno) {
176 		case EMFILE:
177 			errcause = "mount table full";
178 			break;
179 		case EOPNOTSUPP:
180 			errcause = "filesystem not supported by kernel";
181 			break;
182 		default:
183 			errcause = strerror(errno);
184 			break;
185 		}
186 		fprintf(stderr, "%s on %s: %s\n", __func__, dir, errcause);
187 		goto bad;
188 	}
189 
190 	return (fc);
191 bad:
192 	if (fc->fd != -1)
193 		close(fc->fd);
194 	free(fc->dir);
195 	free(fc);
196 	return (NULL);
197 }
198 
199 void
200 fuse_unmount(const char *dir, unused struct fuse_chan *ch)
201 {
202 	if (ch->dead)
203 		return;
204 
205 	if (unmount(dir, MNT_UPDATE) == -1)
206 		DPERROR(__func__);
207 }
208 
209 int
210 fuse_is_lib_option(unused const char *opt)
211 {
212 	return (fuse_opt_match(fuse_core_opts, opt));
213 }
214 
215 int
216 fuse_chan_fd(struct fuse_chan *ch)
217 {
218 	return (ch->fd);
219 }
220 
221 struct fuse_session *
222 fuse_get_session(struct fuse *f)
223 {
224 	return (&f->se);
225 }
226 
227 int
228 fuse_loop_mt(unused struct fuse *fuse)
229 {
230 	return (0);
231 }
232 
233 struct fuse *
234 fuse_new(struct fuse_chan *fc, unused struct fuse_args *args,
235     const struct fuse_operations *ops, unused size_t size,
236     unused void *userdata)
237 {
238 	struct fuse *fuse;
239 	struct fuse_vnode *root;
240 
241 	if ((fuse = calloc(1, sizeof(*fuse))) == NULL)
242 		return (NULL);
243 
244 	/* copy fuse ops to their own structure */
245 	memcpy(&fuse->op, ops, sizeof(fuse->op));
246 
247 	fuse->fc = fc;
248 	fuse->max_ino = FUSE_ROOT_INO;
249 	fuse->se.args = fuse;
250 	fuse->private_data = userdata;
251 
252 	if ((root = alloc_vn(fuse, "/", FUSE_ROOT_INO, 0)) == NULL) {
253 		free(fuse);
254 		return (NULL);
255 	}
256 
257 	tree_init(&fuse->vnode_tree);
258 	tree_init(&fuse->name_tree);
259 	if (!set_vn(fuse, root)) {
260 		free(fuse);
261 		return (NULL);
262 	}
263 
264 	return (fuse);
265 }
266 
267 int
268 fuse_daemonize(unused int foreground)
269 {
270 #ifdef DEBUG
271 	return (daemon(0,1));
272 #else
273 	return (daemon(0,0));
274 #endif
275 }
276 
277 void
278 fuse_destroy(unused struct fuse *f)
279 {
280 	close(f->fc->fd);
281 	free(f->fc->dir);
282 	free(f->fc);
283 	free(f);
284 }
285 
286 static void
287 ifuse_get_signal(unused int num)
288 {
289 	struct fuse *f;
290 	pid_t child;
291 	int status;
292 
293 	if (sigse != NULL) {
294 		child = fork();
295 
296 		if (child < 0)
297 			return ;
298 
299 		f = sigse->args;
300 		if (child == 0) {
301 			fuse_unmount(f->fc->dir, f->fc);
302 			sigse = NULL;
303 			exit(0);
304 		}
305 
306 		fuse_loop(f);
307 		wait(&status);
308 	}
309 }
310 
311 void
312 fuse_remove_signal_handlers(unused struct fuse_session *se)
313 {
314 	sigse = NULL;
315 	signal(SIGHUP, SIG_DFL);
316 	signal(SIGINT, SIG_DFL);
317 	signal(SIGTERM, SIG_DFL);
318 	signal(SIGPIPE, SIG_DFL);
319 }
320 
321 int
322 fuse_set_signal_handlers(unused struct fuse_session *se)
323 {
324 	sigse = se;
325 	signal(SIGHUP, ifuse_get_signal);
326 	signal(SIGINT, ifuse_get_signal);
327 	signal(SIGTERM, ifuse_get_signal);
328 	signal(SIGPIPE, SIG_IGN);
329 	return (0);
330 }
331 
332 static void
333 dump_help(void)
334 {
335 	fprintf(stderr, "FUSE options:\n"
336 	    "    -d   -o debug          enable debug output (implies -f)\n"
337 	    "    -V                     print fuse version\n"
338 	    "\n");
339 }
340 
341 static void
342 dump_version(void)
343 {
344 	fprintf(stderr, "FUSE library version %i\n", FUSE_USE_VERSION);
345 }
346 
347 static int
348 ifuse_process_opt(void *data, const char *arg, int key,
349     unused struct fuse_args *args)
350 {
351 	struct fuse_core_opt *opt = data;
352 	struct stat st;
353 	const char *err;
354 	int res;
355 
356 	switch (key) {
357 		case KEY_STUB:
358 			return (0);
359 		case KEY_HELP:
360 		case KEY_HELP_WITHOUT_HEADER:
361 			dump_help();
362 			return (-1);
363 		case KEY_VERSION:
364 			dump_version();
365 			return (-1);
366 		case KEY_MAXREAD:
367 			res = strtonum(arg, 0, FUSEBUFMAXSIZE, &err);
368 			if (err) {
369 				fprintf(stderr, "fuse: max_read %s\n", err);
370 				return (-1);
371 			}
372 			max_read = res;
373 			break;
374 		case FUSE_OPT_KEY_NONOPT:
375 			if (opt->mp == NULL) {
376 				opt->mp = realpath(arg, opt->mp);
377 				if (opt->mp == NULL) {
378 					fprintf(stderr, "fuse: realpath: "
379 					    "%s : %s\n", arg, strerror(errno));
380 					return (-1);
381 				}
382 
383 				res = stat(opt->mp, &st);
384 				if (res == -1) {
385 					fprintf(stderr, "fuse: bad mount point "
386 					    "%s : %s\n", arg, strerror(errno));
387 					return (-1);
388 				}
389 
390 				if (!S_ISDIR(st.st_mode)) {
391 					fprintf(stderr, "fuse: bad mount point "
392 					    "%s : %s\n", arg,
393 					    strerror(ENOTDIR));
394 					return (-1);
395 				}
396 			} else {
397 				fprintf(stderr, "fuse: invalid argument %s\n",
398 				    arg);
399 				return (-1);
400 			}
401 			break;
402 		default:
403 			fprintf(stderr, "fuse: unknown option %s\n", arg);
404 			return (-1);
405 	}
406 	return (0);
407 }
408 
409 int
410 fuse_parse_cmdline(struct fuse_args *args, char **mp, int *mt, unused int *fg)
411 {
412 	struct fuse_core_opt opt;
413 
414 #ifdef DEBUG
415 	ifuse_debug_init();
416 #endif
417 	bzero(&opt, sizeof(opt));
418 	if (fuse_opt_parse(args, &opt, fuse_core_opts, ifuse_process_opt) == -1)
419 		return (-1);
420 
421 	if (opt.mp == NULL) {
422 		fprintf(stderr, "fuse: missing mountpoint parameter\n");
423 		return (-1);
424 	}
425 
426 	*mp = strdup(opt.mp);
427 	if (*mp == NULL)
428 		return (-1);
429 	*mt = 0;
430 
431 	return (0);
432 }
433 
434 struct fuse_context *
435 fuse_get_context(void)
436 {
437 	return (ictx);
438 }
439 
440 int
441 fuse_version(void)
442 {
443 	return (FUSE_VERSION);
444 }
445 
446 void
447 fuse_teardown(struct fuse *fuse, char *mp)
448 {
449 	fuse_unmount(mp, fuse->fc);
450 	fuse_destroy(fuse);
451 }
452 
453 int
454 fuse_invalidate(unused struct fuse *f, unused const char *path)
455 {
456 	return (EINVAL);
457 }
458 
459 struct fuse *
460 fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
461     size_t size, char **mp, int *mt, void *data)
462 {
463 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
464 	struct fuse_chan *fc;
465 	struct fuse *fuse;
466 	int fg;
467 
468 	if (fuse_parse_cmdline(&args, mp, mt, &fg))
469 		goto err;
470 
471 	fuse_daemonize(0);
472 
473 	if ((fc = fuse_mount(*mp, NULL)) == NULL)
474 		goto err;
475 
476 	if ((fuse = fuse_new(fc, NULL, ops, size, data)) == NULL) {
477 		free(fc);
478 		goto err;
479 	}
480 
481 	return (fuse);
482 err:
483 	free(*mp);
484 	return (NULL);
485 }
486 
487 int
488 fuse_main(int argc, char **argv, const struct fuse_operations *ops, void *data)
489 {
490 	struct fuse *fuse;
491 	char *mp = NULL;
492 	int mt;
493 
494 	fuse = fuse_setup(argc, argv, ops, sizeof(*ops), &mp, &mt, data);
495 	if (!fuse)
496 		return (-1);
497 
498 	return (fuse_loop(fuse));
499 }
500