1152179Sgrehan/* $OpenBSD: fuse.c,v 1.51 2019/06/28 13:32:42 deraadt Exp $ */
2152179Sgrehan/*
3152179Sgrehan * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
4152179Sgrehan *
5152179Sgrehan * Permission to use, copy, modify, and distribute this software for any
6152179Sgrehan * purpose with or without fee is hereby granted, provided that the above
7152179Sgrehan * copyright notice and this permission notice appear in all copies.
8152179Sgrehan *
9152179Sgrehan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10152179Sgrehan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11152179Sgrehan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12152179Sgrehan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13152179Sgrehan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14152179Sgrehan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15152179Sgrehan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16152179Sgrehan */
17152179Sgrehan
18152179Sgrehan#include <sys/wait.h>
19152179Sgrehan#include <sys/types.h>
20152179Sgrehan#include <sys/ioctl.h>
21152179Sgrehan
22152179Sgrehan#include <miscfs/fuse/fusefs.h>
23152179Sgrehan
24152179Sgrehan#include <errno.h>
25152179Sgrehan#include <signal.h>
26152179Sgrehan#include <stddef.h>
27152179Sgrehan#include <stdlib.h>
28152179Sgrehan#include <string.h>
29152179Sgrehan#include <unistd.h>
30152179Sgrehan
31152179Sgrehan#include "fuse_opt.h"
32152179Sgrehan#include "fuse_private.h"
33152179Sgrehan#include "debug.h"
34152179Sgrehan
35152179Sgrehanstatic struct fuse_context *ictx = NULL;
36152179Sgrehan
37152179Sgrehanenum {
38152179Sgrehan	KEY_DEBUG,
39152179Sgrehan	KEY_FOREGROUND,
40152179Sgrehan	KEY_HELP,
41152179Sgrehan	KEY_HELP_WITHOUT_HEADER,
42152179Sgrehan	KEY_VERSION,
43152179Sgrehan	KEY_MAXREAD,
44152179Sgrehan	KEY_STUB
45152179Sgrehan};
46152179Sgrehan
47152179Sgrehan/* options supported by fuse_parse_cmdline */
48152179Sgrehanstatic struct fuse_opt fuse_core_opts[] = {
49152179Sgrehan	FUSE_OPT_KEY("-d",			KEY_DEBUG),
50152179Sgrehan	FUSE_OPT_KEY("debug",			KEY_DEBUG),
51152179Sgrehan	FUSE_OPT_KEY("-f",			KEY_FOREGROUND),
52152179Sgrehan	FUSE_OPT_KEY("-h",			KEY_HELP),
53152179Sgrehan	FUSE_OPT_KEY("--help",			KEY_HELP),
54152179Sgrehan	FUSE_OPT_KEY("-ho",			KEY_HELP_WITHOUT_HEADER),
55152179Sgrehan	FUSE_OPT_KEY("-s",			KEY_STUB),
56152179Sgrehan	FUSE_OPT_KEY("-V",			KEY_VERSION),
57152179Sgrehan	FUSE_OPT_KEY("--version",		KEY_VERSION),
58152179Sgrehan	FUSE_OPT_END
59152179Sgrehan};
60152179Sgrehan
61152179Sgrehan/* options supported by fuse_new */
62152179Sgrehan#define FUSE_LIB_OPT(o, m) {o, offsetof(struct fuse_config, m), 1}
63152179Sgrehanstatic struct fuse_opt fuse_lib_opts[] = {
64152179Sgrehan	FUSE_OPT_KEY("ac_attr_timeout=",	KEY_STUB),
65152179Sgrehan	FUSE_OPT_KEY("attr_timeout=",		KEY_STUB),
66152179Sgrehan	FUSE_OPT_KEY("auto_cache",		KEY_STUB),
67152179Sgrehan	FUSE_OPT_KEY("noauto_cache",		KEY_STUB),
68152179Sgrehan	FUSE_OPT_KEY("big_writes",		KEY_STUB),
69152179Sgrehan	FUSE_OPT_KEY("debug",			KEY_DEBUG),
70152179Sgrehan	FUSE_OPT_KEY("-d",			KEY_DEBUG),
71152179Sgrehan	FUSE_OPT_KEY("entry_timeout=",		KEY_STUB),
72152179Sgrehan	FUSE_LIB_OPT("gid=",			set_gid),
73152179Sgrehan	FUSE_LIB_OPT("gid=%u",			gid),
74152179Sgrehan	FUSE_OPT_KEY("hard_remove",		KEY_STUB),
75152179Sgrehan	FUSE_OPT_KEY("intr_signal",		KEY_STUB),
76152179Sgrehan	FUSE_OPT_KEY("kernel_cache",		KEY_STUB),
77152179Sgrehan	FUSE_OPT_KEY("large_read",		KEY_STUB),
78152179Sgrehan	FUSE_OPT_KEY("modules=",		KEY_STUB),
79152179Sgrehan	FUSE_OPT_KEY("negative_timeout=",	KEY_STUB),
80152179Sgrehan	FUSE_OPT_KEY("readdir_ino",		KEY_STUB),
81152179Sgrehan	FUSE_OPT_KEY("relatime",		KEY_STUB),
82152179Sgrehan	FUSE_OPT_KEY("subtype=",		KEY_STUB),
83152179Sgrehan	FUSE_LIB_OPT("uid=",			set_uid),
84152179Sgrehan	FUSE_LIB_OPT("uid=%u",			uid),
85152179Sgrehan	FUSE_LIB_OPT("use_ino",			use_ino),
86152179Sgrehan	FUSE_OPT_KEY("dmask=%o",		KEY_STUB),
87152179Sgrehan	FUSE_OPT_KEY("fmask=%o",		KEY_STUB),
88157443Speter	FUSE_LIB_OPT("umask=",			set_mode),
89152179Sgrehan	FUSE_LIB_OPT("umask=%o",		umask),
90152179Sgrehan	FUSE_OPT_END
91152179Sgrehan};
92152179Sgrehan
93208504Salc/* options supported by fuse_mount */
94208504Salc#define FUSE_MOUNT_OPT(o, m) {o, offsetof(struct fuse_mount_opts, m), 1}
95152179Sgrehanstatic struct fuse_opt fuse_mount_opts[] = {
96152179Sgrehan	FUSE_MOUNT_OPT("allow_other",		allow_other),
97152179Sgrehan	FUSE_OPT_KEY("allow_root",		KEY_STUB),
98152179Sgrehan	FUSE_OPT_KEY("async_read",		KEY_STUB),
99152179Sgrehan	FUSE_OPT_KEY("blkdev",			KEY_STUB),
100152179Sgrehan	FUSE_OPT_KEY("blksize=",		KEY_STUB),
101152179Sgrehan	FUSE_MOUNT_OPT("default_permissions",	def_perms),
102152179Sgrehan	FUSE_OPT_KEY("direct_io",		KEY_STUB),
103152179Sgrehan	FUSE_MOUNT_OPT("fsname=%s",		fsname),
104179081Salc	FUSE_MOUNT_OPT("max_read=%u",		max_read),
105179081Salc	FUSE_OPT_KEY("max_readahead",		KEY_STUB),
106152179Sgrehan	FUSE_OPT_KEY("max_write",		KEY_STUB),
107179081Salc	FUSE_MOUNT_OPT("noatime",		noatime),
108152179Sgrehan	FUSE_MOUNT_OPT("nonempty",		nonempty),
109190684Smarcel	FUSE_MOUNT_OPT("-r",			rdonly),
110190684Smarcel	FUSE_MOUNT_OPT("ro",			rdonly),
111190684Smarcel	FUSE_OPT_KEY("ro_fallback",		KEY_STUB),
112190684Smarcel	FUSE_OPT_KEY("sync_read",		KEY_STUB),
113190684Smarcel	FUSE_OPT_END
114213307Snwhitehorn};
115213307Snwhitehorn
116213307Snwhitehornstatic void
117213307Snwhitehornifuse_try_unmount(struct fuse *f)
118213307Snwhitehorn{
119213307Snwhitehorn	pid_t child;
120213307Snwhitehorn
121213307Snwhitehorn	/* unmount in another thread so fuse_loop() doesn't deadlock */
122213307Snwhitehorn	child = fork();
123213307Snwhitehorn
124213307Snwhitehorn	if (child == -1) {
125213307Snwhitehorn		DPERROR(__func__);
126213307Snwhitehorn		return;
127213307Snwhitehorn	}
128213307Snwhitehorn
129213307Snwhitehorn	if (child == 0) {
130213307Snwhitehorn		fuse_remove_signal_handlers(fuse_get_session(f));
131213307Snwhitehorn		errno = 0;
132152179Sgrehan		fuse_unmount(f->fc->dir, f->fc);
133152179Sgrehan		_exit(errno);
134152179Sgrehan	}
135152179Sgrehan}
136255028Salc
137255028Salcstatic void
138255028Salcifuse_child_exit(const struct fuse *f)
139255028Salc{
140255028Salc	int status;
141255028Salc
142255028Salc	if (waitpid(WAIT_ANY, &status, WNOHANG) == -1)
143255028Salc		fprintf(stderr, "fuse: %s\n", strerror(errno));
144255028Salc
145255028Salc	if (WIFEXITED(status) && (WEXITSTATUS(status) != 0))
146255028Salc		fprintf(stderr, "fuse: %s: %s\n",
147255028Salc			f->fc->dir, strerror(WEXITSTATUS(status)));
148255028Salc
149255028Salc	return;
150255028Salc}
151255028Salc
152255028Salcint
153255028Salcfuse_loop(struct fuse *fuse)
154255028Salc{
155152179Sgrehan	struct fusebuf fbuf;
156152179Sgrehan	struct fuse_context ctx;
157152179Sgrehan	struct fb_ioctl_xch ioexch;
158152179Sgrehan	struct kevent event[5];
159152179Sgrehan	struct kevent ev;
160152179Sgrehan	ssize_t n;
161152179Sgrehan	int ret;
162152179Sgrehan
163152179Sgrehan	if (fuse == NULL)
164152179Sgrehan		return (-1);
165152179Sgrehan
166160889Salc	fuse->fc->kq = kqueue();
167160889Salc	if (fuse->fc->kq == -1)
168160889Salc		return (-1);
169160889Salc
170160889Salc	EV_SET(&event[0], fuse->fc->fd, EVFILT_READ, EV_ADD |
171160889Salc	    EV_ENABLE, 0, 0, 0);
172160889Salc
173160889Salc	/* signal events */
174160889Salc	EV_SET(&event[1], SIGCHLD, EVFILT_SIGNAL, EV_ADD |
175160889Salc	    EV_ENABLE, 0, 0, 0);
176160889Salc	EV_SET(&event[2], SIGHUP, EVFILT_SIGNAL, EV_ADD |
177160889Salc	    EV_ENABLE, 0, 0, 0);
178152179Sgrehan	EV_SET(&event[3], SIGINT, EVFILT_SIGNAL, EV_ADD |
179152179Sgrehan	    EV_ENABLE, 0, 0, 0);
180152179Sgrehan	EV_SET(&event[4], SIGTERM, EVFILT_SIGNAL, EV_ADD |
181152179Sgrehan	    EV_ENABLE, 0, 0, 0);
182152179Sgrehan
183152179Sgrehan	while (!fuse->fc->dead) {
184152179Sgrehan		ret = kevent(fuse->fc->kq, &event[0], 5, &ev, 1, NULL);
185152179Sgrehan		if (ret == -1) {
186152179Sgrehan			if (errno != EINTR)
187152179Sgrehan				DPERROR(__func__);
188152179Sgrehan		} else if (ret > 0 && ev.filter == EVFILT_SIGNAL) {
189152179Sgrehan			int signum = ev.ident;
190152179Sgrehan			switch (signum) {
191152179Sgrehan			case SIGCHLD:
192152179Sgrehan				ifuse_child_exit(fuse);
193152179Sgrehan				break;
194152179Sgrehan			case SIGHUP:
195152179Sgrehan			case SIGINT:
196152179Sgrehan			case SIGTERM:
197152179Sgrehan				ifuse_try_unmount(fuse);
198152179Sgrehan				break;
199152179Sgrehan			default:
200152179Sgrehan				fprintf(stderr, "%s: %s\n", __func__,
201152179Sgrehan					strsignal(signum));
202152179Sgrehan			}
203152179Sgrehan		} else if (ret > 0) {
204152179Sgrehan			n = read(fuse->fc->fd, &fbuf, sizeof(fbuf));
205152179Sgrehan			if (n != sizeof(fbuf)) {
206152179Sgrehan				fprintf(stderr, "%s: bad fusebuf read\n",
207152179Sgrehan				    __func__);
208152179Sgrehan				return (-1);
209152179Sgrehan			}
210248280Skib
211248280Skib			/* check if there is data something present */
212248280Skib			if (fbuf.fb_len) {
213248280Skib				fbuf.fb_dat = malloc(fbuf.fb_len);
214248280Skib				if (fbuf.fb_dat == NULL)
215248280Skib					return (-1);
216248280Skib				ioexch.fbxch_uuid = fbuf.fb_uuid;
217248280Skib				ioexch.fbxch_len = fbuf.fb_len;
218152179Sgrehan				ioexch.fbxch_data = fbuf.fb_dat;
219152179Sgrehan
220152179Sgrehan				if (ioctl(fuse->fc->fd, FIOCGETFBDAT,
221152179Sgrehan				    &ioexch) == -1) {
222152179Sgrehan					free(fbuf.fb_dat);
223152179Sgrehan					return (-1);
224152179Sgrehan				}
225152179Sgrehan			}
226152179Sgrehan
227270439Skib			ctx.fuse = fuse;
228270439Skib			ctx.uid = fbuf.fb_uid;
229152179Sgrehan			ctx.gid = fbuf.fb_gid;
230270439Skib			ctx.pid = fbuf.fb_tid;
231152179Sgrehan			ctx.umask = fbuf.fb_umask;
232152179Sgrehan			ctx.private_data = fuse->private_data;
233152179Sgrehan			ictx = &ctx;
234152179Sgrehan
235152179Sgrehan			ret = ifuse_exec_opcode(fuse, &fbuf);
236270439Skib			if (ret) {
237270439Skib				ictx = NULL;
238152179Sgrehan				return (-1);
239152179Sgrehan			}
240152179Sgrehan
241152179Sgrehan			n = write(fuse->fc->fd, &fbuf, sizeof(fbuf));
242159303Salc			if (fbuf.fb_len) {
243159303Salc				if (fbuf.fb_dat == NULL) {
244159303Salc					fprintf(stderr, "%s: fb_dat is Null\n",
245159303Salc					    __func__);
246159303Salc					return (-1);
247159303Salc				}
248159303Salc				ioexch.fbxch_uuid = fbuf.fb_uuid;
249159303Salc				ioexch.fbxch_len = fbuf.fb_len;
250159303Salc				ioexch.fbxch_data = fbuf.fb_dat;
251159303Salc
252159303Salc				if (ioctl(fuse->fc->fd, FIOCSETFBDAT, &ioexch) == -1) {
253159303Salc					free(fbuf.fb_dat);
254159303Salc					return (-1);
255159303Salc				}
256159303Salc				free(fbuf.fb_dat);
257159303Salc			}
258159303Salc			ictx = NULL;
259159303Salc
260159303Salc			if (n != FUSEBUFSIZE) {
261152179Sgrehan				errno = EINVAL;
262152179Sgrehan				return (-1);
263152179Sgrehan			}
264152179Sgrehan		}
265152179Sgrehan	}
266152179Sgrehan
267152179Sgrehan	return (0);
268152179Sgrehan}
269159627SupsDEF(fuse_loop);
270152179Sgrehan
271152179Sgrehanstruct fuse_chan *
272152179Sgrehanfuse_mount(const char *dir, struct fuse_args *args)
273152179Sgrehan{
274152179Sgrehan	struct fusefs_args fargs;
275152179Sgrehan	struct fuse_mount_opts opts;
276152179Sgrehan	struct fuse_chan *fc;
277152179Sgrehan	const char *errcause;
278152179Sgrehan	int mnt_flags;
279152179Sgrehan
280152179Sgrehan	if (dir == NULL)
281152179Sgrehan		return (NULL);
282152179Sgrehan
283152179Sgrehan	fc = calloc(1, sizeof(*fc));
284152179Sgrehan	if (fc == NULL)
285152179Sgrehan		return (NULL);
286152179Sgrehan
287152179Sgrehan	fc->dir = realpath(dir, NULL);
288152179Sgrehan	if (fc->dir == NULL)
289152179Sgrehan		goto bad;
290152179Sgrehan
291152179Sgrehan	if ((fc->fd = open("/dev/fuse0", O_RDWR)) == -1) {
292152179Sgrehan		perror(__func__);
293152179Sgrehan		goto bad;
294152179Sgrehan	}
295152179Sgrehan
296152179Sgrehan	memset(&opts, 0, sizeof(opts));
297152179Sgrehan	if (fuse_opt_parse(args, &opts, fuse_mount_opts, NULL) == -1)
298152179Sgrehan		goto bad;
299152179Sgrehan
300152179Sgrehan	mnt_flags = 0;
301152179Sgrehan	if (opts.rdonly)
302152179Sgrehan		mnt_flags |= MNT_RDONLY;
303152179Sgrehan	if (opts.noatime)
304152179Sgrehan		mnt_flags |= MNT_NOATIME;
305152179Sgrehan
306152179Sgrehan	if (opts.max_read > FUSEBUFMAXSIZE) {
307152179Sgrehan		fprintf(stderr, "fuse: invalid max_read (%d > %d)\n",
308152179Sgrehan		    opts.max_read, FUSEBUFMAXSIZE);
309152179Sgrehan		goto bad;
310152179Sgrehan	}
311152179Sgrehan
312152179Sgrehan	memset(&fargs, 0, sizeof(fargs));
313152179Sgrehan	fargs.fd = fc->fd;
314152179Sgrehan	fargs.max_read = opts.max_read;
315152179Sgrehan	fargs.allow_other = opts.allow_other;
316152179Sgrehan
317152179Sgrehan	if (mount(MOUNT_FUSEFS, fc->dir, mnt_flags, &fargs)) {
318152179Sgrehan		switch (errno) {
319152179Sgrehan		case EMFILE:
320152179Sgrehan			errcause = "mount table full";
321152179Sgrehan			break;
322152179Sgrehan		case EOPNOTSUPP:
323152179Sgrehan			errcause = "filesystem not supported by kernel";
324152179Sgrehan			break;
325152179Sgrehan		default:
326152179Sgrehan			errcause = strerror(errno);
327152179Sgrehan			break;
328152179Sgrehan		}
329152179Sgrehan		fprintf(stderr, "%s on %s: %s\n", __func__, dir, errcause);
330152179Sgrehan		goto bad;
331152179Sgrehan	}
332152179Sgrehan
333152179Sgrehan	return (fc);
334152179Sgrehanbad:
335152179Sgrehan	if (fc->fd != -1)
336152179Sgrehan		close(fc->fd);
337152179Sgrehan	free(fc->dir);
338152179Sgrehan	free(fc);
339152179Sgrehan	return (NULL);
340152179Sgrehan}
341152179SgrehanDEF(fuse_mount);
342152179Sgrehan
343152179Sgrehanvoid
344152179Sgrehanfuse_unmount(const char *dir, struct fuse_chan *ch)
345152179Sgrehan{
346152179Sgrehan	if (ch == NULL || ch->dead)
347152179Sgrehan		return;
348152179Sgrehan
349152179Sgrehan	if (unmount(dir, MNT_UPDATE) == -1)
350152179Sgrehan		DPERROR(__func__);
351152179Sgrehan}
352152179SgrehanDEF(fuse_unmount);
353152179Sgrehan
354152179Sgrehanint
355152179Sgrehanfuse_is_lib_option(const char *opt)
356152179Sgrehan{
357152179Sgrehan	return (fuse_opt_match(fuse_lib_opts, opt));
358152179Sgrehan}
359152179Sgrehan
360152179Sgrehanint
361152179Sgrehanfuse_chan_fd(struct fuse_chan *ch)
362152179Sgrehan{
363152179Sgrehan	if (ch == NULL)
364152179Sgrehan		return (-1);
365152179Sgrehan
366152179Sgrehan	return (ch->fd);
367152179Sgrehan}
368152179Sgrehan
369152179Sgrehanstruct fuse_session *
370207155Salcfuse_get_session(struct fuse *f)
371207155Salc{
372207155Salc	return (&f->se);
373207155Salc}
374207155SalcDEF(fuse_get_session);
375207155Salc
376207155Salcint
377207155Salcfuse_loop_mt(unused struct fuse *fuse)
378207155Salc{
379207155Salc	return (-1);
380207155Salc}
381207155Salc
382207155Salcstatic int
383207155Salcifuse_lib_opt_proc(void *data, const char *arg, int key,
384152179Sgrehan    unused struct fuse_args *args)
385152179Sgrehan{
386152179Sgrehan	switch (key) {
387152179Sgrehan	case KEY_STUB:
388152179Sgrehan		return (0);
389152179Sgrehan	case KEY_DEBUG:
390152179Sgrehan		ifuse_debug_init();
391152179Sgrehan		break;
392238357Salc	default:
393152179Sgrehan		fprintf(stderr, "fuse: unrecognised option %s\n", arg);
394152179Sgrehan		return (-1);
395152179Sgrehan	}
396152179Sgrehan
397152179Sgrehan	/* Keep unknown options. */
398152179Sgrehan	return (1);
399152179Sgrehan}
400152179Sgrehan
401152179Sgrehanstruct fuse *
402152179Sgrehanfuse_new(struct fuse_chan *fc, struct fuse_args *args,
403152179Sgrehan    const struct fuse_operations *ops, unused size_t size,
404152179Sgrehan    void *userdata)
405152179Sgrehan{
406152179Sgrehan	struct fuse *fuse;
407152179Sgrehan	struct fuse_vnode *root;
408152179Sgrehan
409152179Sgrehan	if (fc == NULL || ops == NULL)
410152179Sgrehan		return (NULL);
411152179Sgrehan
412152179Sgrehan	if ((fuse = calloc(1, sizeof(*fuse))) == NULL)
413152179Sgrehan		return (NULL);
414152179Sgrehan
415152179Sgrehan	/* copy fuse ops to their own structure */
416152179Sgrehan	memcpy(&fuse->op, ops, sizeof(fuse->op));
417152179Sgrehan
418152179Sgrehan	if (fuse_opt_parse(args, &fuse->conf, fuse_lib_opts,
419152179Sgrehan	    ifuse_lib_opt_proc) == -1) {
420152179Sgrehan		free(fuse);
421152179Sgrehan		return (NULL);
422152179Sgrehan	}
423152179Sgrehan
424152179Sgrehan	fuse->fc = fc;
425152179Sgrehan	fuse->max_ino = FUSE_ROOT_INO;
426152179Sgrehan	fuse->se.args = fuse;
427152179Sgrehan	fuse->private_data = userdata;
428152179Sgrehan
429152179Sgrehan	if ((root = alloc_vn(fuse, "/", FUSE_ROOT_INO, 0)) == NULL) {
430152179Sgrehan		free(fuse);
431152179Sgrehan		return (NULL);
432152179Sgrehan	}
433152179Sgrehan
434152179Sgrehan	tree_init(&fuse->vnode_tree);
435152179Sgrehan	tree_init(&fuse->name_tree);
436152179Sgrehan	if (!set_vn(fuse, root)) {
437152179Sgrehan		free(fuse);
438152179Sgrehan		return (NULL);
439152179Sgrehan	}
440152179Sgrehan
441152179Sgrehan	return (fuse);
442152179Sgrehan}
443152179SgrehanDEF(fuse_new);
444152179Sgrehan
445152179Sgrehanint
446152179Sgrehanfuse_daemonize(int foreground)
447152179Sgrehan{
448152179Sgrehan	if (foreground)
449152179Sgrehan		return (0);
450152179Sgrehan
451152179Sgrehan	return (daemon(0, 0));
452152179Sgrehan}
453152179SgrehanDEF(fuse_daemonize);
454152179Sgrehan
455152179Sgrehanvoid
456152179Sgrehanfuse_destroy(struct fuse *f)
457152179Sgrehan{
458152179Sgrehan	if (f == NULL)
459152179Sgrehan		return;
460152179Sgrehan
461152179Sgrehan	/*
462152179Sgrehan  	 * Even though these were allocated in fuse_mount(), we can't free them
463152179Sgrehan 	 * in fuse_unmount() since fuse_loop() will not have terminated yet so
464152179Sgrehan 	 * we free them here.
465152179Sgrehan 	 */
466152179Sgrehan	close(f->fc->fd);
467152179Sgrehan	free(f->fc->dir);
468152179Sgrehan	free(f->fc);
469152179Sgrehan	free(f);
470152179Sgrehan}
471152179SgrehanDEF(fuse_destroy);
472152179Sgrehan
473152179Sgrehanvoid
474173708Salcfuse_remove_signal_handlers(unused struct fuse_session *se)
475173708Salc{
476173708Salc	struct sigaction old_sa;
477173708Salc
478173708Salc	if (sigaction(SIGHUP, NULL, &old_sa) == 0)
479173708Salc		if (old_sa.sa_handler == SIG_IGN)
480173708Salc			signal(SIGHUP, SIG_DFL);
481173708Salc
482173708Salc	if (sigaction(SIGINT, NULL, &old_sa) == 0)
483173708Salc		if (old_sa.sa_handler == SIG_IGN)
484173708Salc			signal(SIGINT, SIG_DFL);
485173708Salc
486173708Salc	if (sigaction(SIGTERM, NULL, &old_sa) == 0)
487173708Salc		if (old_sa.sa_handler == SIG_IGN)
488173708Salc			signal(SIGTERM, SIG_DFL);
489152179Sgrehan
490152179Sgrehan	if (sigaction(SIGPIPE, NULL, &old_sa) == 0)
491152179Sgrehan		if (old_sa.sa_handler == SIG_IGN)
492152179Sgrehan			signal(SIGPIPE, SIG_DFL);
493152179Sgrehan
494152179Sgrehan	if (sigaction(SIGCHLD, NULL, &old_sa) == 0)
495152179Sgrehan		if (old_sa.sa_handler == SIG_IGN)
496152179Sgrehan			signal(SIGCHLD, SIG_DFL);
497152179Sgrehan}
498152179SgrehanDEF(fuse_remove_signal_handlers);
499152179Sgrehan
500152179Sgrehanint
501152179Sgrehanfuse_set_signal_handlers(unused struct fuse_session *se)
502152179Sgrehan{
503152179Sgrehan	struct sigaction old_sa;
504152179Sgrehan
505152179Sgrehan	if (sigaction(SIGHUP, NULL, &old_sa) == -1)
506152179Sgrehan		return (-1);
507152179Sgrehan	if (old_sa.sa_handler == SIG_DFL)
508152179Sgrehan		signal(SIGHUP, SIG_IGN);
509152179Sgrehan
510152179Sgrehan	if (sigaction(SIGINT, NULL, &old_sa) == -1)
511152179Sgrehan		return (-1);
512152179Sgrehan	if (old_sa.sa_handler == SIG_DFL)
513152179Sgrehan		signal(SIGINT, SIG_IGN);
514152179Sgrehan
515152179Sgrehan	if (sigaction(SIGTERM, NULL, &old_sa) == -1)
516152179Sgrehan		return (-1);
517152179Sgrehan	if (old_sa.sa_handler == SIG_DFL)
518152179Sgrehan		signal(SIGTERM, SIG_IGN);
519152179Sgrehan
520152179Sgrehan	if (sigaction(SIGPIPE, NULL, &old_sa) == -1)
521152179Sgrehan		return (-1);
522152179Sgrehan	if (old_sa.sa_handler == SIG_DFL)
523152179Sgrehan		signal(SIGPIPE, SIG_IGN);
524152179Sgrehan
525152179Sgrehan	if (sigaction(SIGCHLD, NULL, &old_sa) == -1)
526152179Sgrehan		return (-1);
527152179Sgrehan	if (old_sa.sa_handler == SIG_DFL)
528152179Sgrehan		signal(SIGCHLD, SIG_IGN);
529152179Sgrehan
530152179Sgrehan	return (0);
531152179Sgrehan}
532152179Sgrehan
533152179Sgrehanstatic void
534152179Sgrehandump_help(void)
535152179Sgrehan{
536152179Sgrehan	fprintf(stderr, "FUSE options:\n"
537152179Sgrehan	    "    -d   -o debug          enable debug output (implies -f)\n"
538152179Sgrehan	    "    -f                     run in foreground\n"
539152179Sgrehan	    "    -V   --version         print fuse version\n"
540152179Sgrehan	    "\n");
541152179Sgrehan}
542152179Sgrehan
543152179Sgrehanstatic void
544152179Sgrehandump_version(void)
545152179Sgrehan{
546152179Sgrehan	fprintf(stderr, "FUSE library version: %d.%d\n", FUSE_MAJOR_VERSION,
547152179Sgrehan	    FUSE_MINOR_VERSION);
548152179Sgrehan}
549152179Sgrehan
550152179Sgrehanstatic int
551152179Sgrehanifuse_process_opt(void *data, const char *arg, int key,
552152179Sgrehan    unused struct fuse_args *args)
553152179Sgrehan{
554152179Sgrehan	struct fuse_core_opts *opt = data;
555152179Sgrehan	struct stat st;
556152179Sgrehan	int res;
557152179Sgrehan
558152179Sgrehan	switch (key) {
559152179Sgrehan	case KEY_STUB:
560152179Sgrehan		return (0);
561152179Sgrehan	case KEY_DEBUG:
562152179Sgrehan		ifuse_debug_init();
563152179Sgrehan		/* falls through */
564152179Sgrehan	case KEY_FOREGROUND:
565152179Sgrehan		opt->foreground = 1;
566152179Sgrehan		return (0);
567152179Sgrehan	case KEY_HELP:
568152179Sgrehan	case KEY_HELP_WITHOUT_HEADER:
569152179Sgrehan		dump_help();
570152179Sgrehan		return (-1);
571152179Sgrehan	case KEY_VERSION:
572152179Sgrehan		dump_version();
573152179Sgrehan		return (-1);
574152179Sgrehan	case FUSE_OPT_KEY_NONOPT:
575152179Sgrehan		if (opt->mp == NULL) {
576152179Sgrehan			opt->mp = realpath(arg, opt->mp);
577152179Sgrehan			if (opt->mp == NULL) {
578152179Sgrehan				fprintf(stderr, "fuse: realpath: "
579152179Sgrehan				    "%s : %s\n", arg, strerror(errno));
580152179Sgrehan				return (-1);
581152179Sgrehan			}
582152179Sgrehan
583152179Sgrehan			res = stat(opt->mp, &st);
584152179Sgrehan			if (res == -1) {
585152179Sgrehan				fprintf(stderr, "fuse: bad mount point "
586152179Sgrehan				    "%s : %s\n", arg, strerror(errno));
587152179Sgrehan				return (-1);
588152179Sgrehan			}
589225418Skib
590152179Sgrehan			if (!S_ISDIR(st.st_mode)) {
591152179Sgrehan				fprintf(stderr, "fuse: bad mount point "
592152179Sgrehan				    "%s : %s\n", arg, strerror(ENOTDIR));
593152179Sgrehan				return (-1);
594152179Sgrehan			}
595152179Sgrehan		}
596152179Sgrehan		return (0);
597152179Sgrehan	}
598152179Sgrehan
599152179Sgrehan	/* Pass through unknown options. */
600152179Sgrehan	return (1);
601152179Sgrehan}
602152179Sgrehan
603152179Sgrehanint
604152179Sgrehanfuse_parse_cmdline(struct fuse_args *args, char **mp, int *mt, int *fg)
605152179Sgrehan{
606152179Sgrehan	struct fuse_core_opts opt;
607152179Sgrehan
608152179Sgrehan	memset(&opt, 0, sizeof(opt));
609152179Sgrehan	if (fuse_opt_parse(args, &opt, fuse_core_opts, ifuse_process_opt) == -1)
610152179Sgrehan		return (-1);
611152179Sgrehan
612152179Sgrehan	if (opt.mp == NULL) {
613152179Sgrehan		fprintf(stderr, "fuse: missing mountpoint parameter\n");
614152179Sgrehan		return (-1);
615152179Sgrehan	}
616152179Sgrehan
617270920Skib	if (mp != NULL) {
618270920Skib		*mp = strdup(opt.mp);
619270920Skib		if (*mp == NULL)
620270920Skib			return (-1);
621270920Skib	}
622270920Skib
623270920Skib	if (mt != NULL)
624270920Skib		*mt = 0;
625270920Skib
626270920Skib	if (fg != NULL)
627270920Skib		*fg = opt.foreground;
628270920Skib
629270920Skib	return (0);
630270920Skib}
631270920SkibDEF(fuse_parse_cmdline);
632270920Skib
633152179Sgrehanstruct fuse_context *
634152179Sgrehanfuse_get_context(void)
635152179Sgrehan{
636152179Sgrehan	return (ictx);
637152179Sgrehan}
638152179SgrehanDEF(fuse_get_context);
639152179Sgrehan
640152179Sgrehanint
641152179Sgrehanfuse_version(void)
642152179Sgrehan{
643152179Sgrehan	return (FUSE_VERSION);
644152179Sgrehan}
645152179Sgrehan
646152179Sgrehanvoid
647152179Sgrehanfuse_teardown(struct fuse *fuse, char *mp)
648152179Sgrehan{
649152179Sgrehan	if (fuse == NULL || mp == NULL)
650152179Sgrehan		return;
651152179Sgrehan
652152179Sgrehan	fuse_remove_signal_handlers(fuse_get_session(fuse));
653152179Sgrehan	fuse_unmount(mp, fuse->fc);
654152179Sgrehan	fuse_destroy(fuse);
655152179Sgrehan}
656152179Sgrehan
657152179Sgrehanint
658152179Sgrehanfuse_invalidate(unused struct fuse *f, unused const char *path)
659152179Sgrehan{
660152179Sgrehan	return (EINVAL);
661152179Sgrehan}
662152179Sgrehan
663152179Sgrehanstruct fuse *
664152179Sgrehanfuse_setup(int argc, char **argv, const struct fuse_operations *ops,
665152179Sgrehan    size_t size, char **mp, int *mt, void *data)
666152179Sgrehan{
667152179Sgrehan	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
668152179Sgrehan	struct fuse_chan *fc;
669152179Sgrehan	struct fuse *fuse;
670152179Sgrehan	char *dir;
671152179Sgrehan	int fg;
672152179Sgrehan
673208504Salc	dir = NULL;
674152179Sgrehan	if (fuse_parse_cmdline(&args, &dir, mt, &fg))
675152179Sgrehan		goto err;
676152179Sgrehan
677208504Salc	fuse_daemonize(fg);
678152179Sgrehan
679152179Sgrehan	if ((fc = fuse_mount(dir, &args)) == NULL)
680152179Sgrehan		goto err;
681152179Sgrehan
682152179Sgrehan	if ((fuse = fuse_new(fc, &args, ops, size, data)) == NULL) {
683152179Sgrehan		fuse_unmount(dir, fc);
684152179Sgrehan		close(fc->fd);
685152179Sgrehan		free(fc->dir);
686208504Salc		free(fc);
687152179Sgrehan		goto err;
688152179Sgrehan	}
689152179Sgrehan
690152179Sgrehan	/* args are no longer needed */
691152179Sgrehan	fuse_opt_free_args(&args);
692152179Sgrehan
693152179Sgrehan	if (fuse_set_signal_handlers(fuse_get_session(fuse)) == -1) {
694152179Sgrehan		fuse_unmount(dir, fc);
695152179Sgrehan		fuse_destroy(fuse);
696152179Sgrehan		goto err;
697152179Sgrehan	}
698152179Sgrehan
699152179Sgrehan	/* the caller frees dir, but we do it if the caller doesn't want it */
700152179Sgrehan	if (mp == NULL)
701152179Sgrehan		free(dir);
702152179Sgrehan	else
703152179Sgrehan		*mp = dir;
704152179Sgrehan
705152179Sgrehan	return (fuse);
706152179Sgrehanerr:
707152179Sgrehan	free(dir);
708152179Sgrehan	return (NULL);
709152179Sgrehan}
710152179SgrehanDEF(fuse_setup);
711152179Sgrehan
712152179Sgrehanint
713152179Sgrehanfuse_main(int argc, char **argv, const struct fuse_operations *ops, void *data)
714152179Sgrehan{
715152179Sgrehan	struct fuse *fuse;
716152179Sgrehan
717152179Sgrehan	fuse = fuse_setup(argc, argv, ops, sizeof(*ops), NULL, NULL, data);
718179081Salc	if (fuse == NULL)
719152179Sgrehan		return (-1);
720152179Sgrehan
721152179Sgrehan	return (fuse_loop(fuse));
722179081Salc}
723152179Sgrehan