xref: /openbsd/sys/miscfs/fuse/fuse_device.c (revision e10a268e)
1 /* $OpenBSD: fuse_device.c,v 1.40 2023/12/16 22:17:08 mvs Exp $ */
2 /*
3  * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/fcntl.h>
21 #include <sys/ioctl.h>
22 #include <sys/event.h>
23 #include <sys/malloc.h>
24 #include <sys/mount.h>
25 #include <sys/rwlock.h>
26 #include <sys/stat.h>
27 #include <sys/statvfs.h>
28 #include <sys/vnode.h>
29 #include <sys/fusebuf.h>
30 
31 #include "fusefs_node.h"
32 #include "fusefs.h"
33 
34 #ifdef	FUSE_DEBUG
35 #define	DPRINTF(fmt, arg...)	printf("%s: " fmt, "fuse", ##arg)
36 #else
37 #define	DPRINTF(fmt, arg...)
38 #endif
39 
40 /*
41  * Locks used to protect struct members and global data
42  *	l	fd_lock
43  */
44 
45 SIMPLEQ_HEAD(fusebuf_head, fusebuf);
46 
47 struct fuse_d {
48 	struct rwlock fd_lock;
49 	struct fusefs_mnt *fd_fmp;
50 	int fd_unit;
51 
52 	/*fusebufs queues*/
53 	struct fusebuf_head fd_fbufs_in;	/* [l] */
54 	struct fusebuf_head fd_fbufs_wait;
55 
56 	/* kq fields */
57 	struct klist fd_rklist;			/* [l] */
58 	LIST_ENTRY(fuse_d) fd_list;
59 };
60 
61 int stat_fbufs_in = 0;
62 int stat_fbufs_wait = 0;
63 int stat_opened_fusedev = 0;
64 
65 LIST_HEAD(, fuse_d) fuse_d_list;
66 struct fuse_d *fuse_lookup(int);
67 
68 void	fuseattach(int);
69 int	fuseopen(dev_t, int, int, struct proc *);
70 int	fuseclose(dev_t, int, int, struct proc *);
71 int	fuseioctl(dev_t, u_long, caddr_t, int, struct proc *);
72 int	fuseread(dev_t, struct uio *, int);
73 int	fusewrite(dev_t, struct uio *, int);
74 int	fusekqfilter(dev_t dev, struct knote *kn);
75 int	filt_fuse_read(struct knote *, long);
76 void	filt_fuse_rdetach(struct knote *);
77 int	filt_fuse_modify(struct kevent *, struct knote *);
78 int	filt_fuse_process(struct knote *, struct kevent *);
79 
80 static const struct filterops fuse_rd_filtops = {
81 	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
82 	.f_attach	= NULL,
83 	.f_detach	= filt_fuse_rdetach,
84 	.f_event	= filt_fuse_read,
85 	.f_modify	= filt_fuse_modify,
86 	.f_process	= filt_fuse_process,
87 };
88 
89 #ifdef FUSE_DEBUG
90 static void
fuse_dump_buff(char * buff,int len)91 fuse_dump_buff(char *buff, int len)
92 {
93 	char text[17];
94 	int i;
95 
96 	if (len < 0) {
97 		printf("invalid len: %d", len);
98 		return;
99 	}
100 	if (buff == NULL) {
101 		printf("invalid buff");
102 		return;
103 	}
104 
105 	memset(text, 0, 17);
106 	for (i = 0; i < len; i++) {
107 		if (i != 0 && (i % 16) == 0) {
108 			printf(": %s\n", text);
109 			memset(text, 0, 17);
110 		}
111 
112 		printf("%.2x ", buff[i] & 0xff);
113 
114 		if (buff[i] > ' ' && buff[i] < '~')
115 			text[i%16] = buff[i] & 0xff;
116 		else
117 			text[i%16] = '.';
118 	}
119 
120 	if ((i % 16) != 0)
121 		while ((i % 16) != 0) {
122 			printf("   ");
123 			i++;
124 		}
125 
126 	printf(": %s\n", text);
127 }
128 #endif
129 
130 struct fuse_d *
fuse_lookup(int unit)131 fuse_lookup(int unit)
132 {
133 	struct fuse_d *fd;
134 
135 	LIST_FOREACH(fd, &fuse_d_list, fd_list)
136 		if (fd->fd_unit == unit)
137 			return (fd);
138 	return (NULL);
139 }
140 
141 /*
142  * Cleanup all msgs from sc_fbufs_in and sc_fbufs_wait.
143  */
144 void
fuse_device_cleanup(dev_t dev)145 fuse_device_cleanup(dev_t dev)
146 {
147 	struct fuse_d *fd;
148 	struct fusebuf *f, *ftmp, *lprev;
149 
150 	fd = fuse_lookup(minor(dev));
151 	if (fd == NULL)
152 		return;
153 
154 	/* clear FIFO IN */
155 	lprev = NULL;
156 	rw_enter_write(&fd->fd_lock);
157 	SIMPLEQ_FOREACH_SAFE(f, &fd->fd_fbufs_in, fb_next, ftmp) {
158 		DPRINTF("cleanup unprocessed msg in sc_fbufs_in\n");
159 		if (lprev == NULL)
160 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
161 		else
162 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_in, lprev,
163 			    fb_next);
164 
165 		stat_fbufs_in--;
166 		f->fb_err = ENXIO;
167 		wakeup(f);
168 		lprev = f;
169 	}
170 	rw_exit_write(&fd->fd_lock);
171 
172 	/* clear FIFO WAIT*/
173 	lprev = NULL;
174 	SIMPLEQ_FOREACH_SAFE(f, &fd->fd_fbufs_wait, fb_next, ftmp) {
175 		DPRINTF("umount unprocessed msg in sc_fbufs_wait\n");
176 		if (lprev == NULL)
177 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
178 		else
179 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lprev,
180 			    fb_next);
181 
182 		stat_fbufs_wait--;
183 		f->fb_err = ENXIO;
184 		wakeup(f);
185 		lprev = f;
186 	}
187 }
188 
189 void
fuse_device_queue_fbuf(dev_t dev,struct fusebuf * fbuf)190 fuse_device_queue_fbuf(dev_t dev, struct fusebuf *fbuf)
191 {
192 	struct fuse_d *fd;
193 
194 	fd = fuse_lookup(minor(dev));
195 	if (fd == NULL)
196 		return;
197 
198 	rw_enter_write(&fd->fd_lock);
199 	SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_in, fbuf, fb_next);
200 	knote_locked(&fd->fd_rklist, 0);
201 	rw_exit_write(&fd->fd_lock);
202 	stat_fbufs_in++;
203 }
204 
205 void
fuse_device_set_fmp(struct fusefs_mnt * fmp,int set)206 fuse_device_set_fmp(struct fusefs_mnt *fmp, int set)
207 {
208 	struct fuse_d *fd;
209 
210 	fd = fuse_lookup(minor(fmp->dev));
211 	if (fd == NULL)
212 		return;
213 
214 	fd->fd_fmp = set ? fmp : NULL;
215 }
216 
217 void
fuseattach(int num)218 fuseattach(int num)
219 {
220 	LIST_INIT(&fuse_d_list);
221 }
222 
223 int
fuseopen(dev_t dev,int flags,int fmt,struct proc * p)224 fuseopen(dev_t dev, int flags, int fmt, struct proc * p)
225 {
226 	struct fuse_d *fd;
227 	int unit = minor(dev);
228 
229 	if (flags & O_EXCL)
230 		return (EBUSY); /* No exclusive opens */
231 
232 	if ((fd = fuse_lookup(unit)) != NULL)
233 		return (EBUSY);
234 
235 	fd = malloc(sizeof(*fd), M_DEVBUF, M_WAITOK | M_ZERO);
236 	fd->fd_unit = unit;
237 	SIMPLEQ_INIT(&fd->fd_fbufs_in);
238 	SIMPLEQ_INIT(&fd->fd_fbufs_wait);
239 	rw_init(&fd->fd_lock, "fusedlk");
240 	klist_init_rwlock(&fd->fd_rklist, &fd->fd_lock);
241 
242 	LIST_INSERT_HEAD(&fuse_d_list, fd, fd_list);
243 
244 	stat_opened_fusedev++;
245 	return (0);
246 }
247 
248 int
fuseclose(dev_t dev,int flags,int fmt,struct proc * p)249 fuseclose(dev_t dev, int flags, int fmt, struct proc *p)
250 {
251 	struct fuse_d *fd;
252 	int error;
253 
254 	fd = fuse_lookup(minor(dev));
255 	if (fd == NULL)
256 		return (EINVAL);
257 
258 	if (fd->fd_fmp) {
259 		printf("fuse: device close without umount\n");
260 		fd->fd_fmp->sess_init = 0;
261 		fuse_device_cleanup(dev);
262 		if ((vfs_busy(fd->fd_fmp->mp, VB_WRITE | VB_NOWAIT)) != 0)
263 			goto end;
264 		error = dounmount(fd->fd_fmp->mp, MNT_FORCE, p);
265 		if (error)
266 			printf("fuse: unmount failed with error %d\n", error);
267 		fd->fd_fmp = NULL;
268 	}
269 
270 end:
271 	LIST_REMOVE(fd, fd_list);
272 	free(fd, M_DEVBUF, sizeof(*fd));
273 	stat_opened_fusedev--;
274 	return (0);
275 }
276 
277 /*
278  * FIOCGETFBDAT		Get fusebuf data from kernel to user
279  * FIOCSETFBDAT		Set fusebuf data from user to kernel
280  */
281 int
fuseioctl(dev_t dev,u_long cmd,caddr_t addr,int flags,struct proc * p)282 fuseioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
283 {
284 	struct fb_ioctl_xch *ioexch;
285 	struct fusebuf *lastfbuf;
286 	struct fusebuf *fbuf;
287 	struct fuse_d *fd;
288 	int error = 0;
289 
290 	fd = fuse_lookup(minor(dev));
291 	if (fd == NULL)
292 		return (ENXIO);
293 
294 	switch (cmd) {
295 	case FIOCGETFBDAT:
296 		ioexch = (struct fb_ioctl_xch *)addr;
297 
298 		/* Looking for uuid in fd_fbufs_in */
299 		rw_enter_write(&fd->fd_lock);
300 		SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_in, fb_next) {
301 			if (fbuf->fb_uuid == ioexch->fbxch_uuid)
302 				break;
303 
304 			lastfbuf = fbuf;
305 		}
306 		if (fbuf == NULL) {
307 			rw_exit_write(&fd->fd_lock);
308 			printf("fuse: Cannot find fusebuf\n");
309 			return (EINVAL);
310 		}
311 
312 		/* Remove the fbuf from fd_fbufs_in */
313 		if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_in))
314 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
315 		else
316 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_in, lastfbuf,
317 			    fb_next);
318 		rw_exit_write(&fd->fd_lock);
319 
320 		stat_fbufs_in--;
321 
322 		/* Do not handle fbufs with bad len */
323 		if (fbuf->fb_len != ioexch->fbxch_len) {
324 			printf("fuse: Bad fusebuf len\n");
325 			return (EINVAL);
326 		}
327 
328 		/* Update the userland fbuf */
329 		error = copyout(fbuf->fb_dat, ioexch->fbxch_data,
330 		    ioexch->fbxch_len);
331 		if (error) {
332 			printf("fuse: cannot copyout\n");
333 			return (error);
334 		}
335 
336 #ifdef FUSE_DEBUG
337 		fuse_dump_buff(fbuf->fb_dat, fbuf->fb_len);
338 #endif
339 
340 		/* Adding fbuf in fd_fbufs_wait */
341 		free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
342 		fbuf->fb_dat = NULL;
343 		SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_wait, fbuf, fb_next);
344 		stat_fbufs_wait++;
345 		break;
346 
347 	case FIOCSETFBDAT:
348 		DPRINTF("SET BUFFER\n");
349 		ioexch = (struct fb_ioctl_xch *)addr;
350 
351 		/* looking for uuid in fd_fbufs_wait */
352 		SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) {
353 			if (fbuf->fb_uuid == ioexch->fbxch_uuid)
354 				break;
355 
356 			lastfbuf = fbuf;
357 		}
358 		if (fbuf == NULL) {
359 			printf("fuse: Cannot find fusebuf\n");
360 			return (EINVAL);
361 		}
362 
363 		/* Do not handle fbufs with bad len */
364 		if (fbuf->fb_len != ioexch->fbxch_len) {
365 			printf("fuse: Bad fusebuf size\n");
366 			return (EINVAL);
367 		}
368 
369 		/* fetching data from userland */
370 		fbuf->fb_dat = malloc(ioexch->fbxch_len, M_FUSEFS,
371 		    M_WAITOK | M_ZERO);
372 		error = copyin(ioexch->fbxch_data, fbuf->fb_dat,
373 		    ioexch->fbxch_len);
374 		if (error) {
375 			printf("fuse: Cannot copyin\n");
376 			free(fbuf->fb_dat, M_FUSEFS, fbuf->fb_len);
377 			fbuf->fb_dat = NULL;
378 			return (error);
379 		}
380 
381 #ifdef FUSE_DEBUG
382 		fuse_dump_buff(fbuf->fb_dat, fbuf->fb_len);
383 #endif
384 
385 		/* Remove fbuf from fd_fbufs_wait */
386 		if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait))
387 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
388 		else
389 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lastfbuf,
390 			    fb_next);
391 		stat_fbufs_wait--;
392 		wakeup(fbuf);
393 		break;
394 	default:
395 		error = EINVAL;
396 	}
397 
398 	return (error);
399 }
400 
401 int
fuseread(dev_t dev,struct uio * uio,int ioflag)402 fuseread(dev_t dev, struct uio *uio, int ioflag)
403 {
404 	struct fuse_d *fd;
405 	struct fusebuf *fbuf;
406 	struct fb_hdr hdr;
407 	void *tmpaddr;
408 	int error = 0;
409 
410 	/* We get the whole fusebuf or nothing */
411 	if (uio->uio_resid != FUSEBUFSIZE)
412 		return (EINVAL);
413 
414 	fd = fuse_lookup(minor(dev));
415 	if (fd == NULL)
416 		return (ENXIO);
417 
418 	rw_enter_write(&fd->fd_lock);
419 
420 	if (SIMPLEQ_EMPTY(&fd->fd_fbufs_in)) {
421 		if (ioflag & O_NONBLOCK)
422 			error = EAGAIN;
423 		goto end;
424 	}
425 	fbuf = SIMPLEQ_FIRST(&fd->fd_fbufs_in);
426 
427 	/* Do not send kernel pointers */
428 	memcpy(&hdr.fh_next, &fbuf->fb_next, sizeof(fbuf->fb_next));
429 	memset(&fbuf->fb_next, 0, sizeof(fbuf->fb_next));
430 	tmpaddr = fbuf->fb_dat;
431 	fbuf->fb_dat = NULL;
432 	error = uiomove(fbuf, FUSEBUFSIZE, uio);
433 	if (error)
434 		goto end;
435 
436 #ifdef FUSE_DEBUG
437 	fuse_dump_buff((char *)fbuf, FUSEBUFSIZE);
438 #endif
439 	/* Restore kernel pointers */
440 	memcpy(&fbuf->fb_next, &hdr.fh_next, sizeof(fbuf->fb_next));
441 	fbuf->fb_dat = tmpaddr;
442 
443 	/* Remove the fbuf if it does not contains data */
444 	if (fbuf->fb_len == 0) {
445 		SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_in, fb_next);
446 		stat_fbufs_in--;
447 		SIMPLEQ_INSERT_TAIL(&fd->fd_fbufs_wait, fbuf, fb_next);
448 		stat_fbufs_wait++;
449 	}
450 
451 end:
452 	rw_exit_write(&fd->fd_lock);
453 	return (error);
454 }
455 
456 int
fusewrite(dev_t dev,struct uio * uio,int ioflag)457 fusewrite(dev_t dev, struct uio *uio, int ioflag)
458 {
459 	struct fusebuf *lastfbuf;
460 	struct fuse_d *fd;
461 	struct fusebuf *fbuf;
462 	struct fb_hdr hdr;
463 	int error = 0;
464 
465 	fd = fuse_lookup(minor(dev));
466 	if (fd == NULL)
467 		return (ENXIO);
468 
469 	/* We get the whole fusebuf or nothing */
470 	if (uio->uio_resid != FUSEBUFSIZE)
471 		return (EINVAL);
472 
473 	if ((error = uiomove(&hdr, sizeof(hdr), uio)) != 0)
474 		return (error);
475 
476 	/* looking for uuid in fd_fbufs_wait */
477 	SIMPLEQ_FOREACH(fbuf, &fd->fd_fbufs_wait, fb_next) {
478 		if (fbuf->fb_uuid == hdr.fh_uuid)
479 			break;
480 
481 		lastfbuf = fbuf;
482 	}
483 	if (fbuf == NULL)
484 		return (EINVAL);
485 
486 	/* Update fb_hdr */
487 	fbuf->fb_len = hdr.fh_len;
488 	fbuf->fb_err = hdr.fh_err;
489 	fbuf->fb_ino = hdr.fh_ino;
490 
491 	/* Check for corrupted fbufs */
492 	if ((fbuf->fb_len && fbuf->fb_err) ||
493 	    SIMPLEQ_EMPTY(&fd->fd_fbufs_wait)) {
494 		printf("fuse: dropping corrupted fusebuf\n");
495 		error = EINVAL;
496 		goto end;
497 	}
498 
499 	/* Get the missing data from the fbuf */
500 	error = uiomove(&fbuf->FD, uio->uio_resid, uio);
501 	if (error)
502 		return error;
503 	fbuf->fb_dat = NULL;
504 #ifdef FUSE_DEBUG
505 	fuse_dump_buff((char *)fbuf, FUSEBUFSIZE);
506 #endif
507 
508 	switch (fbuf->fb_type) {
509 	case FBT_INIT:
510 		fd->fd_fmp->sess_init = 1;
511 		break ;
512 	case FBT_DESTROY:
513 		fd->fd_fmp = NULL;
514 		break ;
515 	}
516 end:
517 	/* Remove the fbuf if it does not contains data */
518 	if (fbuf->fb_len == 0) {
519 		if (fbuf == SIMPLEQ_FIRST(&fd->fd_fbufs_wait))
520 			SIMPLEQ_REMOVE_HEAD(&fd->fd_fbufs_wait, fb_next);
521 		else
522 			SIMPLEQ_REMOVE_AFTER(&fd->fd_fbufs_wait, lastfbuf,
523 			    fb_next);
524 		stat_fbufs_wait--;
525 		if (fbuf->fb_type == FBT_INIT)
526 			fb_delete(fbuf);
527 		else
528 			wakeup(fbuf);
529 	}
530 
531 	return (error);
532 }
533 
534 int
fusekqfilter(dev_t dev,struct knote * kn)535 fusekqfilter(dev_t dev, struct knote *kn)
536 {
537 	struct fuse_d *fd;
538 	struct klist *klist;
539 
540 	fd = fuse_lookup(minor(dev));
541 	if (fd == NULL)
542 		return (EINVAL);
543 
544 	switch (kn->kn_filter) {
545 	case EVFILT_READ:
546 		klist = &fd->fd_rklist;
547 		kn->kn_fop = &fuse_rd_filtops;
548 		break;
549 	case EVFILT_WRITE:
550 		return (seltrue_kqfilter(dev, kn));
551 	default:
552 		return (EINVAL);
553 	}
554 
555 	kn->kn_hook = fd;
556 
557 	klist_insert(klist, kn);
558 
559 	return (0);
560 }
561 
562 void
filt_fuse_rdetach(struct knote * kn)563 filt_fuse_rdetach(struct knote *kn)
564 {
565 	struct fuse_d *fd = kn->kn_hook;
566 	struct klist *klist = &fd->fd_rklist;
567 
568 	klist_remove(klist, kn);
569 }
570 
571 int
filt_fuse_read(struct knote * kn,long hint)572 filt_fuse_read(struct knote *kn, long hint)
573 {
574 	struct fuse_d *fd = kn->kn_hook;
575 	int event = 0;
576 
577 	rw_assert_wrlock(&fd->fd_lock);
578 
579 	if (!SIMPLEQ_EMPTY(&fd->fd_fbufs_in))
580 		event = 1;
581 
582 	return (event);
583 }
584 
585 int
filt_fuse_modify(struct kevent * kev,struct knote * kn)586 filt_fuse_modify(struct kevent *kev, struct knote *kn)
587 {
588 	struct fuse_d *fd = kn->kn_hook;
589 	int active;
590 
591 	rw_enter_write(&fd->fd_lock);
592 	active = knote_modify(kev, kn);
593 	rw_exit_write(&fd->fd_lock);
594 
595 	return (active);
596 }
597 
598 int
filt_fuse_process(struct knote * kn,struct kevent * kev)599 filt_fuse_process(struct knote *kn, struct kevent *kev)
600 {
601 	struct fuse_d *fd = kn->kn_hook;
602 	int active;
603 
604 	rw_enter_write(&fd->fd_lock);
605 	active = knote_process(kn, kev);
606 	rw_exit_write(&fd->fd_lock);
607 
608 	return (active);
609 }
610