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