xref: /freebsd/sys/fs/fuse/fuse_device.c (revision 02295caf4333432bf3909c35d53ba74d2d8770db)
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  *
365fe58019SAttilio Rao  * Redistribution and use in source and binary forms, with or without
375fe58019SAttilio Rao  * modification, are permitted provided that the following conditions
385fe58019SAttilio Rao  * are met:
395fe58019SAttilio Rao  * 1. Redistributions of source code must retain the above copyright
405fe58019SAttilio Rao  *    notice, this list of conditions and the following disclaimer.
415fe58019SAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
425fe58019SAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
435fe58019SAttilio Rao  *    documentation and/or other materials provided with the distribution.
445fe58019SAttilio Rao  *
455fe58019SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
465fe58019SAttilio Rao  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
475fe58019SAttilio Rao  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
485fe58019SAttilio Rao  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
495fe58019SAttilio Rao  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
505fe58019SAttilio Rao  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
515fe58019SAttilio Rao  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
525fe58019SAttilio Rao  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
535fe58019SAttilio Rao  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
545fe58019SAttilio Rao  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
555fe58019SAttilio Rao  * SUCH DAMAGE.
565fe58019SAttilio Rao  */
575fe58019SAttilio Rao 
585fe58019SAttilio Rao #include <sys/cdefs.h>
595fe58019SAttilio Rao __FBSDID("$FreeBSD$");
605fe58019SAttilio Rao 
615fe58019SAttilio Rao #include <sys/types.h>
625fe58019SAttilio Rao #include <sys/module.h>
635fe58019SAttilio Rao #include <sys/systm.h>
645fe58019SAttilio Rao #include <sys/errno.h>
655fe58019SAttilio Rao #include <sys/param.h>
665fe58019SAttilio Rao #include <sys/kernel.h>
675fe58019SAttilio Rao #include <sys/conf.h>
685fe58019SAttilio Rao #include <sys/uio.h>
695fe58019SAttilio Rao #include <sys/malloc.h>
705fe58019SAttilio Rao #include <sys/queue.h>
715fe58019SAttilio Rao #include <sys/lock.h>
725fe58019SAttilio Rao #include <sys/sx.h>
735fe58019SAttilio Rao #include <sys/mutex.h>
745fe58019SAttilio Rao #include <sys/proc.h>
755fe58019SAttilio Rao #include <sys/mount.h>
765fe58019SAttilio Rao #include <sys/stat.h>
775fe58019SAttilio Rao #include <sys/fcntl.h>
785fe58019SAttilio Rao #include <sys/sysctl.h>
795fe58019SAttilio Rao #include <sys/poll.h>
805fe58019SAttilio Rao #include <sys/selinfo.h>
815fe58019SAttilio Rao 
825fe58019SAttilio Rao #include "fuse.h"
835fe58019SAttilio Rao #include "fuse_ipc.h"
845fe58019SAttilio Rao 
855fe58019SAttilio Rao #define FUSE_DEBUG_MODULE DEVICE
865fe58019SAttilio Rao #include "fuse_debug.h"
875fe58019SAttilio Rao 
885fe58019SAttilio Rao static struct cdev *fuse_dev;
895fe58019SAttilio Rao 
905fe58019SAttilio Rao static d_open_t fuse_device_open;
915fe58019SAttilio Rao static d_close_t fuse_device_close;
925fe58019SAttilio Rao static d_poll_t fuse_device_poll;
935fe58019SAttilio Rao static d_read_t fuse_device_read;
945fe58019SAttilio Rao static d_write_t fuse_device_write;
955fe58019SAttilio Rao 
965fe58019SAttilio Rao static struct cdevsw fuse_device_cdevsw = {
975fe58019SAttilio Rao 	.d_open = fuse_device_open,
985fe58019SAttilio Rao 	.d_close = fuse_device_close,
995fe58019SAttilio Rao 	.d_name = "fuse",
1005fe58019SAttilio Rao 	.d_poll = fuse_device_poll,
1015fe58019SAttilio Rao 	.d_read = fuse_device_read,
1025fe58019SAttilio Rao 	.d_write = fuse_device_write,
1035fe58019SAttilio Rao 	.d_version = D_VERSION,
1045fe58019SAttilio Rao };
1055fe58019SAttilio Rao 
1065fe58019SAttilio Rao /****************************
1075fe58019SAttilio Rao  *
1085fe58019SAttilio Rao  * >>> Fuse device op defs
1095fe58019SAttilio Rao  *
1105fe58019SAttilio Rao  ****************************/
1115fe58019SAttilio Rao 
1125fe58019SAttilio Rao static void
1135fe58019SAttilio Rao fdata_dtor(void *arg)
1145fe58019SAttilio Rao {
1155fe58019SAttilio Rao 	struct fuse_data *fdata;
1165fe58019SAttilio Rao 
1175fe58019SAttilio Rao 	fdata = arg;
1185fe58019SAttilio Rao 	fdata_trydestroy(fdata);
1195fe58019SAttilio Rao }
1205fe58019SAttilio Rao 
1215fe58019SAttilio Rao /*
1225fe58019SAttilio Rao  * Resources are set up on a per-open basis
1235fe58019SAttilio Rao  */
1245fe58019SAttilio Rao static int
1255fe58019SAttilio Rao fuse_device_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
1265fe58019SAttilio Rao {
1275fe58019SAttilio Rao 	struct fuse_data *fdata;
1285fe58019SAttilio Rao 	int error;
1295fe58019SAttilio Rao 
1304cff153bSAttilio Rao 	FS_DEBUG("device %p\n", dev);
1315fe58019SAttilio Rao 
1325fe58019SAttilio Rao 	fdata = fdata_alloc(dev, td->td_ucred);
1335fe58019SAttilio Rao 	error = devfs_set_cdevpriv(fdata, fdata_dtor);
1345fe58019SAttilio Rao 	if (error != 0)
1355fe58019SAttilio Rao 		fdata_trydestroy(fdata);
1365fe58019SAttilio Rao 	else
1374cff153bSAttilio Rao 		FS_DEBUG("%s: device opened by thread %d.\n", dev->si_name,
1385fe58019SAttilio Rao 		    td->td_tid);
1395fe58019SAttilio Rao 	return (error);
1405fe58019SAttilio Rao }
1415fe58019SAttilio Rao 
1425fe58019SAttilio Rao static int
1435fe58019SAttilio Rao fuse_device_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
1445fe58019SAttilio Rao {
1455fe58019SAttilio Rao 	struct fuse_data *data;
1465fe58019SAttilio Rao 	struct fuse_ticket *tick;
1475fe58019SAttilio Rao 	int error;
1485fe58019SAttilio Rao 
1495fe58019SAttilio Rao 	error = devfs_get_cdevpriv((void **)&data);
1505fe58019SAttilio Rao 	if (error != 0)
1515fe58019SAttilio Rao 		return (error);
1525fe58019SAttilio Rao 	if (!data)
1535fe58019SAttilio Rao 		panic("no fuse data upon fuse device close");
1545fe58019SAttilio Rao 	fdata_set_dead(data);
1555fe58019SAttilio Rao 
1565fe58019SAttilio Rao 	FUSE_LOCK();
1575fe58019SAttilio Rao 	fuse_lck_mtx_lock(data->aw_mtx);
1585fe58019SAttilio Rao 	/* wakup poll()ers */
1595fe58019SAttilio Rao 	selwakeuppri(&data->ks_rsel, PZERO + 1);
1605fe58019SAttilio Rao 	/* Don't let syscall handlers wait in vain */
1615fe58019SAttilio Rao 	while ((tick = fuse_aw_pop(data))) {
1625fe58019SAttilio Rao 		fuse_lck_mtx_lock(tick->tk_aw_mtx);
1635fe58019SAttilio Rao 		fticket_set_answered(tick);
1645fe58019SAttilio Rao 		tick->tk_aw_errno = ENOTCONN;
1655fe58019SAttilio Rao 		wakeup(tick);
1665fe58019SAttilio Rao 		fuse_lck_mtx_unlock(tick->tk_aw_mtx);
1675fe58019SAttilio Rao 		FUSE_ASSERT_AW_DONE(tick);
1685fe58019SAttilio Rao 		fuse_ticket_drop(tick);
1695fe58019SAttilio Rao 	}
1705fe58019SAttilio Rao 	fuse_lck_mtx_unlock(data->aw_mtx);
1715fe58019SAttilio Rao 	FUSE_UNLOCK();
1725fe58019SAttilio Rao 
1734cff153bSAttilio Rao 	FS_DEBUG("%s: device closed by thread %d.\n", dev->si_name, td->td_tid);
1745fe58019SAttilio Rao 	return (0);
1755fe58019SAttilio Rao }
1765fe58019SAttilio Rao 
1775fe58019SAttilio Rao int
1785fe58019SAttilio Rao fuse_device_poll(struct cdev *dev, int events, struct thread *td)
1795fe58019SAttilio Rao {
1805fe58019SAttilio Rao 	struct fuse_data *data;
1815fe58019SAttilio Rao 	int error, revents = 0;
1825fe58019SAttilio Rao 
1835fe58019SAttilio Rao 	error = devfs_get_cdevpriv((void **)&data);
1845fe58019SAttilio Rao 	if (error != 0)
1855fe58019SAttilio Rao 		return (events &
1865fe58019SAttilio Rao 		    (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
1875fe58019SAttilio Rao 
1885fe58019SAttilio Rao 	if (events & (POLLIN | POLLRDNORM)) {
1895fe58019SAttilio Rao 		fuse_lck_mtx_lock(data->ms_mtx);
1905fe58019SAttilio Rao 		if (fdata_get_dead(data) || STAILQ_FIRST(&data->ms_head))
1915fe58019SAttilio Rao 			revents |= events & (POLLIN | POLLRDNORM);
1925fe58019SAttilio Rao 		else
1935fe58019SAttilio Rao 			selrecord(td, &data->ks_rsel);
1945fe58019SAttilio Rao 		fuse_lck_mtx_unlock(data->ms_mtx);
1955fe58019SAttilio Rao 	}
1965fe58019SAttilio Rao 	if (events & (POLLOUT | POLLWRNORM)) {
1975fe58019SAttilio Rao 		revents |= events & (POLLOUT | POLLWRNORM);
1985fe58019SAttilio Rao 	}
1995fe58019SAttilio Rao 	return (revents);
2005fe58019SAttilio Rao }
2015fe58019SAttilio Rao 
2025fe58019SAttilio Rao /*
2035fe58019SAttilio Rao  * fuse_device_read hangs on the queue of VFS messages.
2045fe58019SAttilio Rao  * When it's notified that there is a new one, it picks that and
2055fe58019SAttilio Rao  * passes up to the daemon
2065fe58019SAttilio Rao  */
2075fe58019SAttilio Rao int
2085fe58019SAttilio Rao fuse_device_read(struct cdev *dev, struct uio *uio, int ioflag)
2095fe58019SAttilio Rao {
2105fe58019SAttilio Rao 	int err;
2115fe58019SAttilio Rao 	struct fuse_data *data;
2125fe58019SAttilio Rao 	struct fuse_ticket *tick;
2135fe58019SAttilio Rao 	void *buf[] = {NULL, NULL, NULL};
2145fe58019SAttilio Rao 	int buflen[3];
2155fe58019SAttilio Rao 	int i;
2165fe58019SAttilio Rao 
2174cff153bSAttilio Rao 	FS_DEBUG("fuse device being read on thread %d\n", uio->uio_td->td_tid);
2185fe58019SAttilio Rao 
2195fe58019SAttilio Rao 	err = devfs_get_cdevpriv((void **)&data);
2205fe58019SAttilio Rao 	if (err != 0)
2215fe58019SAttilio Rao 		return (err);
2225fe58019SAttilio Rao 
2235fe58019SAttilio Rao 	fuse_lck_mtx_lock(data->ms_mtx);
2245fe58019SAttilio Rao again:
2255fe58019SAttilio Rao 	if (fdata_get_dead(data)) {
2264cff153bSAttilio Rao 		FS_DEBUG2G("we know early on that reader should be kicked so we don't wait for news\n");
2275fe58019SAttilio Rao 		fuse_lck_mtx_unlock(data->ms_mtx);
2285fe58019SAttilio Rao 		return (ENODEV);
2295fe58019SAttilio Rao 	}
2305fe58019SAttilio Rao 	if (!(tick = fuse_ms_pop(data))) {
2315fe58019SAttilio Rao 		/* check if we may block */
2325fe58019SAttilio Rao 		if (ioflag & O_NONBLOCK) {
2335fe58019SAttilio Rao 			/* get outa here soon */
2345fe58019SAttilio Rao 			fuse_lck_mtx_unlock(data->ms_mtx);
2355fe58019SAttilio Rao 			return (EAGAIN);
2365fe58019SAttilio Rao 		} else {
2375fe58019SAttilio Rao 			err = msleep(data, &data->ms_mtx, PCATCH, "fu_msg", 0);
2385fe58019SAttilio Rao 			if (err != 0) {
2395fe58019SAttilio Rao 				fuse_lck_mtx_unlock(data->ms_mtx);
2405fe58019SAttilio Rao 				return (fdata_get_dead(data) ? ENODEV : err);
2415fe58019SAttilio Rao 			}
2425fe58019SAttilio Rao 			tick = fuse_ms_pop(data);
2435fe58019SAttilio Rao 		}
2445fe58019SAttilio Rao 	}
2455fe58019SAttilio Rao 	if (!tick) {
2465fe58019SAttilio Rao 		/*
2475fe58019SAttilio Rao 		 * We can get here if fuse daemon suddenly terminates,
2485fe58019SAttilio Rao 		 * eg, by being hit by a SIGKILL
2495fe58019SAttilio Rao 		 * -- and some other cases, too, tho not totally clear, when
2505fe58019SAttilio Rao 		 * (cv_signal/wakeup_one signals the whole process ?)
2515fe58019SAttilio Rao 		 */
2524cff153bSAttilio Rao 		FS_DEBUG("no message on thread #%d\n", uio->uio_td->td_tid);
2535fe58019SAttilio Rao 		goto again;
2545fe58019SAttilio Rao 	}
2555fe58019SAttilio Rao 	fuse_lck_mtx_unlock(data->ms_mtx);
2565fe58019SAttilio Rao 
2575fe58019SAttilio Rao 	if (fdata_get_dead(data)) {
2585fe58019SAttilio Rao 		/*
2595fe58019SAttilio Rao 		 * somebody somewhere -- eg., umount routine --
2605fe58019SAttilio Rao 		 * wants this liaison finished off
2615fe58019SAttilio Rao 		 */
2624cff153bSAttilio Rao 		FS_DEBUG2G("reader is to be sacked\n");
2635fe58019SAttilio Rao 		if (tick) {
2644cff153bSAttilio Rao 			FS_DEBUG2G("weird -- \"kick\" is set tho there is message\n");
2655fe58019SAttilio Rao 			FUSE_ASSERT_MS_DONE(tick);
2665fe58019SAttilio Rao 			fuse_ticket_drop(tick);
2675fe58019SAttilio Rao 		}
2685fe58019SAttilio Rao 		return (ENODEV);	/* This should make the daemon get off
2695fe58019SAttilio Rao 					 * of us */
2705fe58019SAttilio Rao 	}
2714cff153bSAttilio Rao 	FS_DEBUG("message got on thread #%d\n", uio->uio_td->td_tid);
2725fe58019SAttilio Rao 
2735fe58019SAttilio Rao 	KASSERT(tick->tk_ms_bufdata || tick->tk_ms_bufsize == 0,
2745fe58019SAttilio Rao 	    ("non-null buf pointer with positive size"));
2755fe58019SAttilio Rao 
2765fe58019SAttilio Rao 	switch (tick->tk_ms_type) {
2775fe58019SAttilio Rao 	case FT_M_FIOV:
2785fe58019SAttilio Rao 		buf[0] = tick->tk_ms_fiov.base;
2795fe58019SAttilio Rao 		buflen[0] = tick->tk_ms_fiov.len;
2805fe58019SAttilio Rao 		break;
2815fe58019SAttilio Rao 	case FT_M_BUF:
2825fe58019SAttilio Rao 		buf[0] = tick->tk_ms_fiov.base;
2835fe58019SAttilio Rao 		buflen[0] = tick->tk_ms_fiov.len;
2845fe58019SAttilio Rao 		buf[1] = tick->tk_ms_bufdata;
2855fe58019SAttilio Rao 		buflen[1] = tick->tk_ms_bufsize;
2865fe58019SAttilio Rao 		break;
2875fe58019SAttilio Rao 	default:
2885fe58019SAttilio Rao 		panic("unknown message type for fuse_ticket %p", tick);
2895fe58019SAttilio Rao 	}
2905fe58019SAttilio Rao 
2915fe58019SAttilio Rao 	for (i = 0; buf[i]; i++) {
2925fe58019SAttilio Rao 		/*
2935fe58019SAttilio Rao 		 * Why not ban mercilessly stupid daemons who can't keep up
2945fe58019SAttilio Rao 		 * with us? (There is no much use of a partial read here...)
2955fe58019SAttilio Rao 		 */
2965fe58019SAttilio Rao 		/*
2975fe58019SAttilio Rao 		 * XXX note that in such cases Linux FUSE throws EIO at the
2985fe58019SAttilio Rao 		 * syscall invoker and stands back to the message queue. The
2995fe58019SAttilio Rao 		 * rationale should be made clear (and possibly adopt that
3005fe58019SAttilio Rao 		 * behaviour). Keeping the current scheme at least makes
3015fe58019SAttilio Rao 		 * fallacy as loud as possible...
3025fe58019SAttilio Rao 		 */
3035fe58019SAttilio Rao 		if (uio->uio_resid < buflen[i]) {
3045fe58019SAttilio Rao 			fdata_set_dead(data);
3054cff153bSAttilio Rao 			FS_DEBUG2G("daemon is stupid, kick it off...\n");
3065fe58019SAttilio Rao 			err = ENODEV;
3075fe58019SAttilio Rao 			break;
3085fe58019SAttilio Rao 		}
3095fe58019SAttilio Rao 		err = uiomove(buf[i], buflen[i], uio);
3105fe58019SAttilio Rao 		if (err)
3115fe58019SAttilio Rao 			break;
3125fe58019SAttilio Rao 	}
3135fe58019SAttilio Rao 
3145fe58019SAttilio Rao 	FUSE_ASSERT_MS_DONE(tick);
3155fe58019SAttilio Rao 	fuse_ticket_drop(tick);
3165fe58019SAttilio Rao 
3175fe58019SAttilio Rao 	return (err);
3185fe58019SAttilio Rao }
3195fe58019SAttilio Rao 
320*02295cafSConrad Meyer static inline int
3215fe58019SAttilio Rao fuse_ohead_audit(struct fuse_out_header *ohead, struct uio *uio)
3225fe58019SAttilio Rao {
3234cff153bSAttilio Rao 	FS_DEBUG("Out header -- len: %i, error: %i, unique: %llu; iovecs: %d\n",
3245fe58019SAttilio Rao 	    ohead->len, ohead->error, (unsigned long long)ohead->unique,
3255fe58019SAttilio Rao 	    uio->uio_iovcnt);
3265fe58019SAttilio Rao 
3275fe58019SAttilio Rao 	if (uio->uio_resid + sizeof(struct fuse_out_header) != ohead->len) {
3284cff153bSAttilio Rao 		FS_DEBUG("Format error: body size differs from size claimed by header\n");
3295fe58019SAttilio Rao 		return (EINVAL);
3305fe58019SAttilio Rao 	}
3315fe58019SAttilio Rao 	if (uio->uio_resid && ohead->error) {
3324cff153bSAttilio Rao 		FS_DEBUG("Format error: non zero error but message had a body\n");
3335fe58019SAttilio Rao 		return (EINVAL);
3345fe58019SAttilio Rao 	}
3355fe58019SAttilio Rao 	/* Sanitize the linuxism of negative errnos */
3365fe58019SAttilio Rao 	ohead->error = -(ohead->error);
3375fe58019SAttilio Rao 
3385fe58019SAttilio Rao 	return (0);
3395fe58019SAttilio Rao }
3405fe58019SAttilio Rao 
3415fe58019SAttilio Rao /*
3425fe58019SAttilio Rao  * fuse_device_write first reads the header sent by the daemon.
3435fe58019SAttilio Rao  * If that's OK, looks up ticket/callback node by the unique id seen in header.
3445fe58019SAttilio Rao  * If the callback node contains a handler function, the uio is passed over
3455fe58019SAttilio Rao  * that.
3465fe58019SAttilio Rao  */
3475fe58019SAttilio Rao static int
3485fe58019SAttilio Rao fuse_device_write(struct cdev *dev, struct uio *uio, int ioflag)
3495fe58019SAttilio Rao {
3505fe58019SAttilio Rao 	struct fuse_out_header ohead;
3515fe58019SAttilio Rao 	int err = 0;
3525fe58019SAttilio Rao 	struct fuse_data *data;
3535fe58019SAttilio Rao 	struct fuse_ticket *tick, *x_tick;
3545fe58019SAttilio Rao 	int found = 0;
3555fe58019SAttilio Rao 
3564cff153bSAttilio Rao 	FS_DEBUG("resid: %zd, iovcnt: %d, thread: %d\n",
3575fe58019SAttilio Rao 	    uio->uio_resid, uio->uio_iovcnt, uio->uio_td->td_tid);
3585fe58019SAttilio Rao 
3595fe58019SAttilio Rao 	err = devfs_get_cdevpriv((void **)&data);
3605fe58019SAttilio Rao 	if (err != 0)
3615fe58019SAttilio Rao 		return (err);
3625fe58019SAttilio Rao 
3635fe58019SAttilio Rao 	if (uio->uio_resid < sizeof(struct fuse_out_header)) {
3644cff153bSAttilio Rao 		FS_DEBUG("got less than a header!\n");
3655fe58019SAttilio Rao 		fdata_set_dead(data);
3665fe58019SAttilio Rao 		return (EINVAL);
3675fe58019SAttilio Rao 	}
3685fe58019SAttilio Rao 	if ((err = uiomove(&ohead, sizeof(struct fuse_out_header), uio)) != 0)
3695fe58019SAttilio Rao 		return (err);
3705fe58019SAttilio Rao 
3715fe58019SAttilio Rao 	/*
3725fe58019SAttilio Rao 	 * We check header information (which is redundant) and compare it
3735fe58019SAttilio Rao 	 * with what we see. If we see some inconsistency we discard the
3745fe58019SAttilio Rao 	 * whole answer and proceed on as if it had never existed. In
3755fe58019SAttilio Rao 	 * particular, no pretender will be woken up, regardless the
3765fe58019SAttilio Rao 	 * "unique" value in the header.
3775fe58019SAttilio Rao 	 */
3785fe58019SAttilio Rao 	if ((err = fuse_ohead_audit(&ohead, uio))) {
3795fe58019SAttilio Rao 		fdata_set_dead(data);
3805fe58019SAttilio Rao 		return (err);
3815fe58019SAttilio Rao 	}
3825fe58019SAttilio Rao 	/* Pass stuff over to callback if there is one installed */
3835fe58019SAttilio Rao 
3845fe58019SAttilio Rao 	/* Looking for ticket with the unique id of header */
3855fe58019SAttilio Rao 	fuse_lck_mtx_lock(data->aw_mtx);
3865fe58019SAttilio Rao 	TAILQ_FOREACH_SAFE(tick, &data->aw_head, tk_aw_link,
3875fe58019SAttilio Rao 	    x_tick) {
3884cff153bSAttilio Rao 		FS_DEBUG("bumped into callback #%llu\n",
3895fe58019SAttilio Rao 		    (unsigned long long)tick->tk_unique);
3905fe58019SAttilio Rao 		if (tick->tk_unique == ohead.unique) {
3915fe58019SAttilio Rao 			found = 1;
3925fe58019SAttilio Rao 			fuse_aw_remove(tick);
3935fe58019SAttilio Rao 			break;
3945fe58019SAttilio Rao 		}
3955fe58019SAttilio Rao 	}
3965fe58019SAttilio Rao 	fuse_lck_mtx_unlock(data->aw_mtx);
3975fe58019SAttilio Rao 
3985fe58019SAttilio Rao 	if (found) {
3995fe58019SAttilio Rao 		if (tick->tk_aw_handler) {
4005fe58019SAttilio Rao 			/*
4015fe58019SAttilio Rao 			 * We found a callback with proper handler. In this
4025fe58019SAttilio Rao 			 * case the out header will be 0wnd by the callback,
4035fe58019SAttilio Rao 			 * so the fun of freeing that is left for her.
4045fe58019SAttilio Rao 			 * (Then, by all chance, she'll just get that's done
4055fe58019SAttilio Rao 			 * via ticket_drop(), so no manual mucking
4065fe58019SAttilio Rao 			 * around...)
4075fe58019SAttilio Rao 			 */
4084cff153bSAttilio Rao 			FS_DEBUG("pass ticket to a callback\n");
4095fe58019SAttilio Rao 			memcpy(&tick->tk_aw_ohead, &ohead, sizeof(ohead));
4105fe58019SAttilio Rao 			err = tick->tk_aw_handler(tick, uio);
4115fe58019SAttilio Rao 		} else {
4125fe58019SAttilio Rao 			/* pretender doesn't wanna do anything with answer */
4134cff153bSAttilio Rao 			FS_DEBUG("stuff devalidated, so we drop it\n");
4145fe58019SAttilio Rao 		}
4155fe58019SAttilio Rao 
4165fe58019SAttilio Rao 		/*
4175fe58019SAttilio Rao 		 * As aw_mtx was not held during the callback execution the
4185fe58019SAttilio Rao 		 * ticket may have been inserted again.  However, this is safe
4195fe58019SAttilio Rao 		 * because fuse_ticket_drop() will deal with refcount anyway.
4205fe58019SAttilio Rao 		 */
4215fe58019SAttilio Rao 		fuse_ticket_drop(tick);
4225fe58019SAttilio Rao 	} else {
4235fe58019SAttilio Rao 		/* no callback at all! */
4244cff153bSAttilio Rao 		FS_DEBUG("erhm, no handler for this response\n");
4255fe58019SAttilio Rao 		err = EINVAL;
4265fe58019SAttilio Rao 	}
4275fe58019SAttilio Rao 
4285fe58019SAttilio Rao 	return (err);
4295fe58019SAttilio Rao }
4305fe58019SAttilio Rao 
4315fe58019SAttilio Rao int
4325fe58019SAttilio Rao fuse_device_init(void)
4335fe58019SAttilio Rao {
4345fe58019SAttilio Rao 
4355fe58019SAttilio Rao 	fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
4365fe58019SAttilio Rao 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse");
4375fe58019SAttilio Rao 	if (fuse_dev == NULL)
4385fe58019SAttilio Rao 		return (ENOMEM);
4395fe58019SAttilio Rao 	return (0);
4405fe58019SAttilio Rao }
4415fe58019SAttilio Rao 
4425fe58019SAttilio Rao void
4435fe58019SAttilio Rao fuse_device_destroy(void)
4445fe58019SAttilio Rao {
4455fe58019SAttilio Rao 
4465fe58019SAttilio Rao 	MPASS(fuse_dev != NULL);
4475fe58019SAttilio Rao 	destroy_dev(fuse_dev);
4485fe58019SAttilio Rao }
449