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