xref: /openbsd-src/lib/libfuse/fuse.c (revision df69c215c7c66baf660f3f65414fd34796c96152)
1*df69c215Sderaadt /* $OpenBSD: fuse.c,v 1.51 2019/06/28 13:32:42 deraadt Exp $ */
275af46c2Stedu /*
375af46c2Stedu  * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
475af46c2Stedu  *
575af46c2Stedu  * Permission to use, copy, modify, and distribute this software for any
675af46c2Stedu  * purpose with or without fee is hereby granted, provided that the above
775af46c2Stedu  * copyright notice and this permission notice appear in all copies.
875af46c2Stedu  *
975af46c2Stedu  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1075af46c2Stedu  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1175af46c2Stedu  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1275af46c2Stedu  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1375af46c2Stedu  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1475af46c2Stedu  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1575af46c2Stedu  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1675af46c2Stedu  */
1775af46c2Stedu 
1875af46c2Stedu #include <sys/wait.h>
198dbfc628Ssyl #include <sys/types.h>
208dbfc628Ssyl #include <sys/ioctl.h>
218dbfc628Ssyl 
228dbfc628Ssyl #include <miscfs/fuse/fusefs.h>
2375af46c2Stedu 
2475af46c2Stedu #include <errno.h>
2575af46c2Stedu #include <signal.h>
2658efe7d1Shelg #include <stddef.h>
2775af46c2Stedu #include <stdlib.h>
2875af46c2Stedu #include <string.h>
2975af46c2Stedu #include <unistd.h>
3075af46c2Stedu 
3188a7ba5eStedu #include "fuse_opt.h"
3275af46c2Stedu #include "fuse_private.h"
3375af46c2Stedu #include "debug.h"
3475af46c2Stedu 
355f1aa8d9Ssyl static struct fuse_context *ictx = NULL;
3675af46c2Stedu 
37f8cc4b14Ssyl enum {
38c839d22fShelg 	KEY_DEBUG,
39c286f01cShelg 	KEY_FOREGROUND,
40f8cc4b14Ssyl 	KEY_HELP,
41f8cc4b14Ssyl 	KEY_HELP_WITHOUT_HEADER,
42f8cc4b14Ssyl 	KEY_VERSION,
431b8c4fd6Ssyl 	KEY_MAXREAD,
441b8c4fd6Ssyl 	KEY_STUB
45f8cc4b14Ssyl };
46f8cc4b14Ssyl 
4758efe7d1Shelg /* options supported by fuse_parse_cmdline */
48f8cc4b14Ssyl static struct fuse_opt fuse_core_opts[] = {
4958efe7d1Shelg 	FUSE_OPT_KEY("-d",			KEY_DEBUG),
5058efe7d1Shelg 	FUSE_OPT_KEY("debug",			KEY_DEBUG),
5158efe7d1Shelg 	FUSE_OPT_KEY("-f",			KEY_FOREGROUND),
52f8cc4b14Ssyl 	FUSE_OPT_KEY("-h",			KEY_HELP),
53f8cc4b14Ssyl 	FUSE_OPT_KEY("--help",			KEY_HELP),
54f8cc4b14Ssyl 	FUSE_OPT_KEY("-ho",			KEY_HELP_WITHOUT_HEADER),
5558efe7d1Shelg 	FUSE_OPT_KEY("-s",			KEY_STUB),
56f8cc4b14Ssyl 	FUSE_OPT_KEY("-V",			KEY_VERSION),
57f8cc4b14Ssyl 	FUSE_OPT_KEY("--version",		KEY_VERSION),
5858efe7d1Shelg 	FUSE_OPT_END
5958efe7d1Shelg };
6058efe7d1Shelg 
6158efe7d1Shelg /* options supported by fuse_new */
6258efe7d1Shelg #define FUSE_LIB_OPT(o, m) {o, offsetof(struct fuse_config, m), 1}
6358efe7d1Shelg static struct fuse_opt fuse_lib_opts[] = {
6458efe7d1Shelg 	FUSE_OPT_KEY("ac_attr_timeout=",	KEY_STUB),
6558efe7d1Shelg 	FUSE_OPT_KEY("attr_timeout=",		KEY_STUB),
6658efe7d1Shelg 	FUSE_OPT_KEY("auto_cache",		KEY_STUB),
6758efe7d1Shelg 	FUSE_OPT_KEY("noauto_cache",		KEY_STUB),
6858efe7d1Shelg 	FUSE_OPT_KEY("big_writes",		KEY_STUB),
69c839d22fShelg 	FUSE_OPT_KEY("debug",			KEY_DEBUG),
70c839d22fShelg 	FUSE_OPT_KEY("-d",			KEY_DEBUG),
7158efe7d1Shelg 	FUSE_OPT_KEY("entry_timeout=",		KEY_STUB),
7258efe7d1Shelg 	FUSE_LIB_OPT("gid=",			set_gid),
7358efe7d1Shelg 	FUSE_LIB_OPT("gid=%u",			gid),
7458efe7d1Shelg 	FUSE_OPT_KEY("hard_remove",		KEY_STUB),
7558efe7d1Shelg 	FUSE_OPT_KEY("intr_signal",		KEY_STUB),
7658efe7d1Shelg 	FUSE_OPT_KEY("kernel_cache",		KEY_STUB),
7758efe7d1Shelg 	FUSE_OPT_KEY("large_read",		KEY_STUB),
7858efe7d1Shelg 	FUSE_OPT_KEY("modules=",		KEY_STUB),
7958efe7d1Shelg 	FUSE_OPT_KEY("negative_timeout=",	KEY_STUB),
8058efe7d1Shelg 	FUSE_OPT_KEY("readdir_ino",		KEY_STUB),
8158efe7d1Shelg 	FUSE_OPT_KEY("relatime",		KEY_STUB),
8258efe7d1Shelg 	FUSE_OPT_KEY("subtype=",		KEY_STUB),
8358efe7d1Shelg 	FUSE_LIB_OPT("uid=",			set_uid),
8458efe7d1Shelg 	FUSE_LIB_OPT("uid=%u",			uid),
85dae5ffecShelg 	FUSE_LIB_OPT("use_ino",			use_ino),
8658efe7d1Shelg 	FUSE_OPT_KEY("dmask=%o",		KEY_STUB),
8758efe7d1Shelg 	FUSE_OPT_KEY("fmask=%o",		KEY_STUB),
8858efe7d1Shelg 	FUSE_LIB_OPT("umask=",			set_mode),
8958efe7d1Shelg 	FUSE_LIB_OPT("umask=%o",		umask),
9058efe7d1Shelg 	FUSE_OPT_END
9158efe7d1Shelg };
9258efe7d1Shelg 
9358efe7d1Shelg /* options supported by fuse_mount */
9458efe7d1Shelg #define FUSE_MOUNT_OPT(o, m) {o, offsetof(struct fuse_mount_opts, m), 1}
9558efe7d1Shelg static struct fuse_opt fuse_mount_opts[] = {
960f4d2db5Shelg 	FUSE_MOUNT_OPT("allow_other",		allow_other),
970f4d2db5Shelg 	FUSE_OPT_KEY("allow_root",		KEY_STUB),
9858efe7d1Shelg 	FUSE_OPT_KEY("async_read",		KEY_STUB),
9958efe7d1Shelg 	FUSE_OPT_KEY("blkdev",			KEY_STUB),
10058efe7d1Shelg 	FUSE_OPT_KEY("blksize=",		KEY_STUB),
10158efe7d1Shelg 	FUSE_MOUNT_OPT("default_permissions",	def_perms),
10258efe7d1Shelg 	FUSE_OPT_KEY("direct_io",		KEY_STUB),
10358efe7d1Shelg 	FUSE_MOUNT_OPT("fsname=%s",		fsname),
10458efe7d1Shelg 	FUSE_MOUNT_OPT("max_read=%u",		max_read),
10558efe7d1Shelg 	FUSE_OPT_KEY("max_readahead",		KEY_STUB),
10658efe7d1Shelg 	FUSE_OPT_KEY("max_write",		KEY_STUB),
10758efe7d1Shelg 	FUSE_MOUNT_OPT("noatime",		noatime),
10858efe7d1Shelg 	FUSE_MOUNT_OPT("nonempty",		nonempty),
10958efe7d1Shelg 	FUSE_MOUNT_OPT("-r",			rdonly),
11058efe7d1Shelg 	FUSE_MOUNT_OPT("ro",			rdonly),
11158efe7d1Shelg 	FUSE_OPT_KEY("ro_fallback",		KEY_STUB),
11258efe7d1Shelg 	FUSE_OPT_KEY("sync_read",		KEY_STUB),
113f8cc4b14Ssyl 	FUSE_OPT_END
114f8cc4b14Ssyl };
115f8cc4b14Ssyl 
1166d7e81b0Shelg static void
ifuse_try_unmount(struct fuse * f)1176d7e81b0Shelg ifuse_try_unmount(struct fuse *f)
1186d7e81b0Shelg {
1196d7e81b0Shelg 	pid_t child;
1206d7e81b0Shelg 
1216d7e81b0Shelg 	/* unmount in another thread so fuse_loop() doesn't deadlock */
1226d7e81b0Shelg 	child = fork();
1236d7e81b0Shelg 
124*df69c215Sderaadt 	if (child == -1) {
1256d7e81b0Shelg 		DPERROR(__func__);
1266d7e81b0Shelg 		return;
1276d7e81b0Shelg 	}
1286d7e81b0Shelg 
1296d7e81b0Shelg 	if (child == 0) {
1306d7e81b0Shelg 		fuse_remove_signal_handlers(fuse_get_session(f));
1316d7e81b0Shelg 		errno = 0;
1326d7e81b0Shelg 		fuse_unmount(f->fc->dir, f->fc);
1336d7e81b0Shelg 		_exit(errno);
1346d7e81b0Shelg 	}
1356d7e81b0Shelg }
1366d7e81b0Shelg 
1376d7e81b0Shelg static void
ifuse_child_exit(const struct fuse * f)1386d7e81b0Shelg ifuse_child_exit(const struct fuse *f)
1396d7e81b0Shelg {
1406d7e81b0Shelg 	int status;
1416d7e81b0Shelg 
1426d7e81b0Shelg 	if (waitpid(WAIT_ANY, &status, WNOHANG) == -1)
1436d7e81b0Shelg 		fprintf(stderr, "fuse: %s\n", strerror(errno));
1446d7e81b0Shelg 
1456d7e81b0Shelg 	if (WIFEXITED(status) && (WEXITSTATUS(status) != 0))
1466d7e81b0Shelg 		fprintf(stderr, "fuse: %s: %s\n",
1476d7e81b0Shelg 			f->fc->dir, strerror(WEXITSTATUS(status)));
1486d7e81b0Shelg 
1496d7e81b0Shelg 	return;
1506d7e81b0Shelg }
1516d7e81b0Shelg 
15275af46c2Stedu int
fuse_loop(struct fuse * fuse)15375af46c2Stedu fuse_loop(struct fuse *fuse)
15475af46c2Stedu {
15575af46c2Stedu 	struct fusebuf fbuf;
1565f1aa8d9Ssyl 	struct fuse_context ctx;
1578dbfc628Ssyl 	struct fb_ioctl_xch ioexch;
1584b23cf72Stedu 	struct kevent event[5];
15975af46c2Stedu 	struct kevent ev;
1608e11bbb8Sstsp 	ssize_t n;
16175af46c2Stedu 	int ret;
16275af46c2Stedu 
1633a6cc41bSmpi 	if (fuse == NULL)
1643a6cc41bSmpi 		return (-1);
1653a6cc41bSmpi 
16675af46c2Stedu 	fuse->fc->kq = kqueue();
16775af46c2Stedu 	if (fuse->fc->kq == -1)
16875af46c2Stedu 		return (-1);
16975af46c2Stedu 
1704b23cf72Stedu 	EV_SET(&event[0], fuse->fc->fd, EVFILT_READ, EV_ADD |
1714b23cf72Stedu 	    EV_ENABLE, 0, 0, 0);
1724b23cf72Stedu 
1734b23cf72Stedu 	/* signal events */
1744b23cf72Stedu 	EV_SET(&event[1], SIGCHLD, EVFILT_SIGNAL, EV_ADD |
1754b23cf72Stedu 	    EV_ENABLE, 0, 0, 0);
1764b23cf72Stedu 	EV_SET(&event[2], SIGHUP, EVFILT_SIGNAL, EV_ADD |
1774b23cf72Stedu 	    EV_ENABLE, 0, 0, 0);
1784b23cf72Stedu 	EV_SET(&event[3], SIGINT, EVFILT_SIGNAL, EV_ADD |
1794b23cf72Stedu 	    EV_ENABLE, 0, 0, 0);
1804b23cf72Stedu 	EV_SET(&event[4], SIGTERM, EVFILT_SIGNAL, EV_ADD |
18175af46c2Stedu 	    EV_ENABLE, 0, 0, 0);
18275af46c2Stedu 
18375af46c2Stedu 	while (!fuse->fc->dead) {
1844b23cf72Stedu 		ret = kevent(fuse->fc->kq, &event[0], 5, &ev, 1, NULL);
1856d7e81b0Shelg 		if (ret == -1) {
1864b23cf72Stedu 			if (errno != EINTR)
1874b23cf72Stedu 				DPERROR(__func__);
1884b23cf72Stedu 		} else if (ret > 0 && ev.filter == EVFILT_SIGNAL) {
1894b23cf72Stedu 			int signum = ev.ident;
1906d7e81b0Shelg 			switch (signum) {
1916d7e81b0Shelg 			case SIGCHLD:
1926d7e81b0Shelg 				ifuse_child_exit(fuse);
1936d7e81b0Shelg 				break;
1946d7e81b0Shelg 			case SIGHUP:
1956d7e81b0Shelg 			case SIGINT:
1966d7e81b0Shelg 			case SIGTERM:
1976d7e81b0Shelg 				ifuse_try_unmount(fuse);
1986d7e81b0Shelg 				break;
1996d7e81b0Shelg 			default:
2006d7e81b0Shelg 				fprintf(stderr, "%s: %s\n", __func__,
2016d7e81b0Shelg 					strsignal(signum));
2026d7e81b0Shelg 			}
2036d7e81b0Shelg 		} else if (ret > 0) {
2048e11bbb8Sstsp 			n = read(fuse->fc->fd, &fbuf, sizeof(fbuf));
2058e11bbb8Sstsp 			if (n != sizeof(fbuf)) {
2068dbfc628Ssyl 				fprintf(stderr, "%s: bad fusebuf read\n",
2078dbfc628Ssyl 				    __func__);
20875af46c2Stedu 				return (-1);
20975af46c2Stedu 			}
21075af46c2Stedu 
2118dbfc628Ssyl 			/* check if there is data something present */
2128dbfc628Ssyl 			if (fbuf.fb_len) {
2138dbfc628Ssyl 				fbuf.fb_dat = malloc(fbuf.fb_len);
2148dbfc628Ssyl 				if (fbuf.fb_dat == NULL)
2158dbfc628Ssyl 					return (-1);
2168dbfc628Ssyl 				ioexch.fbxch_uuid = fbuf.fb_uuid;
2178dbfc628Ssyl 				ioexch.fbxch_len = fbuf.fb_len;
2188dbfc628Ssyl 				ioexch.fbxch_data = fbuf.fb_dat;
21975af46c2Stedu 
2208dbfc628Ssyl 				if (ioctl(fuse->fc->fd, FIOCGETFBDAT,
221*df69c215Sderaadt 				    &ioexch) == -1) {
2228dbfc628Ssyl 					free(fbuf.fb_dat);
22375af46c2Stedu 					return (-1);
22475af46c2Stedu 				}
22575af46c2Stedu 			}
22675af46c2Stedu 
2275f1aa8d9Ssyl 			ctx.fuse = fuse;
228413ec31cShelg 			ctx.uid = fbuf.fb_uid;
229413ec31cShelg 			ctx.gid = fbuf.fb_gid;
230413ec31cShelg 			ctx.pid = fbuf.fb_tid;
231413ec31cShelg 			ctx.umask = fbuf.fb_umask;
232593b95d9Ssyl 			ctx.private_data = fuse->private_data;
2335f1aa8d9Ssyl 			ictx = &ctx;
2345f1aa8d9Ssyl 
23575af46c2Stedu 			ret = ifuse_exec_opcode(fuse, &fbuf);
23675af46c2Stedu 			if (ret) {
2375f1aa8d9Ssyl 				ictx = NULL;
23877cffba6Ssyl 				return (-1);
23975af46c2Stedu 			}
24075af46c2Stedu 
2418e11bbb8Sstsp 			n = write(fuse->fc->fd, &fbuf, sizeof(fbuf));
2428dbfc628Ssyl 			if (fbuf.fb_len) {
2438dbfc628Ssyl 				if (fbuf.fb_dat == NULL) {
2448dbfc628Ssyl 					fprintf(stderr, "%s: fb_dat is Null\n",
2458dbfc628Ssyl 					    __func__);
2468dbfc628Ssyl 					return (-1);
2478dbfc628Ssyl 				}
2488dbfc628Ssyl 				ioexch.fbxch_uuid = fbuf.fb_uuid;
2498dbfc628Ssyl 				ioexch.fbxch_len = fbuf.fb_len;
2508dbfc628Ssyl 				ioexch.fbxch_data = fbuf.fb_dat;
2518dbfc628Ssyl 
252*df69c215Sderaadt 				if (ioctl(fuse->fc->fd, FIOCSETFBDAT, &ioexch) == -1) {
2538dbfc628Ssyl 					free(fbuf.fb_dat);
2548dbfc628Ssyl 					return (-1);
2558dbfc628Ssyl 				}
2568dbfc628Ssyl 				free(fbuf.fb_dat);
2578dbfc628Ssyl 			}
2585f1aa8d9Ssyl 			ictx = NULL;
25975af46c2Stedu 
2608e11bbb8Sstsp 			if (n != FUSEBUFSIZE) {
26175af46c2Stedu 				errno = EINVAL;
26275af46c2Stedu 				return (-1);
26375af46c2Stedu 			}
26475af46c2Stedu 		}
26575af46c2Stedu 	}
26675af46c2Stedu 
26775af46c2Stedu 	return (0);
26875af46c2Stedu }
2697c0bee30Sjca DEF(fuse_loop);
27075af46c2Stedu 
27175af46c2Stedu struct fuse_chan *
fuse_mount(const char * dir,struct fuse_args * args)27258efe7d1Shelg fuse_mount(const char *dir, struct fuse_args *args)
27375af46c2Stedu {
27475af46c2Stedu 	struct fusefs_args fargs;
27558efe7d1Shelg 	struct fuse_mount_opts opts;
27675af46c2Stedu 	struct fuse_chan *fc;
277f76a189dSsyl 	const char *errcause;
27858efe7d1Shelg 	int mnt_flags;
27975af46c2Stedu 
2803a6cc41bSmpi 	if (dir == NULL)
2813a6cc41bSmpi 		return (NULL);
2823a6cc41bSmpi 
28375af46c2Stedu 	fc = calloc(1, sizeof(*fc));
28475af46c2Stedu 	if (fc == NULL)
28575af46c2Stedu 		return (NULL);
28675af46c2Stedu 
2875a512893Stedu 	fc->dir = realpath(dir, NULL);
28875af46c2Stedu 	if (fc->dir == NULL)
28975af46c2Stedu 		goto bad;
29075af46c2Stedu 
291652a85cdSjca 	if ((fc->fd = open("/dev/fuse0", O_RDWR)) == -1) {
292f76a189dSsyl 		perror(__func__);
293f76a189dSsyl 		goto bad;
29475af46c2Stedu 	}
29575af46c2Stedu 
296bb72b40bShelg 	memset(&opts, 0, sizeof(opts));
29758efe7d1Shelg 	if (fuse_opt_parse(args, &opts, fuse_mount_opts, NULL) == -1)
29858efe7d1Shelg 		goto bad;
29958efe7d1Shelg 
30058efe7d1Shelg 	mnt_flags = 0;
30158efe7d1Shelg 	if (opts.rdonly)
30258efe7d1Shelg 		mnt_flags |= MNT_RDONLY;
30358efe7d1Shelg 	if (opts.noatime)
30458efe7d1Shelg 		mnt_flags |= MNT_NOATIME;
30558efe7d1Shelg 
30658efe7d1Shelg 	if (opts.max_read > FUSEBUFMAXSIZE) {
30758efe7d1Shelg 		fprintf(stderr, "fuse: invalid max_read (%d > %d)\n",
30858efe7d1Shelg 		    opts.max_read, FUSEBUFMAXSIZE);
30958efe7d1Shelg 		goto bad;
31058efe7d1Shelg 	}
31158efe7d1Shelg 
312bb72b40bShelg 	memset(&fargs, 0, sizeof(fargs));
313f76a189dSsyl 	fargs.fd = fc->fd;
31458efe7d1Shelg 	fargs.max_read = opts.max_read;
3150f4d2db5Shelg 	fargs.allow_other = opts.allow_other;
31658efe7d1Shelg 
31758efe7d1Shelg 	if (mount(MOUNT_FUSEFS, fc->dir, mnt_flags, &fargs)) {
318f76a189dSsyl 		switch (errno) {
319f76a189dSsyl 		case EMFILE:
320f76a189dSsyl 			errcause = "mount table full";
321f76a189dSsyl 			break;
322f76a189dSsyl 		case EOPNOTSUPP:
323f76a189dSsyl 			errcause = "filesystem not supported by kernel";
324f76a189dSsyl 			break;
325f76a189dSsyl 		default:
326f76a189dSsyl 			errcause = strerror(errno);
32775af46c2Stedu 			break;
32875af46c2Stedu 		}
329f76a189dSsyl 		fprintf(stderr, "%s on %s: %s\n", __func__, dir, errcause);
33075af46c2Stedu 		goto bad;
33175af46c2Stedu 	}
33275af46c2Stedu 
33375af46c2Stedu 	return (fc);
33475af46c2Stedu bad:
335652a85cdSjca 	if (fc->fd != -1)
33675af46c2Stedu 		close(fc->fd);
33775af46c2Stedu 	free(fc->dir);
338652a85cdSjca 	free(fc);
33975af46c2Stedu 	return (NULL);
34075af46c2Stedu }
3417c0bee30Sjca DEF(fuse_mount);
34275af46c2Stedu 
34375af46c2Stedu void
fuse_unmount(const char * dir,struct fuse_chan * ch)3443a6cc41bSmpi fuse_unmount(const char *dir, struct fuse_chan *ch)
34575af46c2Stedu {
3463a6cc41bSmpi 	if (ch == NULL || ch->dead)
34775af46c2Stedu 		return;
34875af46c2Stedu 
3495a2df364Sstsp 	if (unmount(dir, MNT_UPDATE) == -1)
35075af46c2Stedu 		DPERROR(__func__);
35175af46c2Stedu }
3527c0bee30Sjca DEF(fuse_unmount);
35375af46c2Stedu 
35475af46c2Stedu int
fuse_is_lib_option(const char * opt)3553a6cc41bSmpi fuse_is_lib_option(const char *opt)
35675af46c2Stedu {
35758efe7d1Shelg 	return (fuse_opt_match(fuse_lib_opts, opt));
35875af46c2Stedu }
35975af46c2Stedu 
36075af46c2Stedu int
fuse_chan_fd(struct fuse_chan * ch)36175af46c2Stedu fuse_chan_fd(struct fuse_chan *ch)
36275af46c2Stedu {
3633a6cc41bSmpi 	if (ch == NULL)
3643a6cc41bSmpi 		return (-1);
3653a6cc41bSmpi 
36675af46c2Stedu 	return (ch->fd);
36775af46c2Stedu }
36875af46c2Stedu 
36975af46c2Stedu struct fuse_session *
fuse_get_session(struct fuse * f)37075af46c2Stedu fuse_get_session(struct fuse *f)
37175af46c2Stedu {
37275af46c2Stedu 	return (&f->se);
37375af46c2Stedu }
3747c0bee30Sjca DEF(fuse_get_session);
37575af46c2Stedu 
37675af46c2Stedu int
fuse_loop_mt(unused struct fuse * fuse)37775af46c2Stedu fuse_loop_mt(unused struct fuse *fuse)
37875af46c2Stedu {
37934272175Smpi 	return (-1);
38075af46c2Stedu }
38175af46c2Stedu 
38258efe7d1Shelg static int
ifuse_lib_opt_proc(void * data,const char * arg,int key,unused struct fuse_args * args)38358efe7d1Shelg ifuse_lib_opt_proc(void *data, const char *arg, int key,
38458efe7d1Shelg     unused struct fuse_args *args)
38558efe7d1Shelg {
38658efe7d1Shelg 	switch (key) {
38758efe7d1Shelg 	case KEY_STUB:
38858efe7d1Shelg 		return (0);
38958efe7d1Shelg 	case KEY_DEBUG:
39058efe7d1Shelg 		ifuse_debug_init();
39158efe7d1Shelg 		break;
39258efe7d1Shelg 	default:
39358efe7d1Shelg 		fprintf(stderr, "fuse: unrecognised option %s\n", arg);
39458efe7d1Shelg 		return (-1);
39558efe7d1Shelg 	}
39658efe7d1Shelg 
39758efe7d1Shelg 	/* Keep unknown options. */
39858efe7d1Shelg 	return (1);
39958efe7d1Shelg }
40058efe7d1Shelg 
40175af46c2Stedu struct fuse *
fuse_new(struct fuse_chan * fc,struct fuse_args * args,const struct fuse_operations * ops,unused size_t size,void * userdata)40258efe7d1Shelg fuse_new(struct fuse_chan *fc, struct fuse_args *args,
40375af46c2Stedu     const struct fuse_operations *ops, unused size_t size,
4043a6cc41bSmpi     void *userdata)
40575af46c2Stedu {
40675af46c2Stedu 	struct fuse *fuse;
40775af46c2Stedu 	struct fuse_vnode *root;
40875af46c2Stedu 
4093a6cc41bSmpi 	if (fc == NULL || ops == NULL)
4103a6cc41bSmpi 		return (NULL);
4113a6cc41bSmpi 
41275af46c2Stedu 	if ((fuse = calloc(1, sizeof(*fuse))) == NULL)
41375af46c2Stedu 		return (NULL);
41475af46c2Stedu 
41575af46c2Stedu 	/* copy fuse ops to their own structure */
41675af46c2Stedu 	memcpy(&fuse->op, ops, sizeof(fuse->op));
41775af46c2Stedu 
41858efe7d1Shelg 	if (fuse_opt_parse(args, &fuse->conf, fuse_lib_opts,
41958efe7d1Shelg 	    ifuse_lib_opt_proc) == -1) {
42058efe7d1Shelg 		free(fuse);
42158efe7d1Shelg 		return (NULL);
42258efe7d1Shelg 	}
42358efe7d1Shelg 
42475af46c2Stedu 	fuse->fc = fc;
42575af46c2Stedu 	fuse->max_ino = FUSE_ROOT_INO;
42675af46c2Stedu 	fuse->se.args = fuse;
427593b95d9Ssyl 	fuse->private_data = userdata;
42875af46c2Stedu 
42975af46c2Stedu 	if ((root = alloc_vn(fuse, "/", FUSE_ROOT_INO, 0)) == NULL) {
43075af46c2Stedu 		free(fuse);
43175af46c2Stedu 		return (NULL);
43275af46c2Stedu 	}
43375af46c2Stedu 
43475af46c2Stedu 	tree_init(&fuse->vnode_tree);
43575af46c2Stedu 	tree_init(&fuse->name_tree);
43675af46c2Stedu 	if (!set_vn(fuse, root)) {
43775af46c2Stedu 		free(fuse);
43875af46c2Stedu 		return (NULL);
43975af46c2Stedu 	}
44075af46c2Stedu 
44175af46c2Stedu 	return (fuse);
44275af46c2Stedu }
4437c0bee30Sjca DEF(fuse_new);
44475af46c2Stedu 
44575af46c2Stedu int
fuse_daemonize(int foreground)446c286f01cShelg fuse_daemonize(int foreground)
44775af46c2Stedu {
448c286f01cShelg 	if (foreground)
449c286f01cShelg 		return (0);
450c286f01cShelg 
45175af46c2Stedu 	return (daemon(0, 0));
45275af46c2Stedu }
4537c0bee30Sjca DEF(fuse_daemonize);
45475af46c2Stedu 
45575af46c2Stedu void
fuse_destroy(struct fuse * f)4563a6cc41bSmpi fuse_destroy(struct fuse *f)
45775af46c2Stedu {
4583a6cc41bSmpi 	if (f == NULL)
4593a6cc41bSmpi 		return;
4603a6cc41bSmpi 
461a3f88267Shelg 	/*
462a3f88267Shelg   	 * Even though these were allocated in fuse_mount(), we can't free them
463a3f88267Shelg  	 * in fuse_unmount() since fuse_loop() will not have terminated yet so
464a3f88267Shelg  	 * we free them here.
465a3f88267Shelg  	 */
46675af46c2Stedu 	close(f->fc->fd);
46775af46c2Stedu 	free(f->fc->dir);
46875af46c2Stedu 	free(f->fc);
46975af46c2Stedu 	free(f);
47075af46c2Stedu }
4717c0bee30Sjca DEF(fuse_destroy);
47275af46c2Stedu 
47375af46c2Stedu void
fuse_remove_signal_handlers(unused struct fuse_session * se)47475af46c2Stedu fuse_remove_signal_handlers(unused struct fuse_session *se)
47575af46c2Stedu {
4768502b795Shelg 	struct sigaction old_sa;
4778502b795Shelg 
4788502b795Shelg 	if (sigaction(SIGHUP, NULL, &old_sa) == 0)
4794b23cf72Stedu 		if (old_sa.sa_handler == SIG_IGN)
48075af46c2Stedu 			signal(SIGHUP, SIG_DFL);
4818502b795Shelg 
4828502b795Shelg 	if (sigaction(SIGINT, NULL, &old_sa) == 0)
4834b23cf72Stedu 		if (old_sa.sa_handler == SIG_IGN)
48475af46c2Stedu 			signal(SIGINT, SIG_DFL);
4858502b795Shelg 
4868502b795Shelg 	if (sigaction(SIGTERM, NULL, &old_sa) == 0)
4874b23cf72Stedu 		if (old_sa.sa_handler == SIG_IGN)
48875af46c2Stedu 			signal(SIGTERM, SIG_DFL);
4898502b795Shelg 
4908502b795Shelg 	if (sigaction(SIGPIPE, NULL, &old_sa) == 0)
4918502b795Shelg 		if (old_sa.sa_handler == SIG_IGN)
49275af46c2Stedu 			signal(SIGPIPE, SIG_DFL);
4934b23cf72Stedu 
4944b23cf72Stedu 	if (sigaction(SIGCHLD, NULL, &old_sa) == 0)
4954b23cf72Stedu 		if (old_sa.sa_handler == SIG_IGN)
4964b23cf72Stedu 			signal(SIGCHLD, SIG_DFL);
49775af46c2Stedu }
4987c0bee30Sjca DEF(fuse_remove_signal_handlers);
49975af46c2Stedu 
50075af46c2Stedu int
fuse_set_signal_handlers(unused struct fuse_session * se)5016d7e81b0Shelg fuse_set_signal_handlers(unused struct fuse_session *se)
50275af46c2Stedu {
5038502b795Shelg 	struct sigaction old_sa;
5048502b795Shelg 
5058502b795Shelg 	if (sigaction(SIGHUP, NULL, &old_sa) == -1)
5068502b795Shelg 		return (-1);
5078502b795Shelg 	if (old_sa.sa_handler == SIG_DFL)
5084b23cf72Stedu 		signal(SIGHUP, SIG_IGN);
5098502b795Shelg 
5108502b795Shelg 	if (sigaction(SIGINT, NULL, &old_sa) == -1)
5118502b795Shelg 		return (-1);
5128502b795Shelg 	if (old_sa.sa_handler == SIG_DFL)
5134b23cf72Stedu 		signal(SIGINT, SIG_IGN);
5148502b795Shelg 
5158502b795Shelg 	if (sigaction(SIGTERM, NULL, &old_sa) == -1)
5168502b795Shelg 		return (-1);
5178502b795Shelg 	if (old_sa.sa_handler == SIG_DFL)
5184b23cf72Stedu 		signal(SIGTERM, SIG_IGN);
5198502b795Shelg 
5208502b795Shelg 	if (sigaction(SIGPIPE, NULL, &old_sa) == -1)
5218502b795Shelg 		return (-1);
5228502b795Shelg 	if (old_sa.sa_handler == SIG_DFL)
52375af46c2Stedu 		signal(SIGPIPE, SIG_IGN);
5248502b795Shelg 
5254b23cf72Stedu 	if (sigaction(SIGCHLD, NULL, &old_sa) == -1)
5264b23cf72Stedu 		return (-1);
5274b23cf72Stedu 	if (old_sa.sa_handler == SIG_DFL)
5284b23cf72Stedu 		signal(SIGCHLD, SIG_IGN);
5294b23cf72Stedu 
53075af46c2Stedu 	return (0);
53175af46c2Stedu }
53275af46c2Stedu 
533f8cc4b14Ssyl static void
dump_help(void)534f8cc4b14Ssyl dump_help(void)
535f8cc4b14Ssyl {
536f8cc4b14Ssyl 	fprintf(stderr, "FUSE options:\n"
537f8cc4b14Ssyl 	    "    -d   -o debug          enable debug output (implies -f)\n"
538c286f01cShelg 	    "    -f                     run in foreground\n"
53958efe7d1Shelg 	    "    -V   --version         print fuse version\n"
540f8cc4b14Ssyl 	    "\n");
541f8cc4b14Ssyl }
542f8cc4b14Ssyl 
543f8cc4b14Ssyl static void
dump_version(void)544f8cc4b14Ssyl dump_version(void)
545f8cc4b14Ssyl {
546b4fdc7adShelg 	fprintf(stderr, "FUSE library version: %d.%d\n", FUSE_MAJOR_VERSION,
547b4fdc7adShelg 	    FUSE_MINOR_VERSION);
548f8cc4b14Ssyl }
549f8cc4b14Ssyl 
550f8cc4b14Ssyl static int
ifuse_process_opt(void * data,const char * arg,int key,unused struct fuse_args * args)551628ba615Ssyl ifuse_process_opt(void *data, const char *arg, int key,
552628ba615Ssyl     unused struct fuse_args *args)
553f8cc4b14Ssyl {
55458efe7d1Shelg 	struct fuse_core_opts *opt = data;
555f8cc4b14Ssyl 	struct stat st;
556f8cc4b14Ssyl 	int res;
557f8cc4b14Ssyl 
558f8cc4b14Ssyl 	switch (key) {
5591b8c4fd6Ssyl 	case KEY_STUB:
560f8cc4b14Ssyl 		return (0);
561c839d22fShelg 	case KEY_DEBUG:
562c839d22fShelg 		ifuse_debug_init();
563c839d22fShelg 		/* falls through */
564c286f01cShelg 	case KEY_FOREGROUND:
565c286f01cShelg 		opt->foreground = 1;
566c286f01cShelg 		return (0);
567f8cc4b14Ssyl 	case KEY_HELP:
568f8cc4b14Ssyl 	case KEY_HELP_WITHOUT_HEADER:
569f8cc4b14Ssyl 		dump_help();
570627fe389Sajacoutot 		return (-1);
571f8cc4b14Ssyl 	case KEY_VERSION:
572f8cc4b14Ssyl 		dump_version();
573627fe389Sajacoutot 		return (-1);
574f8cc4b14Ssyl 	case FUSE_OPT_KEY_NONOPT:
575f8cc4b14Ssyl 		if (opt->mp == NULL) {
576f8cc4b14Ssyl 			opt->mp = realpath(arg, opt->mp);
577487e5ad7Sstsp 			if (opt->mp == NULL) {
578487e5ad7Sstsp 				fprintf(stderr, "fuse: realpath: "
579487e5ad7Sstsp 				    "%s : %s\n", arg, strerror(errno));
580487e5ad7Sstsp 				return (-1);
581487e5ad7Sstsp 			}
582f8cc4b14Ssyl 
583487e5ad7Sstsp 			res = stat(opt->mp, &st);
584487e5ad7Sstsp 			if (res == -1) {
585f8cc4b14Ssyl 				fprintf(stderr, "fuse: bad mount point "
586f8cc4b14Ssyl 				    "%s : %s\n", arg, strerror(errno));
587f8cc4b14Ssyl 				return (-1);
588f8cc4b14Ssyl 			}
589f8cc4b14Ssyl 
590f8cc4b14Ssyl 			if (!S_ISDIR(st.st_mode)) {
591f8cc4b14Ssyl 				fprintf(stderr, "fuse: bad mount point "
59258efe7d1Shelg 				    "%s : %s\n", arg, strerror(ENOTDIR));
593f8cc4b14Ssyl 				return (-1);
594f8cc4b14Ssyl 			}
595f8cc4b14Ssyl 		}
596f8cc4b14Ssyl 		return (0);
597f8cc4b14Ssyl 	}
598f8cc4b14Ssyl 
59958efe7d1Shelg 	/* Pass through unknown options. */
60058efe7d1Shelg 	return (1);
60158efe7d1Shelg }
60258efe7d1Shelg 
60375af46c2Stedu int
fuse_parse_cmdline(struct fuse_args * args,char ** mp,int * mt,int * fg)604c286f01cShelg fuse_parse_cmdline(struct fuse_args *args, char **mp, int *mt, int *fg)
60575af46c2Stedu {
60658efe7d1Shelg 	struct fuse_core_opts opt;
60775af46c2Stedu 
608bb72b40bShelg 	memset(&opt, 0, sizeof(opt));
609f8cc4b14Ssyl 	if (fuse_opt_parse(args, &opt, fuse_core_opts, ifuse_process_opt) == -1)
610f8cc4b14Ssyl 		return (-1);
61175af46c2Stedu 
61267b20abaSsyl 	if (opt.mp == NULL) {
61367b20abaSsyl 		fprintf(stderr, "fuse: missing mountpoint parameter\n");
614f8cc4b14Ssyl 		return (-1);
61567b20abaSsyl 	}
616f8cc4b14Ssyl 
617c63127e5Smpi 	if (mp != NULL) {
618f8cc4b14Ssyl 		*mp = strdup(opt.mp);
619cec0f43fSokan 		if (*mp == NULL)
620cec0f43fSokan 			return (-1);
621c63127e5Smpi 	}
622c63127e5Smpi 
623c63127e5Smpi 	if (mt != NULL)
62475af46c2Stedu 		*mt = 0;
62575af46c2Stedu 
626c286f01cShelg 	if (fg != NULL)
627c286f01cShelg 		*fg = opt.foreground;
628c286f01cShelg 
62975af46c2Stedu 	return (0);
63075af46c2Stedu }
6317c0bee30Sjca DEF(fuse_parse_cmdline);
63275af46c2Stedu 
6335f1aa8d9Ssyl struct fuse_context *
fuse_get_context(void)6345f1aa8d9Ssyl fuse_get_context(void)
6355f1aa8d9Ssyl {
6365f1aa8d9Ssyl 	return (ictx);
6375f1aa8d9Ssyl }
6387c0bee30Sjca DEF(fuse_get_context);
6395f1aa8d9Ssyl 
64075af46c2Stedu int
fuse_version(void)6419874ba4aSsyl fuse_version(void)
6429874ba4aSsyl {
6439874ba4aSsyl 	return (FUSE_VERSION);
6449874ba4aSsyl }
6459874ba4aSsyl 
6466e1e865eSsyl void
fuse_teardown(struct fuse * fuse,char * mp)6476e1e865eSsyl fuse_teardown(struct fuse *fuse, char *mp)
6486e1e865eSsyl {
6493a6cc41bSmpi 	if (fuse == NULL || mp == NULL)
6503a6cc41bSmpi 		return;
6513a6cc41bSmpi 
6524f1d7555Shelg 	fuse_remove_signal_handlers(fuse_get_session(fuse));
6536e1e865eSsyl 	fuse_unmount(mp, fuse->fc);
6546e1e865eSsyl 	fuse_destroy(fuse);
6556e1e865eSsyl }
6566e1e865eSsyl 
6579874ba4aSsyl int
fuse_invalidate(unused struct fuse * f,unused const char * path)658628ba615Ssyl fuse_invalidate(unused struct fuse *f, unused const char *path)
659e4ecea32Ssyl {
660e4ecea32Ssyl 	return (EINVAL);
661e4ecea32Ssyl }
662e4ecea32Ssyl 
663e4ecea32Ssyl struct fuse *
fuse_setup(int argc,char ** argv,const struct fuse_operations * ops,size_t size,char ** mp,int * mt,void * data)664e4ecea32Ssyl fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
665e4ecea32Ssyl     size_t size, char **mp, int *mt, void *data)
66675af46c2Stedu {
667f8cc4b14Ssyl 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
66875af46c2Stedu 	struct fuse_chan *fc;
669f8cc4b14Ssyl 	struct fuse *fuse;
67057f837f2Smpi 	char *dir;
671e4ecea32Ssyl 	int fg;
67275af46c2Stedu 
67357f837f2Smpi 	dir = NULL;
67457f837f2Smpi 	if (fuse_parse_cmdline(&args, &dir, mt, &fg))
675f8cc4b14Ssyl 		goto err;
67675af46c2Stedu 
677c286f01cShelg 	fuse_daemonize(fg);
67875af46c2Stedu 
67958efe7d1Shelg 	if ((fc = fuse_mount(dir, &args)) == NULL)
680f8cc4b14Ssyl 		goto err;
68175af46c2Stedu 
68258efe7d1Shelg 	if ((fuse = fuse_new(fc, &args, ops, size, data)) == NULL) {
683a3f88267Shelg 		fuse_unmount(dir, fc);
684a3f88267Shelg 		close(fc->fd);
685a3f88267Shelg 		free(fc->dir);
68675af46c2Stedu 		free(fc);
687f8cc4b14Ssyl 		goto err;
68875af46c2Stedu 	}
68975af46c2Stedu 
69058efe7d1Shelg 	/* args are no longer needed */
69158efe7d1Shelg 	fuse_opt_free_args(&args);
69258efe7d1Shelg 
6930f49c61eShelg 	if (fuse_set_signal_handlers(fuse_get_session(fuse)) == -1) {
6940f49c61eShelg 		fuse_unmount(dir, fc);
6950f49c61eShelg 		fuse_destroy(fuse);
6960f49c61eShelg 		goto err;
6970f49c61eShelg 	}
6980f49c61eShelg 
69958efe7d1Shelg 	/* the caller frees dir, but we do it if the caller doesn't want it */
70058efe7d1Shelg 	if (mp == NULL)
70158efe7d1Shelg 		free(dir);
70258efe7d1Shelg 	else
70357f837f2Smpi 		*mp = dir;
70457f837f2Smpi 
705e4ecea32Ssyl 	return (fuse);
706f8cc4b14Ssyl err:
70757f837f2Smpi 	free(dir);
708e4ecea32Ssyl 	return (NULL);
709e4ecea32Ssyl }
7107c0bee30Sjca DEF(fuse_setup);
711e4ecea32Ssyl 
712e4ecea32Ssyl int
fuse_main(int argc,char ** argv,const struct fuse_operations * ops,void * data)713e4ecea32Ssyl fuse_main(int argc, char **argv, const struct fuse_operations *ops, void *data)
714e4ecea32Ssyl {
715e4ecea32Ssyl 	struct fuse *fuse;
716e4ecea32Ssyl 
7173a6cc41bSmpi 	fuse = fuse_setup(argc, argv, ops, sizeof(*ops), NULL, NULL, data);
71858efe7d1Shelg 	if (fuse == NULL)
719e4ecea32Ssyl 		return (-1);
720e4ecea32Ssyl 
721e4ecea32Ssyl 	return (fuse_loop(fuse));
72275af46c2Stedu }
723