xref: /openbsd-src/sys/miscfs/fuse/fuse_device.c (revision e10a268edd841334ce53dd5a87213dbd7e3b4baa)
1*e10a268eSmvs /* $OpenBSD: fuse_device.c,v 1.40 2023/12/16 22:17:08 mvs Exp $ */
2a2badd06Stedu /*
3a2badd06Stedu  * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
4a2badd06Stedu  *
5a2badd06Stedu  * Permission to use, copy, modify, and distribute this software for any
6a2badd06Stedu  * purpose with or without fee is hereby granted, provided that the above
7a2badd06Stedu  * copyright notice and this permission notice appear in all copies.
8a2badd06Stedu  *
9a2badd06Stedu  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10a2badd06Stedu  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11a2badd06Stedu  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12a2badd06Stedu  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13a2badd06Stedu  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14a2badd06Stedu  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15a2badd06Stedu  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16a2badd06Stedu  */
17a2badd06Stedu 
18a2badd06Stedu #include <sys/param.h>
194bc91defSmpi #include <sys/systm.h>
20a2badd06Stedu #include <sys/fcntl.h>
2192f58b66Ssyl #include <sys/ioctl.h>
22*e10a268eSmvs #include <sys/event.h>
23a2badd06Stedu #include <sys/malloc.h>
24b52e1426Ssyl #include <sys/mount.h>
25*e10a268eSmvs #include <sys/rwlock.h>
266f9b5942Snatano #include <sys/stat.h>
27a2badd06Stedu #include <sys/statvfs.h>
28a2badd06Stedu #include <sys/vnode.h>
29a2badd06Stedu #include <sys/fusebuf.h>
30a2badd06Stedu 
31a2badd06Stedu #include "fusefs_node.h"
32a2badd06Stedu #include "fusefs.h"
33a2badd06Stedu 
34a2badd06Stedu #ifdef	FUSE_DEBUG
35f76a189dSsyl #define	DPRINTF(fmt, arg...)	printf("%s: " fmt, "fuse", ##arg)
36a2badd06Stedu #else
37a2badd06Stedu #define	DPRINTF(fmt, arg...)
38a2badd06Stedu #endif
39a2badd06Stedu 
40*e10a268eSmvs /*
41*e10a268eSmvs  * Locks used to protect struct members and global data
42*e10a268eSmvs  *	l	fd_lock
43*e10a268eSmvs  */
44*e10a268eSmvs 
45a2badd06Stedu SIMPLEQ_HEAD(fusebuf_head, fusebuf);
46a2badd06Stedu 
47f76a189dSsyl struct fuse_d {
48*e10a268eSmvs 	struct rwlock fd_lock;
49f76a189dSsyl 	struct fusefs_mnt *fd_fmp;
50f76a189dSsyl 	int fd_unit;
51a2badd06Stedu 
52f76a189dSsyl 	/*fusebufs queues*/
53*e10a268eSmvs 	struct fusebuf_head fd_fbufs_in;	/* [l] */
54f76a189dSsyl 	struct fusebuf_head fd_fbufs_wait;
55a2badd06Stedu 
56a2badd06Stedu 	/* kq fields */
57*e10a268eSmvs 	struct klist fd_rklist;			/* [l] */
58f76a189dSsyl 	LIST_ENTRY(fuse_d) fd_list;
59a2badd06Stedu };
60a2badd06Stedu 
61a2badd06Stedu int stat_fbufs_in = 0;
62a2badd06Stedu int stat_fbufs_wait = 0;
63a2badd06Stedu int stat_opened_fusedev = 0;
64a2badd06Stedu 
65f76a189dSsyl LIST_HEAD(, fuse_d) fuse_d_list;
66f76a189dSsyl struct fuse_d *fuse_lookup(int);
67f76a189dSsyl 
68a2badd06Stedu void	fuseattach(int);
69a2badd06Stedu int	fuseopen(dev_t, int, int, struct proc *);
70a2badd06Stedu int	fuseclose(dev_t, int, int, struct proc *);
71a2badd06Stedu int	fuseioctl(dev_t, u_long, caddr_t, int, struct proc *);
72a2badd06Stedu int	fuseread(dev_t, struct uio *, int);
73a2badd06Stedu int	fusewrite(dev_t, struct uio *, int);
74a2badd06Stedu int	fusekqfilter(dev_t dev, struct knote *kn);
75a2badd06Stedu int	filt_fuse_read(struct knote *, long);
76a2badd06Stedu void	filt_fuse_rdetach(struct knote *);
77*e10a268eSmvs int	filt_fuse_modify(struct kevent *, struct knote *);
78*e10a268eSmvs int	filt_fuse_process(struct knote *, struct kevent *);
79a2badd06Stedu 
808f47c760Sjsg static const struct filterops fuse_rd_filtops = {
81*e10a268eSmvs 	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
8294321eb4Svisa 	.f_attach	= NULL,
8394321eb4Svisa 	.f_detach	= filt_fuse_rdetach,
8494321eb4Svisa 	.f_event	= filt_fuse_read,
85*e10a268eSmvs 	.f_modify	= filt_fuse_modify,
86*e10a268eSmvs 	.f_process	= filt_fuse_process,
87a2badd06Stedu };
88a2badd06Stedu 
89a2badd06Stedu #ifdef FUSE_DEBUG
90a2badd06Stedu static void
fuse_dump_buff(char * buff,int len)91a2badd06Stedu fuse_dump_buff(char *buff, int len)
92a2badd06Stedu {
93a2badd06Stedu 	char text[17];
94a2badd06Stedu 	int i;
95a2badd06Stedu 
96c37506c6Shelg 	if (len < 0) {
97c37506c6Shelg 		printf("invalid len: %d", len);
98c37506c6Shelg 		return;
99c37506c6Shelg 	}
100c37506c6Shelg 	if (buff == NULL) {
101c37506c6Shelg 		printf("invalid buff");
102c37506c6Shelg 		return;
103c37506c6Shelg 	}
104c37506c6Shelg 
105bb72b40bShelg 	memset(text, 0, 17);
106a2badd06Stedu 	for (i = 0; i < len; i++) {
107a2badd06Stedu 		if (i != 0 && (i % 16) == 0) {
108a2badd06Stedu 			printf(": %s\n", text);
109bb72b40bShelg 			memset(text, 0, 17);
110a2badd06Stedu 		}
111a2badd06Stedu 
112a2badd06Stedu 		printf("%.2x ", buff[i] & 0xff);
113a2badd06Stedu 
114a2badd06Stedu 		if (buff[i] > ' ' && buff[i] < '~')
115a2badd06Stedu 			text[i%16] = buff[i] & 0xff;
116a2badd06Stedu 		else
117a2badd06Stedu 			text[i%16] = '.';
118a2badd06Stedu 	}
119a2badd06Stedu 
120a2badd06Stedu 	if ((i % 16) != 0)
121a2badd06Stedu 		while ((i % 16) != 0) {
122a2badd06Stedu 			printf("   ");
123a2badd06Stedu 			i++;
124a2badd06Stedu 		}
125a2badd06Stedu 
126a2badd06Stedu 	printf(": %s\n", text);
127a2badd06Stedu }
128a2badd06Stedu #endif
129a2badd06Stedu 
130f76a189dSsyl struct fuse_d *
fuse_lookup(int unit)131f76a189dSsyl fuse_lookup(int unit)
132f76a189dSsyl {
133f76a189dSsyl 	struct fuse_d *fd;
134f76a189dSsyl 
135f76a189dSsyl 	LIST_FOREACH(fd, &fuse_d_list, fd_list)
136f76a189dSsyl 		if (fd->fd_unit == unit)
137f76a189dSsyl 			return (fd);
138f76a189dSsyl 	return (NULL);
139f76a189dSsyl }
140f76a189dSsyl 
141a2badd06Stedu /*
142b7e69da4Shelg  * Cleanup all msgs from sc_fbufs_in and sc_fbufs_wait.
143a2badd06Stedu  */
144a2badd06Stedu void
fuse_device_cleanup(dev_t dev)145b7e69da4Shelg fuse_device_cleanup(dev_t dev)
146a2badd06Stedu {
147f76a189dSsyl 	struct fuse_d *fd;
14820bf07e8Ssyl 	struct fusebuf *f, *ftmp, *lprev;
149a2badd06Stedu 
150f76a189dSsyl 	fd = fuse_lookup(minor(dev));
151f76a189dSsyl 	if (fd == NULL)
152a2badd06Stedu 		return;
153a2badd06Stedu 
154a2badd06Stedu 	/* clear FIFO IN */
15520bf07e8Ssyl 	lprev = NULL;
156*e10a268eSmvs 	rw_enter_write(&fd->fd_lock);
15720bf07e8Ssyl 	SIMPLEQ_FOREACH_SAFE(f, &fd->fd_fbufs_in, fb_next, ftmp) {
158a2badd06Stedu 		DPRINTF("cleanup unprocessed msg in sc_fbufs_in\n");
15920bf07e8Ssyl 		if (lprev == NULL)
160f76a189dSsyl 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
16120bf07e8Ssyl 		else
16220bf07e8Ssyl 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_in, lprev,
16320bf07e8Ssyl 			    fb_next);
16420bf07e8Ssyl 
165a2badd06Stedu 		stat_fbufs_in--;
16613907cdeShelg 		f->fb_err = ENXIO;
16713907cdeShelg 		wakeup(f);
16820bf07e8Ssyl 		lprev = f;
169a2badd06Stedu 	}
170*e10a268eSmvs 	rw_exit_write(&fd->fd_lock);
171a2badd06Stedu 
172a2badd06Stedu 	/* clear FIFO WAIT*/
17320bf07e8Ssyl 	lprev = NULL;
17420bf07e8Ssyl 	SIMPLEQ_FOREACH_SAFE(f, &fd->fd_fbufs_wait, fb_next, ftmp) {
175a2badd06Stedu 		DPRINTF("umount unprocessed msg in sc_fbufs_wait\n");
17620bf07e8Ssyl 		if (lprev == NULL)
177b7e69da4Shelg 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
17820bf07e8Ssyl 		else
17920bf07e8Ssyl 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lprev,
18020bf07e8Ssyl 			    fb_next);
18120bf07e8Ssyl 
182a2badd06Stedu 		stat_fbufs_wait--;
18313907cdeShelg 		f->fb_err = ENXIO;
18413907cdeShelg 		wakeup(f);
18520bf07e8Ssyl 		lprev = f;
186a2badd06Stedu 	}
187a2badd06Stedu }
188a2badd06Stedu 
189a2badd06Stedu void
fuse_device_queue_fbuf(dev_t dev,struct fusebuf * fbuf)190a2badd06Stedu fuse_device_queue_fbuf(dev_t dev, struct fusebuf *fbuf)
191a2badd06Stedu {
192f76a189dSsyl 	struct fuse_d *fd;
193a2badd06Stedu 
194f76a189dSsyl 	fd = fuse_lookup(minor(dev));
195f76a189dSsyl 	if (fd == NULL)
196a2badd06Stedu 		return;
197a2badd06Stedu 
198*e10a268eSmvs 	rw_enter_write(&fd->fd_lock);
199f76a189dSsyl 	SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_in, fbuf, fb_next);
200*e10a268eSmvs 	knote_locked(&fd->fd_rklist, 0);
201*e10a268eSmvs 	rw_exit_write(&fd->fd_lock);
202a2badd06Stedu 	stat_fbufs_in++;
203a2badd06Stedu }
204a2badd06Stedu 
205a2badd06Stedu void
fuse_device_set_fmp(struct fusefs_mnt * fmp,int set)20648ee6ddaSpelikan fuse_device_set_fmp(struct fusefs_mnt *fmp, int set)
207a2badd06Stedu {
208f76a189dSsyl 	struct fuse_d *fd;
209a2badd06Stedu 
210f76a189dSsyl 	fd = fuse_lookup(minor(fmp->dev));
211f76a189dSsyl 	if (fd == NULL)
212a2badd06Stedu 		return;
213a2badd06Stedu 
21448ee6ddaSpelikan 	fd->fd_fmp = set ? fmp : NULL;
215a2badd06Stedu }
216a2badd06Stedu 
217a2badd06Stedu void
fuseattach(int num)218a2badd06Stedu fuseattach(int num)
219a2badd06Stedu {
220f76a189dSsyl 	LIST_INIT(&fuse_d_list);
221a2badd06Stedu }
222a2badd06Stedu 
223a2badd06Stedu int
fuseopen(dev_t dev,int flags,int fmt,struct proc * p)224a2badd06Stedu fuseopen(dev_t dev, int flags, int fmt, struct proc * p)
225a2badd06Stedu {
226f76a189dSsyl 	struct fuse_d *fd;
2272fd654f7Shelg 	int unit = minor(dev);
228a2badd06Stedu 
229f76a189dSsyl 	if (flags & O_EXCL)
230f76a189dSsyl 		return (EBUSY); /* No exclusive opens */
231a2badd06Stedu 
2322fd654f7Shelg 	if ((fd = fuse_lookup(unit)) != NULL)
233a2badd06Stedu 		return (EBUSY);
234a2badd06Stedu 
2352fd654f7Shelg 	fd = malloc(sizeof(*fd), M_DEVBUF, M_WAITOK | M_ZERO);
2362fd654f7Shelg 	fd->fd_unit = unit;
2372fd654f7Shelg 	SIMPLEQ_INIT(&fd->fd_fbufs_in);
2382fd654f7Shelg 	SIMPLEQ_INIT(&fd->fd_fbufs_wait);
239*e10a268eSmvs 	rw_init(&fd->fd_lock, "fusedlk");
240*e10a268eSmvs 	klist_init_rwlock(&fd->fd_rklist, &fd->fd_lock);
241*e10a268eSmvs 
2422fd654f7Shelg 	LIST_INSERT_HEAD(&fuse_d_list, fd, fd_list);
2432fd654f7Shelg 
244a2badd06Stedu 	stat_opened_fusedev++;
245a2badd06Stedu 	return (0);
246a2badd06Stedu }
247a2badd06Stedu 
248a2badd06Stedu int
fuseclose(dev_t dev,int flags,int fmt,struct proc * p)249a2badd06Stedu fuseclose(dev_t dev, int flags, int fmt, struct proc *p)
250a2badd06Stedu {
251f76a189dSsyl 	struct fuse_d *fd;
252b52e1426Ssyl 	int error;
253a2badd06Stedu 
254f76a189dSsyl 	fd = fuse_lookup(minor(dev));
255f76a189dSsyl 	if (fd == NULL)
256f76a189dSsyl 		return (EINVAL);
257a2badd06Stedu 
258f76a189dSsyl 	if (fd->fd_fmp) {
2594f926cddSsyl 		printf("fuse: device close without umount\n");
260f76a189dSsyl 		fd->fd_fmp->sess_init = 0;
261b7e69da4Shelg 		fuse_device_cleanup(dev);
262b52e1426Ssyl 		if ((vfs_busy(fd->fd_fmp->mp, VB_WRITE | VB_NOWAIT)) != 0)
263b52e1426Ssyl 			goto end;
2642fd654f7Shelg 		error = dounmount(fd->fd_fmp->mp, MNT_FORCE, p);
2655b26e4e1Sbluhm 		if (error)
266b52e1426Ssyl 			printf("fuse: unmount failed with error %d\n", error);
267f76a189dSsyl 		fd->fd_fmp = NULL;
268a2badd06Stedu 	}
269a2badd06Stedu 
270b52e1426Ssyl end:
2712fd654f7Shelg 	LIST_REMOVE(fd, fd_list);
2722fd654f7Shelg 	free(fd, M_DEVBUF, sizeof(*fd));
273a2badd06Stedu 	stat_opened_fusedev--;
274a2badd06Stedu 	return (0);
275a2badd06Stedu }
276a2badd06Stedu 
27792f58b66Ssyl /*
278b66b9ef8Sjsg  * FIOCGETFBDAT		Get fusebuf data from kernel to user
279b66b9ef8Sjsg  * FIOCSETFBDAT		Set fusebuf data from user to kernel
28092f58b66Ssyl  */
281a2badd06Stedu int
fuseioctl(dev_t dev,u_long cmd,caddr_t addr,int flags,struct proc * p)282a2badd06Stedu fuseioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
283a2badd06Stedu {
28492f58b66Ssyl 	struct fb_ioctl_xch *ioexch;
28592f58b66Ssyl 	struct fusebuf *lastfbuf;
28692f58b66Ssyl 	struct fusebuf *fbuf;
28792f58b66Ssyl 	struct fuse_d *fd;
288a2badd06Stedu 	int error = 0;
289a2badd06Stedu 
29092f58b66Ssyl 	fd = fuse_lookup(minor(dev));
291dbcea99fSmestre 	if (fd == NULL)
292dbcea99fSmestre 		return (ENXIO);
29392f58b66Ssyl 
294a2badd06Stedu 	switch (cmd) {
29592f58b66Ssyl 	case FIOCGETFBDAT:
29692f58b66Ssyl 		ioexch = (struct fb_ioctl_xch *)addr;
29792f58b66Ssyl 
29892f58b66Ssyl 		/* Looking for uuid in fd_fbufs_in */
299*e10a268eSmvs 		rw_enter_write(&fd->fd_lock);
30092f58b66Ssyl 		SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_in, fb_next) {
30192f58b66Ssyl 			if (fbuf->fb_uuid == ioexch->fbxch_uuid)
30292f58b66Ssyl 				break;
30392f58b66Ssyl 
30492f58b66Ssyl 			lastfbuf = fbuf;
30592f58b66Ssyl 		}
30692f58b66Ssyl 		if (fbuf == NULL) {
307*e10a268eSmvs 			rw_exit_write(&fd->fd_lock);
30892f58b66Ssyl 			printf("fuse: Cannot find fusebuf\n");
30992f58b66Ssyl 			return (EINVAL);
31092f58b66Ssyl 		}
31192f58b66Ssyl 
31292f58b66Ssyl 		/* Remove the fbuf from fd_fbufs_in */
31392f58b66Ssyl 		if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_in))
31492f58b66Ssyl 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
31592f58b66Ssyl 		else
31692f58b66Ssyl 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_in, lastfbuf,
31792f58b66Ssyl 			    fb_next);
318*e10a268eSmvs 		rw_exit_write(&fd->fd_lock);
319*e10a268eSmvs 
32092f58b66Ssyl 		stat_fbufs_in--;
32192f58b66Ssyl 
32292f58b66Ssyl 		/* Do not handle fbufs with bad len */
32392f58b66Ssyl 		if (fbuf->fb_len != ioexch->fbxch_len) {
32492f58b66Ssyl 			printf("fuse: Bad fusebuf len\n");
32592f58b66Ssyl 			return (EINVAL);
32692f58b66Ssyl 		}
32792f58b66Ssyl 
32892f58b66Ssyl 		/* Update the userland fbuf */
32992f58b66Ssyl 		error = copyout(fbuf->fb_dat, ioexch->fbxch_data,
33092f58b66Ssyl 		    ioexch->fbxch_len);
33192f58b66Ssyl 		if (error) {
33292f58b66Ssyl 			printf("fuse: cannot copyout\n");
33392f58b66Ssyl 			return (error);
33492f58b66Ssyl 		}
33592f58b66Ssyl 
33692f58b66Ssyl #ifdef FUSE_DEBUG
337c37506c6Shelg 		fuse_dump_buff(fbuf->fb_dat, fbuf->fb_len);
33892f58b66Ssyl #endif
33992f58b66Ssyl 
34092f58b66Ssyl 		/* Adding fbuf in fd_fbufs_wait */
341903f970aShelg 		free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
3426ff63212Ssyl 		fbuf->fb_dat = NULL;
34392f58b66Ssyl 		SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_wait, fbuf, fb_next);
34492f58b66Ssyl 		stat_fbufs_wait++;
34592f58b66Ssyl 		break;
34692f58b66Ssyl 
34792f58b66Ssyl 	case FIOCSETFBDAT:
34892f58b66Ssyl 		DPRINTF("SET BUFFER\n");
34992f58b66Ssyl 		ioexch = (struct fb_ioctl_xch *)addr;
35092f58b66Ssyl 
35192f58b66Ssyl 		/* looking for uuid in fd_fbufs_wait */
35292f58b66Ssyl 		SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) {
35392f58b66Ssyl 			if (fbuf->fb_uuid == ioexch->fbxch_uuid)
35492f58b66Ssyl 				break;
35592f58b66Ssyl 
35692f58b66Ssyl 			lastfbuf = fbuf;
35792f58b66Ssyl 		}
35892f58b66Ssyl 		if (fbuf == NULL) {
35992f58b66Ssyl 			printf("fuse: Cannot find fusebuf\n");
36092f58b66Ssyl 			return (EINVAL);
36192f58b66Ssyl 		}
36292f58b66Ssyl 
36392f58b66Ssyl 		/* Do not handle fbufs with bad len */
36492f58b66Ssyl 		if (fbuf->fb_len != ioexch->fbxch_len) {
36592f58b66Ssyl 			printf("fuse: Bad fusebuf size\n");
36692f58b66Ssyl 			return (EINVAL);
36792f58b66Ssyl 		}
36892f58b66Ssyl 
36992f58b66Ssyl 		/* fetching data from userland */
37092f58b66Ssyl 		fbuf->fb_dat = malloc(ioexch->fbxch_len, M_FUSEFS,
37192f58b66Ssyl 		    M_WAITOK | M_ZERO);
37292f58b66Ssyl 		error = copyin(ioexch->fbxch_data, fbuf->fb_dat,
37392f58b66Ssyl 		    ioexch->fbxch_len);
37492f58b66Ssyl 		if (error) {
37592f58b66Ssyl 			printf("fuse: Cannot copyin\n");
376903f970aShelg 			free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
3776ff63212Ssyl 			fbuf->fb_dat = NULL;
37892f58b66Ssyl 			return (error);
37992f58b66Ssyl 		}
38092f58b66Ssyl 
38192f58b66Ssyl #ifdef FUSE_DEBUG
38292f58b66Ssyl 		fuse_dump_buff(fbuf->fb_dat, fbuf->fb_len);
38392f58b66Ssyl #endif
38492f58b66Ssyl 
38592f58b66Ssyl 		/* Remove fbuf from fd_fbufs_wait */
38692f58b66Ssyl 		if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait))
38792f58b66Ssyl 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
38892f58b66Ssyl 		else
38992f58b66Ssyl 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lastfbuf,
39092f58b66Ssyl 			    fb_next);
39192f58b66Ssyl 		stat_fbufs_wait--;
39292f58b66Ssyl 		wakeup(fbuf);
39392f58b66Ssyl 		break;
394a2badd06Stedu 	default:
39592f58b66Ssyl 		error = EINVAL;
396a2badd06Stedu 	}
397a2badd06Stedu 
398a2badd06Stedu 	return (error);
399a2badd06Stedu }
400a2badd06Stedu 
401a2badd06Stedu int
fuseread(dev_t dev,struct uio * uio,int ioflag)402a2badd06Stedu fuseread(dev_t dev, struct uio *uio, int ioflag)
403a2badd06Stedu {
404f76a189dSsyl 	struct fuse_d *fd;
405a2badd06Stedu 	struct fusebuf *fbuf;
4065acd79ebSsyl 	struct fb_hdr hdr;
4074c526d15Ssyl 	void *tmpaddr;
408a2badd06Stedu 	int error = 0;
409a2badd06Stedu 
410*e10a268eSmvs 	/* We get the whole fusebuf or nothing */
411*e10a268eSmvs 	if (uio->uio_resid != FUSEBUFSIZE)
412*e10a268eSmvs 		return (EINVAL);
413*e10a268eSmvs 
414f76a189dSsyl 	fd = fuse_lookup(minor(dev));
415f76a189dSsyl 	if (fd == NULL)
416a2badd06Stedu 		return (ENXIO);
417a2badd06Stedu 
418*e10a268eSmvs 	rw_enter_write(&fd->fd_lock);
419*e10a268eSmvs 
420f76a189dSsyl 	if (SIMPLEQ_EMPTY(&fd->fd_fbufs_in)) {
421a2badd06Stedu 		if (ioflag & O_NONBLOCK)
422*e10a268eSmvs 			error = EAGAIN;
423a2badd06Stedu 		goto end;
424a2badd06Stedu 	}
425f76a189dSsyl 	fbuf = SIMPLEQ_FIRST(&fd->fd_fbufs_in);
426a2badd06Stedu 
4274c526d15Ssyl 	/* Do not send kernel pointers */
4284c526d15Ssyl 	memcpy(&hdr.fh_next, &fbuf->fb_next, sizeof(fbuf->fb_next));
429bb72b40bShelg 	memset(&fbuf->fb_next, 0, sizeof(fbuf->fb_next));
4304c526d15Ssyl 	tmpaddr = fbuf->fb_dat;
4314c526d15Ssyl 	fbuf->fb_dat = NULL;
43268343589Smiod 	error = uiomove(fbuf, FUSEBUFSIZE, uio);
433a2badd06Stedu 	if (error)
434a2badd06Stedu 		goto end;
435a2badd06Stedu 
436a2badd06Stedu #ifdef FUSE_DEBUG
4374c526d15Ssyl 	fuse_dump_buff((char *)fbuf, FUSEBUFSIZE);
438a2badd06Stedu #endif
4394c526d15Ssyl 	/* Restore kernel pointers */
4404c526d15Ssyl 	memcpy(&fbuf->fb_next, &hdr.fh_next, sizeof(fbuf->fb_next));
4414c526d15Ssyl 	fbuf->fb_dat = tmpaddr;
442a2badd06Stedu 
4434c526d15Ssyl 	/* Remove the fbuf if it does not contains data */
4444c526d15Ssyl 	if (fbuf->fb_len == 0) {
445f76a189dSsyl 		SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
446a2badd06Stedu 		stat_fbufs_in--;
447f76a189dSsyl 		SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_wait, fbuf, fb_next);
448a2badd06Stedu 		stat_fbufs_wait++;
449a2badd06Stedu 	}
450a2badd06Stedu 
451a2badd06Stedu end:
452*e10a268eSmvs 	rw_exit_write(&fd->fd_lock);
453a2badd06Stedu 	return (error);
454a2badd06Stedu }
455a2badd06Stedu 
456a2badd06Stedu int
fusewrite(dev_t dev,struct uio * uio,int ioflag)457a2badd06Stedu fusewrite(dev_t dev, struct uio *uio, int ioflag)
458a2badd06Stedu {
459a2badd06Stedu 	struct fusebuf *lastfbuf;
460f76a189dSsyl 	struct fuse_d *fd;
461a2badd06Stedu 	struct fusebuf *fbuf;
462a2badd06Stedu 	struct fb_hdr hdr;
463a2badd06Stedu 	int error = 0;
464a2badd06Stedu 
465f76a189dSsyl 	fd = fuse_lookup(minor(dev));
466f76a189dSsyl 	if (fd == NULL)
467a2badd06Stedu 		return (ENXIO);
468a2badd06Stedu 
4694c526d15Ssyl 	/* We get the whole fusebuf or nothing */
4704c526d15Ssyl 	if (uio->uio_resid != FUSEBUFSIZE)
471a2badd06Stedu 		return (EINVAL);
472a2badd06Stedu 
4735138b086Smiod 	if ((error = uiomove(&hdr, sizeof(hdr), uio)) != 0)
474a2badd06Stedu 		return (error);
475a2badd06Stedu 
4764c526d15Ssyl 	/* looking for uuid in fd_fbufs_wait */
477f76a189dSsyl 	SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) {
478460ac7beSsyl 		if (fbuf->fb_uuid == hdr.fh_uuid)
479a2badd06Stedu 			break;
480a2badd06Stedu 
481a2badd06Stedu 		lastfbuf = fbuf;
482a2badd06Stedu 	}
4834c526d15Ssyl 	if (fbuf == NULL)
4844c526d15Ssyl 		return (EINVAL);
485a2badd06Stedu 
4864c526d15Ssyl 	/* Update fb_hdr */
4875acd79ebSsyl 	fbuf->fb_len = hdr.fh_len;
4885acd79ebSsyl 	fbuf->fb_err = hdr.fh_err;
4895acd79ebSsyl 	fbuf->fb_ino = hdr.fh_ino;
490a2badd06Stedu 
4914c526d15Ssyl 	/* Check for corrupted fbufs */
4924c526d15Ssyl 	if ((fbuf->fb_len && fbuf->fb_err) ||
493f76a189dSsyl 	    SIMPLEQ_EMPTY(&fd->fd_fbufs_wait)) {
4944f926cddSsyl 		printf("fuse: dropping corrupted fusebuf\n");
4954c526d15Ssyl 		error = EINVAL;
4964c526d15Ssyl 		goto end;
497a2badd06Stedu 	}
498a2badd06Stedu 
499b66b9ef8Sjsg 	/* Get the missing data from the fbuf */
5006f2108a5Sstefan 	error = uiomove(&fbuf->FD, uio->uio_resid, uio);
501a2badd06Stedu 	if (error)
502a2badd06Stedu 		return error;
5034c526d15Ssyl 	fbuf->fb_dat = NULL;
504a2badd06Stedu #ifdef FUSE_DEBUG
5054c526d15Ssyl 	fuse_dump_buff((char *)fbuf, FUSEBUFSIZE);
506a2badd06Stedu #endif
507a2badd06Stedu 
508a2badd06Stedu 	switch (fbuf->fb_type) {
509a2badd06Stedu 	case FBT_INIT:
510f76a189dSsyl 		fd->fd_fmp->sess_init = 1;
511a2badd06Stedu 		break ;
512a2badd06Stedu 	case FBT_DESTROY:
513f76a189dSsyl 		fd->fd_fmp = NULL;
514a2badd06Stedu 		break ;
515a2badd06Stedu 	}
5164c526d15Ssyl end:
5174c526d15Ssyl 	/* Remove the fbuf if it does not contains data */
5184c526d15Ssyl 	if (fbuf->fb_len == 0) {
519f76a189dSsyl 		if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait))
520f76a189dSsyl 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
521a2badd06Stedu 		else
522f76a189dSsyl 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lastfbuf,
523a2badd06Stedu 			    fb_next);
524a2badd06Stedu 		stat_fbufs_wait--;
525a2badd06Stedu 		if (fbuf->fb_type == FBT_INIT)
52637318181Ssyl 			fb_delete(fbuf);
5274c526d15Ssyl 		else
5284c526d15Ssyl 			wakeup(fbuf);
5294c526d15Ssyl 	}
530a2badd06Stedu 
531a2badd06Stedu 	return (error);
532a2badd06Stedu }
533a2badd06Stedu 
534a2badd06Stedu int
fusekqfilter(dev_t dev,struct knote * kn)535a2badd06Stedu fusekqfilter(dev_t dev, struct knote *kn)
536a2badd06Stedu {
537f76a189dSsyl 	struct fuse_d *fd;
538a2badd06Stedu 	struct klist *klist;
539a2badd06Stedu 
540f76a189dSsyl 	fd = fuse_lookup(minor(dev));
541f76a189dSsyl 	if (fd == NULL)
542f76a189dSsyl 		return (EINVAL);
543a2badd06Stedu 
544a2badd06Stedu 	switch (kn->kn_filter) {
545a2badd06Stedu 	case EVFILT_READ:
546*e10a268eSmvs 		klist = &fd->fd_rklist;
547a2badd06Stedu 		kn->kn_fop = &fuse_rd_filtops;
548a2badd06Stedu 		break;
549a2badd06Stedu 	case EVFILT_WRITE:
550a9ad0377Smpi 		return (seltrue_kqfilter(dev, kn));
551a2badd06Stedu 	default:
552a2badd06Stedu 		return (EINVAL);
553a2badd06Stedu 	}
554a2badd06Stedu 
555f76a189dSsyl 	kn->kn_hook = fd;
556a2badd06Stedu 
557*e10a268eSmvs 	klist_insert(klist, kn);
558a2badd06Stedu 
559a2badd06Stedu 	return (0);
560a2badd06Stedu }
561a2badd06Stedu 
562a2badd06Stedu void
filt_fuse_rdetach(struct knote * kn)563a2badd06Stedu filt_fuse_rdetach(struct knote *kn)
564a2badd06Stedu {
565f76a189dSsyl 	struct fuse_d *fd = kn->kn_hook;
566*e10a268eSmvs 	struct klist *klist = &fd->fd_rklist;
567a2badd06Stedu 
568*e10a268eSmvs 	klist_remove(klist, kn);
569a2badd06Stedu }
570a2badd06Stedu 
571a2badd06Stedu int
filt_fuse_read(struct knote * kn,long hint)572a2badd06Stedu filt_fuse_read(struct knote *kn, long hint)
573a2badd06Stedu {
574f76a189dSsyl 	struct fuse_d *fd = kn->kn_hook;
575a2badd06Stedu 	int event = 0;
576a2badd06Stedu 
577*e10a268eSmvs 	rw_assert_wrlock(&fd->fd_lock);
578*e10a268eSmvs 
579f76a189dSsyl 	if (!SIMPLEQ_EMPTY(&fd->fd_fbufs_in))
580a2badd06Stedu 		event = 1;
581a2badd06Stedu 
582a2badd06Stedu 	return (event);
583a2badd06Stedu }
584*e10a268eSmvs 
585*e10a268eSmvs int
filt_fuse_modify(struct kevent * kev,struct knote * kn)586*e10a268eSmvs filt_fuse_modify(struct kevent *kev, struct knote *kn)
587*e10a268eSmvs {
588*e10a268eSmvs 	struct fuse_d *fd = kn->kn_hook;
589*e10a268eSmvs 	int active;
590*e10a268eSmvs 
591*e10a268eSmvs 	rw_enter_write(&fd->fd_lock);
592*e10a268eSmvs 	active = knote_modify(kev, kn);
593*e10a268eSmvs 	rw_exit_write(&fd->fd_lock);
594*e10a268eSmvs 
595*e10a268eSmvs 	return (active);
596*e10a268eSmvs }
597*e10a268eSmvs 
598*e10a268eSmvs int
filt_fuse_process(struct knote * kn,struct kevent * kev)599*e10a268eSmvs filt_fuse_process(struct knote *kn, struct kevent *kev)
600*e10a268eSmvs {
601*e10a268eSmvs 	struct fuse_d *fd = kn->kn_hook;
602*e10a268eSmvs 	int active;
603*e10a268eSmvs 
604*e10a268eSmvs 	rw_enter_write(&fd->fd_lock);
605*e10a268eSmvs 	active = knote_process(kn, kev);
606*e10a268eSmvs 	rw_exit_write(&fd->fd_lock);
607*e10a268eSmvs 
608*e10a268eSmvs 	return (active);
609*e10a268eSmvs }
610