xref: /freebsd/sys/fs/fuse/fuse_device.c (revision 155ac516c60f20573d15c54bafabfd0e52d21fa6)
151369649SPedro F. Giffuni /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
45fe58019SAttilio Rao  * Copyright (c) 2007-2009 Google Inc.
55fe58019SAttilio Rao  * All rights reserved.
65fe58019SAttilio Rao  *
75fe58019SAttilio Rao  * Redistribution and use in source and binary forms, with or without
85fe58019SAttilio Rao  * modification, are permitted provided that the following conditions are
95fe58019SAttilio Rao  * met:
105fe58019SAttilio Rao  *
115fe58019SAttilio Rao  * * Redistributions of source code must retain the above copyright
125fe58019SAttilio Rao  *   notice, this list of conditions and the following disclaimer.
135fe58019SAttilio Rao  * * Redistributions in binary form must reproduce the above
145fe58019SAttilio Rao  *   copyright notice, this list of conditions and the following disclaimer
155fe58019SAttilio Rao  *   in the documentation and/or other materials provided with the
165fe58019SAttilio Rao  *   distribution.
175fe58019SAttilio Rao  * * Neither the name of Google Inc. nor the names of its
185fe58019SAttilio Rao  *   contributors may be used to endorse or promote products derived from
195fe58019SAttilio Rao  *   this software without specific prior written permission.
205fe58019SAttilio Rao  *
215fe58019SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
225fe58019SAttilio Rao  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
235fe58019SAttilio Rao  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
245fe58019SAttilio Rao  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
255fe58019SAttilio Rao  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
265fe58019SAttilio Rao  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
275fe58019SAttilio Rao  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
285fe58019SAttilio Rao  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
295fe58019SAttilio Rao  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
305fe58019SAttilio Rao  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
315fe58019SAttilio Rao  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
325fe58019SAttilio Rao  *
335fe58019SAttilio Rao  * Copyright (C) 2005 Csaba Henk.
345fe58019SAttilio Rao  * All rights reserved.
355fe58019SAttilio Rao  *
368aafc8c3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
378aafc8c3SAlan Somers  *
388aafc8c3SAlan Somers  * Portions of this software were developed by BFF Storage Systems, LLC under
398aafc8c3SAlan Somers  * sponsorship from the FreeBSD Foundation.
408aafc8c3SAlan Somers  *
415fe58019SAttilio Rao  * Redistribution and use in source and binary forms, with or without
425fe58019SAttilio Rao  * modification, are permitted provided that the following conditions
435fe58019SAttilio Rao  * are met:
445fe58019SAttilio Rao  * 1. Redistributions of source code must retain the above copyright
455fe58019SAttilio Rao  *    notice, this list of conditions and the following disclaimer.
465fe58019SAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
475fe58019SAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
485fe58019SAttilio Rao  *    documentation and/or other materials provided with the distribution.
495fe58019SAttilio Rao  *
505fe58019SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
515fe58019SAttilio Rao  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
525fe58019SAttilio Rao  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
535fe58019SAttilio Rao  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
545fe58019SAttilio Rao  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
555fe58019SAttilio Rao  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
565fe58019SAttilio Rao  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
575fe58019SAttilio Rao  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
585fe58019SAttilio Rao  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
595fe58019SAttilio Rao  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
605fe58019SAttilio Rao  * SUCH DAMAGE.
615fe58019SAttilio Rao  */
625fe58019SAttilio Rao 
635fe58019SAttilio Rao #include <sys/cdefs.h>
645fe58019SAttilio Rao __FBSDID("$FreeBSD$");
655fe58019SAttilio Rao 
665fe58019SAttilio Rao #include <sys/types.h>
67df38ada2SBjoern A. Zeeb #include <sys/param.h>
685fe58019SAttilio Rao #include <sys/module.h>
695fe58019SAttilio Rao #include <sys/systm.h>
705fe58019SAttilio Rao #include <sys/errno.h>
715fe58019SAttilio Rao #include <sys/param.h>
725fe58019SAttilio Rao #include <sys/kernel.h>
735fe58019SAttilio Rao #include <sys/conf.h>
745fe58019SAttilio Rao #include <sys/uio.h>
755fe58019SAttilio Rao #include <sys/malloc.h>
765fe58019SAttilio Rao #include <sys/queue.h>
775fe58019SAttilio Rao #include <sys/lock.h>
785fe58019SAttilio Rao #include <sys/sx.h>
795fe58019SAttilio Rao #include <sys/mutex.h>
805fe58019SAttilio Rao #include <sys/proc.h>
815fe58019SAttilio Rao #include <sys/mount.h>
82cf169498SAlan Somers #include <sys/sdt.h>
835fe58019SAttilio Rao #include <sys/stat.h>
845fe58019SAttilio Rao #include <sys/fcntl.h>
855fe58019SAttilio Rao #include <sys/sysctl.h>
865fe58019SAttilio Rao #include <sys/poll.h>
875fe58019SAttilio Rao #include <sys/selinfo.h>
885fe58019SAttilio Rao 
895fe58019SAttilio Rao #include "fuse.h"
90c2d70d6eSAlan Somers #include "fuse_internal.h"
915fe58019SAttilio Rao #include "fuse_ipc.h"
925fe58019SAttilio Rao 
93e3b1c847SEdward Tomasz Napierala #include <compat/linux/linux_errno.h>
94e3b1c847SEdward Tomasz Napierala #include <compat/linux/linux_errno.inc>
95e3b1c847SEdward Tomasz Napierala 
96419e7ff6SAlan Somers SDT_PROVIDER_DECLARE(fusefs);
97cf169498SAlan Somers /*
98cf169498SAlan Somers  * Fuse trace probe:
99cf169498SAlan Somers  * arg0: verbosity.  Higher numbers give more verbose messages
100cf169498SAlan Somers  * arg1: Textual message
101cf169498SAlan Somers  */
102419e7ff6SAlan Somers SDT_PROBE_DEFINE2(fusefs, , device, trace, "int", "char*");
1035fe58019SAttilio Rao 
1045fe58019SAttilio Rao static struct cdev *fuse_dev;
1055fe58019SAttilio Rao 
1063429092cSAlan Somers static d_kqfilter_t fuse_device_filter;
1075fe58019SAttilio Rao static d_open_t fuse_device_open;
1085fe58019SAttilio Rao static d_poll_t fuse_device_poll;
1095fe58019SAttilio Rao static d_read_t fuse_device_read;
1105fe58019SAttilio Rao static d_write_t fuse_device_write;
1115fe58019SAttilio Rao 
1125fe58019SAttilio Rao static struct cdevsw fuse_device_cdevsw = {
1133429092cSAlan Somers 	.d_kqfilter = fuse_device_filter,
1145fe58019SAttilio Rao 	.d_open = fuse_device_open,
1155fe58019SAttilio Rao 	.d_name = "fuse",
1165fe58019SAttilio Rao 	.d_poll = fuse_device_poll,
1175fe58019SAttilio Rao 	.d_read = fuse_device_read,
1185fe58019SAttilio Rao 	.d_write = fuse_device_write,
1195fe58019SAttilio Rao 	.d_version = D_VERSION,
1205fe58019SAttilio Rao };
1215fe58019SAttilio Rao 
1223429092cSAlan Somers static int fuse_device_filt_read(struct knote *kn, long hint);
1237b8622faSAlan Somers static int fuse_device_filt_write(struct knote *kn, long hint);
1243429092cSAlan Somers static void fuse_device_filt_detach(struct knote *kn);
1253429092cSAlan Somers 
1263429092cSAlan Somers struct filterops fuse_device_rfiltops = {
1273429092cSAlan Somers 	.f_isfd = 1,
1283429092cSAlan Somers 	.f_detach = fuse_device_filt_detach,
1293429092cSAlan Somers 	.f_event = fuse_device_filt_read,
1303429092cSAlan Somers };
1313429092cSAlan Somers 
1327b8622faSAlan Somers struct filterops fuse_device_wfiltops = {
1337b8622faSAlan Somers 	.f_isfd = 1,
1347b8622faSAlan Somers 	.f_event = fuse_device_filt_write,
1357b8622faSAlan Somers };
1367b8622faSAlan Somers 
1375fe58019SAttilio Rao /****************************
1385fe58019SAttilio Rao  *
1395fe58019SAttilio Rao  * >>> Fuse device op defs
1405fe58019SAttilio Rao  *
1415fe58019SAttilio Rao  ****************************/
1425fe58019SAttilio Rao 
1435fe58019SAttilio Rao static void
1445fe58019SAttilio Rao fdata_dtor(void *arg)
1455fe58019SAttilio Rao {
1465fe58019SAttilio Rao 	struct fuse_data *fdata;
1478b73a4c5SAlan Somers 	struct fuse_ticket *tick;
1485fe58019SAttilio Rao 
1495fe58019SAttilio Rao 	fdata = arg;
1508b73a4c5SAlan Somers 	if (fdata == NULL)
1518b73a4c5SAlan Somers 		return;
1528b73a4c5SAlan Somers 
1538b73a4c5SAlan Somers 	fdata_set_dead(fdata);
1548b73a4c5SAlan Somers 
1558b73a4c5SAlan Somers 	FUSE_LOCK();
1568b73a4c5SAlan Somers 	fuse_lck_mtx_lock(fdata->aw_mtx);
1578b73a4c5SAlan Somers 	/* wakup poll()ers */
1588b73a4c5SAlan Somers 	selwakeuppri(&fdata->ks_rsel, PZERO + 1);
1598b73a4c5SAlan Somers 	/* Don't let syscall handlers wait in vain */
1608b73a4c5SAlan Somers 	while ((tick = fuse_aw_pop(fdata))) {
1618b73a4c5SAlan Somers 		fuse_lck_mtx_lock(tick->tk_aw_mtx);
1628b73a4c5SAlan Somers 		fticket_set_answered(tick);
1638b73a4c5SAlan Somers 		tick->tk_aw_errno = ENOTCONN;
1648b73a4c5SAlan Somers 		wakeup(tick);
1658b73a4c5SAlan Somers 		fuse_lck_mtx_unlock(tick->tk_aw_mtx);
1668b73a4c5SAlan Somers 		FUSE_ASSERT_AW_DONE(tick);
1678b73a4c5SAlan Somers 		fuse_ticket_drop(tick);
1688b73a4c5SAlan Somers 	}
1698b73a4c5SAlan Somers 	fuse_lck_mtx_unlock(fdata->aw_mtx);
1707e1f5432SAlan Somers 
1717e1f5432SAlan Somers 	/* Cleanup unsent operations */
1727e1f5432SAlan Somers 	fuse_lck_mtx_lock(fdata->ms_mtx);
1737e1f5432SAlan Somers 	while ((tick = fuse_ms_pop(fdata))) {
1747e1f5432SAlan Somers 		fuse_ticket_drop(tick);
1757e1f5432SAlan Somers 	}
1767e1f5432SAlan Somers 	fuse_lck_mtx_unlock(fdata->ms_mtx);
1778b73a4c5SAlan Somers 	FUSE_UNLOCK();
1788b73a4c5SAlan Somers 
1795fe58019SAttilio Rao 	fdata_trydestroy(fdata);
1805fe58019SAttilio Rao }
1815fe58019SAttilio Rao 
1823429092cSAlan Somers static int
1833429092cSAlan Somers fuse_device_filter(struct cdev *dev, struct knote *kn)
1843429092cSAlan Somers {
1853429092cSAlan Somers 	struct fuse_data *data;
1863429092cSAlan Somers 	int error;
1873429092cSAlan Somers 
1883429092cSAlan Somers 	error = devfs_get_cdevpriv((void **)&data);
1893429092cSAlan Somers 
1903429092cSAlan Somers 	if (error == 0 && kn->kn_filter == EVFILT_READ) {
1913429092cSAlan Somers 		kn->kn_fop = &fuse_device_rfiltops;
1923429092cSAlan Somers 		kn->kn_hook = data;
1933429092cSAlan Somers 		knlist_add(&data->ks_rsel.si_note, kn, 0);
1943429092cSAlan Somers 		error = 0;
1957b8622faSAlan Somers 	} else if (error == 0 && kn->kn_filter == EVFILT_WRITE) {
1967b8622faSAlan Somers 		kn->kn_fop = &fuse_device_wfiltops;
1977b8622faSAlan Somers 		error = 0;
1983429092cSAlan Somers 	} else if (error == 0) {
1993429092cSAlan Somers 		error = EINVAL;
2003429092cSAlan Somers 		kn->kn_data = error;
2013429092cSAlan Somers 	}
2023429092cSAlan Somers 
2033429092cSAlan Somers 	return (error);
2043429092cSAlan Somers }
2053429092cSAlan Somers 
2063429092cSAlan Somers static void
2073429092cSAlan Somers fuse_device_filt_detach(struct knote *kn)
2083429092cSAlan Somers {
2093429092cSAlan Somers 	struct fuse_data *data;
2103429092cSAlan Somers 
2113429092cSAlan Somers 	data = (struct fuse_data*)kn->kn_hook;
2123429092cSAlan Somers 	MPASS(data != NULL);
2133429092cSAlan Somers 	knlist_remove(&data->ks_rsel.si_note, kn, 0);
2143429092cSAlan Somers 	kn->kn_hook = NULL;
2153429092cSAlan Somers }
2163429092cSAlan Somers 
2173429092cSAlan Somers static int
2183429092cSAlan Somers fuse_device_filt_read(struct knote *kn, long hint)
2193429092cSAlan Somers {
2203429092cSAlan Somers 	struct fuse_data *data;
2213429092cSAlan Somers 	int ready;
2223429092cSAlan Somers 
2233429092cSAlan Somers 	data = (struct fuse_data*)kn->kn_hook;
2243429092cSAlan Somers 	MPASS(data != NULL);
2253429092cSAlan Somers 
2263429092cSAlan Somers 	mtx_assert(&data->ms_mtx, MA_OWNED);
2273429092cSAlan Somers 	if (fdata_get_dead(data)) {
2283429092cSAlan Somers 		kn->kn_flags |= EV_EOF;
2293429092cSAlan Somers 		kn->kn_fflags = ENODEV;
2303429092cSAlan Somers 		kn->kn_data = 1;
2313429092cSAlan Somers 		ready = 1;
2323429092cSAlan Somers 	} else if (STAILQ_FIRST(&data->ms_head)) {
2330a7c63e0SAlan Somers 		MPASS(data->ms_count >= 1);
2340a7c63e0SAlan Somers 		kn->kn_data = data->ms_count;
2353429092cSAlan Somers 		ready = 1;
2363429092cSAlan Somers 	} else {
2373429092cSAlan Somers 		ready = 0;
2383429092cSAlan Somers 	}
2393429092cSAlan Somers 
2403429092cSAlan Somers 	return (ready);
2413429092cSAlan Somers }
2423429092cSAlan Somers 
2437b8622faSAlan Somers static int
2447b8622faSAlan Somers fuse_device_filt_write(struct knote *kn, long hint)
2457b8622faSAlan Somers {
2467b8622faSAlan Somers 
2477b8622faSAlan Somers 	kn->kn_data = 0;
2487b8622faSAlan Somers 
2499b876fbdSgAlfonso-bit 	/* The device is always ready to write, so we return 1*/
2509b876fbdSgAlfonso-bit 	return (1);
2517b8622faSAlan Somers }
2527b8622faSAlan Somers 
2535fe58019SAttilio Rao /*
2545fe58019SAttilio Rao  * Resources are set up on a per-open basis
2555fe58019SAttilio Rao  */
2565fe58019SAttilio Rao static int
2575fe58019SAttilio Rao fuse_device_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
2585fe58019SAttilio Rao {
2595fe58019SAttilio Rao 	struct fuse_data *fdata;
2605fe58019SAttilio Rao 	int error;
2615fe58019SAttilio Rao 
262419e7ff6SAlan Somers 	SDT_PROBE2(fusefs, , device, trace, 1, "device open");
2635fe58019SAttilio Rao 
2645fe58019SAttilio Rao 	fdata = fdata_alloc(dev, td->td_ucred);
2655fe58019SAttilio Rao 	error = devfs_set_cdevpriv(fdata, fdata_dtor);
2665fe58019SAttilio Rao 	if (error != 0)
2675fe58019SAttilio Rao 		fdata_trydestroy(fdata);
2685fe58019SAttilio Rao 	else
269419e7ff6SAlan Somers 		SDT_PROBE2(fusefs, , device, trace, 1, "device open success");
2705fe58019SAttilio Rao 	return (error);
2715fe58019SAttilio Rao }
2725fe58019SAttilio Rao 
2735fe58019SAttilio Rao int
2745fe58019SAttilio Rao fuse_device_poll(struct cdev *dev, int events, struct thread *td)
2755fe58019SAttilio Rao {
2765fe58019SAttilio Rao 	struct fuse_data *data;
2775fe58019SAttilio Rao 	int error, revents = 0;
2785fe58019SAttilio Rao 
2795fe58019SAttilio Rao 	error = devfs_get_cdevpriv((void **)&data);
2805fe58019SAttilio Rao 	if (error != 0)
2815fe58019SAttilio Rao 		return (events &
2825fe58019SAttilio Rao 		    (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
2835fe58019SAttilio Rao 
2845fe58019SAttilio Rao 	if (events & (POLLIN | POLLRDNORM)) {
2855fe58019SAttilio Rao 		fuse_lck_mtx_lock(data->ms_mtx);
2865fe58019SAttilio Rao 		if (fdata_get_dead(data) || STAILQ_FIRST(&data->ms_head))
2875fe58019SAttilio Rao 			revents |= events & (POLLIN | POLLRDNORM);
2885fe58019SAttilio Rao 		else
2895fe58019SAttilio Rao 			selrecord(td, &data->ks_rsel);
2905fe58019SAttilio Rao 		fuse_lck_mtx_unlock(data->ms_mtx);
2915fe58019SAttilio Rao 	}
2925fe58019SAttilio Rao 	if (events & (POLLOUT | POLLWRNORM)) {
2935fe58019SAttilio Rao 		revents |= events & (POLLOUT | POLLWRNORM);
2945fe58019SAttilio Rao 	}
2955fe58019SAttilio Rao 	return (revents);
2965fe58019SAttilio Rao }
2975fe58019SAttilio Rao 
2985fe58019SAttilio Rao /*
2995fe58019SAttilio Rao  * fuse_device_read hangs on the queue of VFS messages.
3005fe58019SAttilio Rao  * When it's notified that there is a new one, it picks that and
3015fe58019SAttilio Rao  * passes up to the daemon
3025fe58019SAttilio Rao  */
3035fe58019SAttilio Rao int
3045fe58019SAttilio Rao fuse_device_read(struct cdev *dev, struct uio *uio, int ioflag)
3055fe58019SAttilio Rao {
3065fe58019SAttilio Rao 	int err;
3075fe58019SAttilio Rao 	struct fuse_data *data;
3085fe58019SAttilio Rao 	struct fuse_ticket *tick;
3094f4111d2SAlan Somers 	void *buf;
3104f4111d2SAlan Somers 	int buflen;
3115fe58019SAttilio Rao 
312419e7ff6SAlan Somers 	SDT_PROBE2(fusefs, , device, trace, 1, "fuse device read");
3135fe58019SAttilio Rao 
3145fe58019SAttilio Rao 	err = devfs_get_cdevpriv((void **)&data);
3155fe58019SAttilio Rao 	if (err != 0)
3165fe58019SAttilio Rao 		return (err);
3175fe58019SAttilio Rao 
3185fe58019SAttilio Rao 	fuse_lck_mtx_lock(data->ms_mtx);
3195fe58019SAttilio Rao again:
3205fe58019SAttilio Rao 	if (fdata_get_dead(data)) {
321419e7ff6SAlan Somers 		SDT_PROBE2(fusefs, , device, trace, 2,
322cf169498SAlan Somers 			"we know early on that reader should be kicked so we "
323cf169498SAlan Somers 			"don't wait for news");
3245fe58019SAttilio Rao 		fuse_lck_mtx_unlock(data->ms_mtx);
3255fe58019SAttilio Rao 		return (ENODEV);
3265fe58019SAttilio Rao 	}
3275fe58019SAttilio Rao 	if (!(tick = fuse_ms_pop(data))) {
3285fe58019SAttilio Rao 		/* check if we may block */
3295fe58019SAttilio Rao 		if (ioflag & O_NONBLOCK) {
3305fe58019SAttilio Rao 			/* get outa here soon */
3315fe58019SAttilio Rao 			fuse_lck_mtx_unlock(data->ms_mtx);
3325fe58019SAttilio Rao 			return (EAGAIN);
3335fe58019SAttilio Rao 		} else {
3345fe58019SAttilio Rao 			err = msleep(data, &data->ms_mtx, PCATCH, "fu_msg", 0);
3355fe58019SAttilio Rao 			if (err != 0) {
3365fe58019SAttilio Rao 				fuse_lck_mtx_unlock(data->ms_mtx);
3375fe58019SAttilio Rao 				return (fdata_get_dead(data) ? ENODEV : err);
3385fe58019SAttilio Rao 			}
3395fe58019SAttilio Rao 			tick = fuse_ms_pop(data);
3405fe58019SAttilio Rao 		}
3415fe58019SAttilio Rao 	}
3425fe58019SAttilio Rao 	if (!tick) {
3435fe58019SAttilio Rao 		/*
3445fe58019SAttilio Rao 		 * We can get here if fuse daemon suddenly terminates,
3455fe58019SAttilio Rao 		 * eg, by being hit by a SIGKILL
3465fe58019SAttilio Rao 		 * -- and some other cases, too, tho not totally clear, when
3475fe58019SAttilio Rao 		 * (cv_signal/wakeup_one signals the whole process ?)
3485fe58019SAttilio Rao 		 */
349419e7ff6SAlan Somers 		SDT_PROBE2(fusefs, , device, trace, 1, "no message on thread");
3505fe58019SAttilio Rao 		goto again;
3515fe58019SAttilio Rao 	}
3525fe58019SAttilio Rao 	fuse_lck_mtx_unlock(data->ms_mtx);
3535fe58019SAttilio Rao 
3545fe58019SAttilio Rao 	if (fdata_get_dead(data)) {
3555fe58019SAttilio Rao 		/*
3565fe58019SAttilio Rao 		 * somebody somewhere -- eg., umount routine --
3575fe58019SAttilio Rao 		 * wants this liaison finished off
3585fe58019SAttilio Rao 		 */
359419e7ff6SAlan Somers 		SDT_PROBE2(fusefs, , device, trace, 2,
360419e7ff6SAlan Somers 			"reader is to be sacked");
3615fe58019SAttilio Rao 		if (tick) {
362419e7ff6SAlan Somers 			SDT_PROBE2(fusefs, , device, trace, 2, "weird -- "
363cf169498SAlan Somers 				"\"kick\" is set tho there is message");
3645fe58019SAttilio Rao 			FUSE_ASSERT_MS_DONE(tick);
3655fe58019SAttilio Rao 			fuse_ticket_drop(tick);
3665fe58019SAttilio Rao 		}
3675fe58019SAttilio Rao 		return (ENODEV);	/* This should make the daemon get off
3685fe58019SAttilio Rao 					 * of us */
3695fe58019SAttilio Rao 	}
370419e7ff6SAlan Somers 	SDT_PROBE2(fusefs, , device, trace, 1,
371cf169498SAlan Somers 		"fuse device read message successfully");
3725fe58019SAttilio Rao 
3734f4111d2SAlan Somers 	buf = tick->tk_ms_fiov.base;
3744f4111d2SAlan Somers 	buflen = tick->tk_ms_fiov.len;
3755fe58019SAttilio Rao 
3765fe58019SAttilio Rao 	/*
3775fe58019SAttilio Rao 	 * Why not ban mercilessly stupid daemons who can't keep up
3785fe58019SAttilio Rao 	 * with us? (There is no much use of a partial read here...)
3795fe58019SAttilio Rao 	 */
3805fe58019SAttilio Rao 	/*
3815fe58019SAttilio Rao 	 * XXX note that in such cases Linux FUSE throws EIO at the
3825fe58019SAttilio Rao 	 * syscall invoker and stands back to the message queue. The
3835fe58019SAttilio Rao 	 * rationale should be made clear (and possibly adopt that
3845fe58019SAttilio Rao 	 * behaviour). Keeping the current scheme at least makes
3855fe58019SAttilio Rao 	 * fallacy as loud as possible...
3865fe58019SAttilio Rao 	 */
3874f4111d2SAlan Somers 	if (uio->uio_resid < buflen) {
3885fe58019SAttilio Rao 		fdata_set_dead(data);
389419e7ff6SAlan Somers 		SDT_PROBE2(fusefs, , device, trace, 2,
390cf169498SAlan Somers 		    "daemon is stupid, kick it off...");
3915fe58019SAttilio Rao 		err = ENODEV;
3924f4111d2SAlan Somers 	} else {
3934f4111d2SAlan Somers 		err = uiomove(buf, buflen, uio);
3945fe58019SAttilio Rao 	}
3955fe58019SAttilio Rao 
3965fe58019SAttilio Rao 	FUSE_ASSERT_MS_DONE(tick);
3975fe58019SAttilio Rao 	fuse_ticket_drop(tick);
3985fe58019SAttilio Rao 
3995fe58019SAttilio Rao 	return (err);
4005fe58019SAttilio Rao }
4015fe58019SAttilio Rao 
40202295cafSConrad Meyer static inline int
4035fe58019SAttilio Rao fuse_ohead_audit(struct fuse_out_header *ohead, struct uio *uio)
4045fe58019SAttilio Rao {
4055fe58019SAttilio Rao 	if (uio->uio_resid + sizeof(struct fuse_out_header) != ohead->len) {
406419e7ff6SAlan Somers 		SDT_PROBE2(fusefs, , device, trace, 1,
407419e7ff6SAlan Somers 			"Format error: body size "
408cf169498SAlan Somers 			"differs from size claimed by header");
4095fe58019SAttilio Rao 		return (EINVAL);
4105fe58019SAttilio Rao 	}
411c2d70d6eSAlan Somers 	if (uio->uio_resid && ohead->unique != 0 && ohead->error) {
412419e7ff6SAlan Somers 		SDT_PROBE2(fusefs, , device, trace, 1,
413cf169498SAlan Somers 			"Format error: non zero error but message had a body");
4145fe58019SAttilio Rao 		return (EINVAL);
4155fe58019SAttilio Rao 	}
4165fe58019SAttilio Rao 
4175fe58019SAttilio Rao 	return (0);
4185fe58019SAttilio Rao }
4195fe58019SAttilio Rao 
420c2d70d6eSAlan Somers SDT_PROBE_DEFINE1(fusefs, , device, fuse_device_write_notify,
421c2d70d6eSAlan Somers 	"struct fuse_out_header*");
422419e7ff6SAlan Somers SDT_PROBE_DEFINE1(fusefs, , device, fuse_device_write_missing_ticket,
423419e7ff6SAlan Somers 	"uint64_t");
424419e7ff6SAlan Somers SDT_PROBE_DEFINE1(fusefs, , device, fuse_device_write_found,
425419e7ff6SAlan Somers 	"struct fuse_ticket*");
4265fe58019SAttilio Rao /*
4275fe58019SAttilio Rao  * fuse_device_write first reads the header sent by the daemon.
4285fe58019SAttilio Rao  * If that's OK, looks up ticket/callback node by the unique id seen in header.
4295fe58019SAttilio Rao  * If the callback node contains a handler function, the uio is passed over
4305fe58019SAttilio Rao  * that.
4315fe58019SAttilio Rao  */
4325fe58019SAttilio Rao static int
4335fe58019SAttilio Rao fuse_device_write(struct cdev *dev, struct uio *uio, int ioflag)
4345fe58019SAttilio Rao {
4355fe58019SAttilio Rao 	struct fuse_out_header ohead;
4365fe58019SAttilio Rao 	int err = 0;
4375fe58019SAttilio Rao 	struct fuse_data *data;
438c2d70d6eSAlan Somers 	struct mount *mp;
439a1542146SAlan Somers 	struct fuse_ticket *tick, *itick, *x_tick;
4405fe58019SAttilio Rao 	int found = 0;
4415fe58019SAttilio Rao 
4425fe58019SAttilio Rao 	err = devfs_get_cdevpriv((void **)&data);
4435fe58019SAttilio Rao 	if (err != 0)
4445fe58019SAttilio Rao 		return (err);
445c2d70d6eSAlan Somers 	mp = data->mp;
4465fe58019SAttilio Rao 
4475fe58019SAttilio Rao 	if (uio->uio_resid < sizeof(struct fuse_out_header)) {
448419e7ff6SAlan Somers 		SDT_PROBE2(fusefs, , device, trace, 1,
449cf169498SAlan Somers 			"fuse_device_write got less than a header!");
4505fe58019SAttilio Rao 		fdata_set_dead(data);
4515fe58019SAttilio Rao 		return (EINVAL);
4525fe58019SAttilio Rao 	}
4535fe58019SAttilio Rao 	if ((err = uiomove(&ohead, sizeof(struct fuse_out_header), uio)) != 0)
4545fe58019SAttilio Rao 		return (err);
4555fe58019SAttilio Rao 
456e3b1c847SEdward Tomasz Napierala 	if (data->linux_errnos != 0 && ohead.error != 0) {
457e3b1c847SEdward Tomasz Napierala 		err = -ohead.error;
458e3b1c847SEdward Tomasz Napierala 		if (err < 0 || err >= nitems(linux_to_bsd_errtbl))
459e3b1c847SEdward Tomasz Napierala 			return (EINVAL);
460e3b1c847SEdward Tomasz Napierala 
461e3b1c847SEdward Tomasz Napierala 		/* '-', because it will get flipped again below */
462e3b1c847SEdward Tomasz Napierala 		ohead.error = -linux_to_bsd_errtbl[err];
463e3b1c847SEdward Tomasz Napierala 	}
464e3b1c847SEdward Tomasz Napierala 
4655fe58019SAttilio Rao 	/*
4665fe58019SAttilio Rao 	 * We check header information (which is redundant) and compare it
4675fe58019SAttilio Rao 	 * with what we see. If we see some inconsistency we discard the
4685fe58019SAttilio Rao 	 * whole answer and proceed on as if it had never existed. In
4695fe58019SAttilio Rao 	 * particular, no pretender will be woken up, regardless the
4705fe58019SAttilio Rao 	 * "unique" value in the header.
4715fe58019SAttilio Rao 	 */
4725fe58019SAttilio Rao 	if ((err = fuse_ohead_audit(&ohead, uio))) {
4735fe58019SAttilio Rao 		fdata_set_dead(data);
4745fe58019SAttilio Rao 		return (err);
4755fe58019SAttilio Rao 	}
4765fe58019SAttilio Rao 	/* Pass stuff over to callback if there is one installed */
4775fe58019SAttilio Rao 
4785fe58019SAttilio Rao 	/* Looking for ticket with the unique id of header */
4795fe58019SAttilio Rao 	fuse_lck_mtx_lock(data->aw_mtx);
4805fe58019SAttilio Rao 	TAILQ_FOREACH_SAFE(tick, &data->aw_head, tk_aw_link,
4815fe58019SAttilio Rao 	    x_tick) {
4825fe58019SAttilio Rao 		if (tick->tk_unique == ohead.unique) {
483419e7ff6SAlan Somers 			SDT_PROBE1(fusefs, , device, fuse_device_write_found,
484723c7768SAlan Somers 				tick);
4855fe58019SAttilio Rao 			found = 1;
4865fe58019SAttilio Rao 			fuse_aw_remove(tick);
4875fe58019SAttilio Rao 			break;
4885fe58019SAttilio Rao 		}
4895fe58019SAttilio Rao 	}
490a1542146SAlan Somers 	if (found && tick->irq_unique > 0) {
491a1542146SAlan Somers 		/*
492a1542146SAlan Somers 		 * Discard the FUSE_INTERRUPT ticket that tried to interrupt
493a1542146SAlan Somers 		 * this operation
494a1542146SAlan Somers 		 */
495a1542146SAlan Somers 		TAILQ_FOREACH_SAFE(itick, &data->aw_head, tk_aw_link,
496a1542146SAlan Somers 		    x_tick) {
497a1542146SAlan Somers 			if (itick->tk_unique == tick->irq_unique) {
498a1542146SAlan Somers 				fuse_aw_remove(itick);
499c1afff11SAlan Somers 				fuse_ticket_drop(itick);
500a1542146SAlan Somers 				break;
501a1542146SAlan Somers 			}
502a1542146SAlan Somers 		}
503a1542146SAlan Somers 		tick->irq_unique = 0;
504a1542146SAlan Somers 	}
5055fe58019SAttilio Rao 	fuse_lck_mtx_unlock(data->aw_mtx);
5065fe58019SAttilio Rao 
5075fe58019SAttilio Rao 	if (found) {
5085fe58019SAttilio Rao 		if (tick->tk_aw_handler) {
5095fe58019SAttilio Rao 			/*
5105fe58019SAttilio Rao 			 * We found a callback with proper handler. In this
5115fe58019SAttilio Rao 			 * case the out header will be 0wnd by the callback,
5125fe58019SAttilio Rao 			 * so the fun of freeing that is left for her.
5135fe58019SAttilio Rao 			 * (Then, by all chance, she'll just get that's done
5145fe58019SAttilio Rao 			 * via ticket_drop(), so no manual mucking
5155fe58019SAttilio Rao 			 * around...)
5165fe58019SAttilio Rao 			 */
517419e7ff6SAlan Somers 			SDT_PROBE2(fusefs, , device, trace, 1,
518cf169498SAlan Somers 				"pass ticket to a callback");
519c2d70d6eSAlan Somers 			/* Sanitize the linuxism of negative errnos */
520c2d70d6eSAlan Somers 			ohead.error *= -1;
521*155ac516SAlan Somers 			if (ohead.error < 0 || ohead.error > ELAST) {
522*155ac516SAlan Somers 				/* Illegal error code */
523*155ac516SAlan Somers 				ohead.error = EIO;
524*155ac516SAlan Somers 				memcpy(&tick->tk_aw_ohead, &ohead,
525*155ac516SAlan Somers 					sizeof(ohead));
526*155ac516SAlan Somers 				tick->tk_aw_handler(tick, uio);
527*155ac516SAlan Somers 				err = EINVAL;
528*155ac516SAlan Somers 			} else {
529*155ac516SAlan Somers 				memcpy(&tick->tk_aw_ohead, &ohead,
530*155ac516SAlan Somers 					sizeof(ohead));
5315fe58019SAttilio Rao 				err = tick->tk_aw_handler(tick, uio);
532*155ac516SAlan Somers 			}
5335fe58019SAttilio Rao 		} else {
5345fe58019SAttilio Rao 			/* pretender doesn't wanna do anything with answer */
535419e7ff6SAlan Somers 			SDT_PROBE2(fusefs, , device, trace, 1,
536cf169498SAlan Somers 				"stuff devalidated, so we drop it");
5375fe58019SAttilio Rao 		}
5385fe58019SAttilio Rao 
5395fe58019SAttilio Rao 		/*
5405fe58019SAttilio Rao 		 * As aw_mtx was not held during the callback execution the
5415fe58019SAttilio Rao 		 * ticket may have been inserted again.  However, this is safe
5425fe58019SAttilio Rao 		 * because fuse_ticket_drop() will deal with refcount anyway.
5435fe58019SAttilio Rao 		 */
5445fe58019SAttilio Rao 		fuse_ticket_drop(tick);
545c2d70d6eSAlan Somers 	} else if (ohead.unique == 0){
546c2d70d6eSAlan Somers 		/* unique == 0 means asynchronous notification */
547c2d70d6eSAlan Somers 		SDT_PROBE1(fusefs, , device, fuse_device_write_notify, &ohead);
548c2d70d6eSAlan Somers 		switch (ohead.error) {
549c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
550c2d70d6eSAlan Somers 			err = fuse_internal_invalidate_entry(mp, uio);
551c2d70d6eSAlan Somers 			break;
552c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
553eae1ae13SAlan Somers 			err = fuse_internal_invalidate_inode(mp, uio);
554eae1ae13SAlan Somers 			break;
5557cbb8e8aSAlan Somers 		case FUSE_NOTIFY_RETRIEVE:
5567cbb8e8aSAlan Somers 		case FUSE_NOTIFY_STORE:
5577cbb8e8aSAlan Somers 			/*
5587cbb8e8aSAlan Somers 			 * Unimplemented.  I don't know of any file systems
5597cbb8e8aSAlan Somers 			 * that use them, and the protocol isn't sound anyway,
5607cbb8e8aSAlan Somers 			 * since the notification messages don't include the
5617cbb8e8aSAlan Somers 			 * inode's generation number.  Without that, it's
5627cbb8e8aSAlan Somers 			 * possible to manipulate the cache of the wrong vnode.
5637cbb8e8aSAlan Somers 			 * Finally, it's not defined what this message should
5647cbb8e8aSAlan Somers 			 * do for a file with dirty cache.
5657cbb8e8aSAlan Somers 			 */
566eae1ae13SAlan Somers 		case FUSE_NOTIFY_POLL:
5677cbb8e8aSAlan Somers 			/* Unimplemented.  See comments in fuse_vnops */
568c2d70d6eSAlan Somers 		default:
569c2d70d6eSAlan Somers 			/* Not implemented */
570c2d70d6eSAlan Somers 			err = ENOSYS;
571c2d70d6eSAlan Somers 		}
5725fe58019SAttilio Rao 	} else {
5735fe58019SAttilio Rao 		/* no callback at all! */
574419e7ff6SAlan Somers 		SDT_PROBE1(fusefs, , device, fuse_device_write_missing_ticket,
575723c7768SAlan Somers 			ohead.unique);
576c2d70d6eSAlan Somers 		if (ohead.error == -EAGAIN) {
577a1542146SAlan Somers 			/*
578a1542146SAlan Somers 			 * This was probably a response to a FUSE_INTERRUPT
579a1542146SAlan Somers 			 * operation whose original operation is already
580a1542146SAlan Somers 			 * complete.  We can't store FUSE_INTERRUPT tickets
581a1542146SAlan Somers 			 * indefinitely because their responses are optional.
582a1542146SAlan Somers 			 * So we delete them when the original operation
583a1542146SAlan Somers 			 * completes.  And sadly the fuse_header_out doesn't
584a1542146SAlan Somers 			 * identify the opcode, so we have to guess.
585a1542146SAlan Somers 			 */
586a1542146SAlan Somers 			err = 0;
587a1542146SAlan Somers 		} else {
5885fe58019SAttilio Rao 			err = EINVAL;
5895fe58019SAttilio Rao 		}
590a1542146SAlan Somers 	}
5915fe58019SAttilio Rao 
5925fe58019SAttilio Rao 	return (err);
5935fe58019SAttilio Rao }
5945fe58019SAttilio Rao 
5955fe58019SAttilio Rao int
5965fe58019SAttilio Rao fuse_device_init(void)
5975fe58019SAttilio Rao {
5985fe58019SAttilio Rao 
5995fe58019SAttilio Rao 	fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
600b4227f34SAlan Somers 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, "fuse");
6015fe58019SAttilio Rao 	if (fuse_dev == NULL)
6025fe58019SAttilio Rao 		return (ENOMEM);
6035fe58019SAttilio Rao 	return (0);
6045fe58019SAttilio Rao }
6055fe58019SAttilio Rao 
6065fe58019SAttilio Rao void
6075fe58019SAttilio Rao fuse_device_destroy(void)
6085fe58019SAttilio Rao {
6095fe58019SAttilio Rao 
6105fe58019SAttilio Rao 	MPASS(fuse_dev != NULL);
6115fe58019SAttilio Rao 	destroy_dev(fuse_dev);
6125fe58019SAttilio Rao }
613