jcs's openbsd hax
openbsd
1/* $OpenBSD: fuse_session.c,v 1.1 2026/01/22 11:53:31 helg Exp $ */
2/*
3 * Copyright (c) 2025 Helg Bredow <helg@openbsd.org>
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/uio.h>
19#include <errno.h>
20#include <err.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <string.h>
24#include <unistd.h>
25
26#include "debug.h"
27#include "fuse_private.h"
28
29void
30fuse_session_destroy(struct fuse_session *se)
31{
32 if (se->init && se->llops.destroy)
33 se->llops.destroy(se->userdata);
34
35 free(se->chan);
36 free(se);
37}
38DEF(fuse_session_destroy);
39
40void
41fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
42{
43 if (se->chan == NULL && ch->se == NULL) {
44 se->chan = ch;
45 ch->se = se;
46 }
47}
48DEF(fuse_session_add_chan);
49
50void
51fuse_session_remove_chan(struct fuse_chan *ch)
52{
53 if (ch->se->chan == ch) {
54 ch->se->chan = NULL;
55 ch->se = NULL;
56 }
57}
58DEF(fuse_session_remove_chan);
59
60void
61fuse_session_exit(struct fuse_session *se)
62{
63 se->exit = 1;
64}
65DEF(fuse_session_exit);
66
67int
68fuse_session_exited(const struct fuse_session *se)
69{
70 return (se->exit);
71}
72DEF(fuse_session_exited);
73
74void
75fuse_session_reset(struct fuse_session *se)
76{
77 if (se != NULL)
78 se->exit = 0;
79}
80DEF(fuse_session_reset);
81
82int
83fuse_session_loop(struct fuse_session *se)
84{
85 struct fuse_chan *ch;
86 struct fusebuf fbuf;
87 char *buf = (char *)&fbuf;
88 size_t bufsize;
89 int err;
90
91 if (se == NULL)
92 return (-1);
93
94 ch = se->chan;
95 if (ch == NULL)
96 return (-1);
97
98 /* prepare the read and write data buffer */
99 fbuf.fb_dat = calloc(1, FUSEBUFMAXSIZE);
100 if (fbuf.fb_dat == NULL) {
101 DPERROR(__func__);
102 return (-1);
103 }
104
105 bufsize = sizeof(fbuf.fb_hdr) + sizeof(fbuf.FD) + FUSEBUFMAXSIZE;
106
107 while (!fuse_session_exited(se)) {
108 err = fuse_chan_recv(&ch, buf, bufsize);
109 if (err == -EINTR || err == -ENODEV) {
110 fuse_session_exit(se);
111 continue;
112 } else if (err <= 0) {
113 DPERROR(__func__);
114 break;
115 }
116
117 fuse_session_process(se, buf, bufsize, ch);
118 }
119
120 free(fbuf.fb_dat);
121 fuse_session_reset(se);
122
123 return (err == 0 ? 0 : -1);
124}
125DEF(fuse_session_loop);
126
127static void
128iprocess_init(fuse_req_t req)
129{
130 struct fuse_session *se = req->se;
131 struct fuse_conn_info fci;
132
133 DPRINTF("%-11s", "init");
134
135 if (se->llops.init) {
136 memset(&fci, 0, sizeof(fci));
137 fci.proto_minor = FUSE_MINOR_VERSION;
138 fci.proto_major = FUSE_MAJOR_VERSION;
139
140 se->llops.init(se->userdata, &fci);
141 }
142
143 fuse_reply_err(req, 0);
144
145 se->init = 1;
146}
147
148static void
149iprocess_destroy(fuse_req_t req)
150{
151 struct fuse_session *se = req->se;
152
153 DPRINTF("%-11s", "destroy");
154
155 se->chan->dead = 1;
156 fuse_session_exit(se);
157 fuse_reply_err(req, 0);
158}
159
160static void
161iprocess_lookup(fuse_req_t req)
162{
163 struct fusebuf *fbuf = req->fbuf;
164 struct fuse_session *se = req->se;
165 const char *name = fbuf->fb_dat;
166
167 DPRINTF("%-11s", "lookup");
168 DPRINTF("inode: %llu\t", fbuf->fb_ino);
169 DPRINTF("name: %s\t", name);
170
171 if (se->llops.lookup)
172 se->llops.lookup(req, fbuf->fb_ino, name);
173 else
174 fuse_reply_err(req, ENOSYS);
175}
176
177static void
178iprocess_getattr(fuse_req_t req)
179{
180 struct fusebuf *fbuf = req->fbuf;
181 struct fuse_session *se = req->se;
182 struct fuse_file_info ffi;
183
184 DPRINTF("%-11s", "getattr");
185 DPRINTF("inode: %llu\t", fbuf->fb_ino);
186
187 if (se->llops.getattr) {
188 memset(&ffi, 0, sizeof(ffi));
189 ffi.fh = fbuf->fb_io_fd;
190 ffi.fh_old = ffi.fh;
191
192 se->llops.getattr(req, fbuf->fb_ino, &ffi);
193 } else
194 fuse_reply_err(req, ENOSYS);
195}
196
197static void
198iprocess_setattr(fuse_req_t req)
199{
200 struct fusebuf *fbuf = req->fbuf;
201 struct fuse_session *se = req->se;
202 struct fuse_file_info ffi;
203 struct stat *stbuf = &fbuf->fb_attr;
204 struct fb_io *io = fbtod(fbuf, struct fb_io *);
205
206 DPRINTF("%-11s", "setattr");
207 DPRINTF("inode: %llu\t", fbuf->fb_ino);
208
209 if (se->llops.setattr) {
210 memset(&ffi, 0, sizeof(ffi));
211 ffi.fh = fbuf->fb_io_fd;
212 ffi.fh_old = ffi.fh;
213
214 se->llops.setattr(req, fbuf->fb_ino, stbuf, io->fi_flags, &ffi);
215 } else
216 fuse_reply_err(req, ENOSYS);
217}
218
219static void
220iprocess_opendir(fuse_req_t req)
221{
222 struct fusebuf *fbuf = req->fbuf;
223 struct fuse_session *se = req->se;
224 struct fuse_file_info ffi;
225
226 DPRINTF("%-11s", "opendir");
227 DPRINTF("inode: %llu\t", fbuf->fb_ino);
228
229 memset(&ffi, 0, sizeof(ffi));
230 ffi.flags = fbuf->fb_io_flags;
231
232 if (se->llops.opendir)
233 se->llops.opendir(req, fbuf->fb_ino, &ffi);
234 else
235 fuse_reply_open(req, &ffi);
236}
237
238static void
239iprocess_readdir(fuse_req_t req)
240{
241 struct fusebuf *fbuf = req->fbuf;
242 struct fuse_session *se = req->se;
243 struct fuse_file_info ffi;
244
245 DPRINTF("%-11s", "readdir");
246 DPRINTF("inode: %llu\t", fbuf->fb_ino);
247 DPRINTF("size: %lu\t", fbuf->fb_io_len);
248 DPRINTF("offset: %llu\t", fbuf->fb_io_off);
249
250 if (se->llops.readdir) {
251 memset(&ffi, 0, sizeof(ffi));
252 ffi.fh = fbuf->fb_io_fd;
253 ffi.fh_old = ffi.fh;
254
255 se->llops.readdir(req, fbuf->fb_ino, fbuf->fb_io_len,
256 fbuf->fb_io_off, &ffi);
257 } else
258 fuse_reply_err(req, ENOSYS);
259}
260
261static void
262iprocess_releasedir(fuse_req_t req)
263{
264 struct fusebuf *fbuf = req->fbuf;
265 struct fuse_session *se = req->se;
266 struct fuse_file_info ffi;
267
268 DPRINTF("%-11s", "releasedir");
269 DPRINTF("inode: %llu\t", fbuf->fb_ino);
270
271 if (se->llops.releasedir) {
272 memset(&ffi, 0, sizeof(ffi));
273 ffi.fh = fbuf->fb_io_fd;
274 ffi.fh_old = ffi.fh;
275 ffi.flags = fbuf->fb_io_flags;
276
277 se->llops.releasedir(req, fbuf->fb_ino, &ffi);
278 } else
279 fuse_reply_err(req, 0);
280}
281
282static void
283iprocess_mkdir(fuse_req_t req)
284{
285 struct fusebuf *fbuf = req->fbuf;
286 struct fuse_session *se = req->se;
287
288 DPRINTF("%-11s", "mkdir");
289 DPRINTF("inode: %llu\t", fbuf->fb_ino);
290 DPRINTF("mode: %#6o\t", fbuf->fb_io_mode);
291 DPRINTF("name: %s\t", fbuf->fb_dat);
292
293 if (se->llops.mkdir)
294 se->llops.mkdir(req, fbuf->fb_ino, fbuf->fb_dat,
295 fbuf->fb_io_mode);
296 else
297 fuse_reply_err(req, ENOSYS);
298}
299
300static void
301iprocess_rmdir(fuse_req_t req)
302{
303 struct fusebuf *fbuf = req->fbuf;
304 struct fuse_session *se = req->se;
305
306 DPRINTF("%-11s", "rmdir");
307 DPRINTF("inode: %llu\t", fbuf->fb_ino);
308 DPRINTF("name: %s\t", fbuf->fb_dat);
309
310 if (se->llops.rmdir)
311 se->llops.rmdir(req, fbuf->fb_ino, fbuf->fb_dat);
312 else
313 fuse_reply_err(req, ENOSYS);
314}
315
316static void
317iprocess_mknod(fuse_req_t req)
318{
319 struct fusebuf *fbuf = req->fbuf;
320 struct fuse_session *se = req->se;
321
322 DPRINTF("%-11s", "mknod");
323 DPRINTF("inode: %llu\t", fbuf->fb_ino);
324 DPRINTF("mode: %#6o\t", fbuf->fb_io_mode);
325 DPRINTF("name: %s\t", fbuf->fb_dat);
326
327 if (se->llops.mknod)
328 se->llops.mknod(req, fbuf->fb_ino, fbuf->fb_dat,
329 fbuf->fb_io_mode, fbuf->fb_io_rdev);
330 else
331 fuse_reply_err(req, ENOSYS);
332}
333
334static void
335iprocess_open(fuse_req_t req)
336{
337 struct fusebuf *fbuf = req->fbuf;
338 struct fuse_session *se = req->se;
339 struct fuse_file_info ffi;
340
341 DPRINTF("%-11s", "open");
342 DPRINTF("inode: %llu\t", fbuf->fb_ino);
343
344 memset(&ffi, 0, sizeof(ffi));
345 ffi.flags = fbuf->fb_io_flags;
346
347 if (se->llops.open)
348 se->llops.open(req, fbuf->fb_ino, &ffi);
349 else
350 fuse_reply_open(req, &ffi);
351}
352
353static void
354iprocess_read(fuse_req_t req)
355{
356 struct fusebuf *fbuf = req->fbuf;
357 struct fuse_session *se = req->se;
358 struct fuse_file_info ffi;
359
360 DPRINTF("%-11s", "read");
361 DPRINTF("inode: %llu\t", fbuf->fb_ino);
362 DPRINTF("size: %lu\t", fbuf->fb_io_len);
363 DPRINTF("offset: %llu\t", fbuf->fb_io_off);
364
365 if (se->llops.read) {
366 memset(&ffi, 0, sizeof(ffi));
367 ffi.fh = fbuf->fb_io_fd;
368 ffi.fh_old = ffi.fh;
369 ffi.flags = fbuf->fb_io_flags;
370
371 se->llops.read(req, fbuf->fb_ino, fbuf->fb_io_len,
372 fbuf->fb_io_off, &ffi);
373 } else
374 fuse_reply_err(req, ENOSYS);
375}
376
377static void
378iprocess_write(fuse_req_t req)
379{
380 struct fusebuf *fbuf = req->fbuf;
381 struct fuse_session *se = req->se;
382 struct fuse_file_info ffi;
383
384 DPRINTF("%-11s", "write");
385 DPRINTF("inode: %llu\t", fbuf->fb_ino);
386 DPRINTF("size: %lu\t", fbuf->fb_io_len);
387 DPRINTF("offset: %llu\t", fbuf->fb_io_off);
388
389 if (se->llops.write) {
390 memset(&ffi, 0, sizeof(ffi));
391 ffi.fh = fbuf->fb_io_fd;
392 ffi.fh_old = ffi.fh;
393 ffi.writepage = fbuf->fb_io_flags & 1; // XXX
394
395 se->llops.write(req, fbuf->fb_ino, fbuf->fb_dat,
396 fbuf->fb_io_len, fbuf->fb_io_off, &ffi);
397 } else
398 fuse_reply_err(req, ENOSYS);
399}
400
401static void
402iprocess_fsync(fuse_req_t req)
403{
404 struct fusebuf *fbuf = req->fbuf;
405 struct fuse_session *se = req->se;
406 struct fuse_file_info ffi;
407
408 DPRINTF("%-11s", "fsync");
409 DPRINTF("inode: %llu\t", fbuf->fb_ino);
410
411 if (se->llops.fsync) {
412 memset(&ffi, 0, sizeof(ffi));
413 ffi.fh = fbuf->fb_io_fd;
414 ffi.fh_old = ffi.fh;
415
416 /*
417 * fdatasync(2) is just a wrapper around fsync(2) so datasync
418 * is always false.
419 */
420 se->llops.fsync(req, fbuf->fb_ino, 0 /* datasync */, &ffi);
421 } else
422 fuse_reply_err(req, ENOSYS);
423}
424
425static void
426iprocess_flush(fuse_req_t req)
427{
428 struct fusebuf *fbuf = req->fbuf;
429 struct fuse_session *se = req->se;
430 struct fuse_file_info ffi;
431
432 DPRINTF("%-11s", "flush");
433 DPRINTF("inode: %llu\t", fbuf->fb_ino);
434
435 if (se->llops.flush) {
436 memset(&ffi, 0, sizeof(ffi));
437 ffi.fh = fbuf->fb_io_fd;
438 ffi.fh_old = ffi.fh;
439 ffi.flush = 1;
440
441 se->llops.flush(req, fbuf->fb_ino, &ffi);
442 } else
443 fuse_reply_err(req, ENOSYS);
444}
445
446static void
447iprocess_release(fuse_req_t req)
448{
449 struct fusebuf *fbuf = req->fbuf;
450 struct fuse_session *se = req->se;
451 struct fuse_file_info ffi;
452
453 DPRINTF("%-11s", "release");
454 DPRINTF("inode: %llu\t", fbuf->fb_ino);
455
456 if (se->llops.release) {
457 memset(&ffi, 0, sizeof(ffi));
458 ffi.fh = fbuf->fb_io_fd;
459 ffi.fh_old = ffi.fh;
460 ffi.flags = fbuf->fb_io_flags;
461
462 se->llops.release(req, fbuf->fb_ino, &ffi);
463 } else
464 fuse_reply_err(req, 0);
465}
466
467static void
468iprocess_forget(fuse_req_t req)
469{
470 struct fusebuf *fbuf = req->fbuf;
471 struct fuse_session *se = req->se;
472
473 DPRINTF("%-11s", "forget");
474 DPRINTF("inode: %llu\t", fbuf->fb_ino);
475
476 if (se->llops.forget)
477 se->llops.forget(req, fbuf->fb_ino, 1 /* TODO */);
478 else
479 fuse_reply_err(req, 0);
480}
481
482static void
483iprocess_symlink(fuse_req_t req)
484{
485 struct fusebuf *fbuf = req->fbuf;
486 struct fuse_session *se = req->se;
487 const char *target;
488 const char *name;
489 int len;
490
491 name = fbuf->fb_dat;
492 len = strnlen(name, fbuf->fb_len);
493 target = &fbuf->fb_dat[len + 1];
494
495 DPRINTF("%-11s", "symlink");
496 DPRINTF("inode: %llu\t", fbuf->fb_ino);
497 DPRINTF("name: %s\t", name);
498 DPRINTF("target: %s\t", target);
499
500 if (se->llops.symlink)
501 se->llops.symlink(req, target, fbuf->fb_ino, name);
502 else
503 fuse_reply_err(req, ENOSYS);
504}
505
506static void
507iprocess_readlink(fuse_req_t req)
508{
509 struct fusebuf *fbuf = req->fbuf;
510 struct fuse_session *se = req->se;
511
512 DPRINTF("%-11s", "readlink");
513 DPRINTF("inode: %llu\t", fbuf->fb_ino);
514
515 if (se->llops.readlink)
516 se->llops.readlink(req, fbuf->fb_ino);
517 else
518 fuse_reply_err(req, ENOSYS);
519}
520
521static void
522iprocess_link(fuse_req_t req)
523{
524 struct fusebuf *fbuf = req->fbuf;
525 struct fuse_session *se = req->se;
526 const char *name = fbuf->fb_dat;
527
528 DPRINTF("%-11s", "link");
529 DPRINTF("inode: %llu\t", fbuf->fb_ino);
530 DPRINTF("inode: %llu\t", fbuf->fb_io_ino);
531 DPRINTF("name: %s\t", name);
532
533 if (se->llops.link)
534 se->llops.link(req, fbuf->fb_io_ino, fbuf->fb_ino, name);
535 else
536 fuse_reply_err(req, ENOSYS);
537}
538
539static void
540iprocess_unlink(fuse_req_t req)
541{
542 struct fusebuf *fbuf = req->fbuf;
543 struct fuse_session *se = req->se;
544
545 DPRINTF("%-11s", "unlink");
546 DPRINTF("inode: %llu\t", fbuf->fb_ino);
547 DPRINTF("name: %s\t", fbuf->fb_dat);
548
549 if (se->llops.unlink)
550 se->llops.unlink(req, fbuf->fb_ino, fbuf->fb_dat);
551 else
552 fuse_reply_err(req, ENOSYS);
553}
554
555static void
556iprocess_rename(fuse_req_t req)
557{
558 struct fusebuf *fbuf = req->fbuf;
559 struct fuse_session *se = req->se;
560 const char *target;
561 const char *name;
562 int len;
563
564 name = fbuf->fb_dat;
565 len = strnlen(name, fbuf->fb_len);
566 target = &fbuf->fb_dat[len + 1];
567
568 DPRINTF("%-11s", "rename");
569 DPRINTF("inode: %llu\t", fbuf->fb_ino);
570 DPRINTF("name: %s\t", name);
571 DPRINTF("inode: %llu\t", fbuf->fb_io_ino);
572 DPRINTF("target: %s\t", target);
573
574 if (se->llops.rename)
575 se->llops.rename(req, fbuf->fb_ino, name, fbuf->fb_io_ino,
576 target);
577 else
578 fuse_reply_err(req, ENOSYS);
579}
580
581static void
582iprocess_statfs(fuse_req_t req)
583{
584 struct fusebuf *fbuf = req->fbuf;
585 struct fuse_session *se = req->se;
586
587 DPRINTF("%-11s", "statfs");
588 DPRINTF("inode: %llu\t", fbuf->fb_ino);
589
590 if (se->llops.statfs)
591 se->llops.statfs(req, fbuf->fb_ino);
592 else
593 fuse_reply_err(req, ENOSYS);
594}
595
596void
597fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
598 struct fuse_chan *ch)
599{
600 struct fusebuf *fbuf;
601 struct fuse_req req;
602
603 fbuf = (struct fusebuf *)buf;
604 req.fbuf = fbuf;
605 req.se = se;
606 req.ch = (ch == NULL) ? se->chan : ch;
607 req.ctx.uid = fbuf->fb_uid;
608 req.ctx.gid = fbuf->fb_gid;
609 req.ctx.pid = fbuf->fb_tid;
610 req.ctx.umask = fbuf->fb_umask;
611
612 /* need to at least have the header for the next check */
613 if (len < sizeof(fbuf->fb_hdr))
614 return;
615
616 if (len < sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD) + fbuf->fb_len)
617 return;
618
619 switch (fbuf->fb_type) {
620 case FBT_INIT:
621 iprocess_init(&req);
622 break;
623 case FBT_DESTROY:
624 iprocess_destroy(&req);
625 break;
626 case FBT_LOOKUP:
627 iprocess_lookup(&req);
628 break;
629 case FBT_GETATTR:
630 iprocess_getattr(&req);
631 break;
632 case FBT_SETATTR:
633 iprocess_setattr(&req);
634 break;
635 case FBT_OPENDIR:
636 iprocess_opendir(&req);
637 break;
638 case FBT_READDIR:
639 iprocess_readdir(&req);
640 break;
641 case FBT_RELEASEDIR:
642 iprocess_releasedir(&req);
643 break;
644 case FBT_MKDIR:
645 iprocess_mkdir(&req);
646 break;
647 case FBT_RMDIR:
648 iprocess_rmdir(&req);
649 break;
650 case FBT_MKNOD:
651 iprocess_mknod(&req);
652 break;
653 case FBT_OPEN:
654 iprocess_open(&req);
655 break;
656 case FBT_READ:
657 iprocess_read(&req);
658 break;
659 case FBT_WRITE:
660 iprocess_write(&req);
661 break;
662 case FBT_FSYNC:
663 iprocess_fsync(&req);
664 break;
665 case FBT_FLUSH:
666 iprocess_flush(&req);
667 break;
668 case FBT_RELEASE:
669 iprocess_release(&req);
670 break;
671 case FBT_RECLAIM:
672 iprocess_forget(&req);
673 break;
674 case FBT_SYMLINK:
675 iprocess_symlink(&req);
676 break;
677 case FBT_READLINK:
678 iprocess_readlink(&req);
679 break;
680 case FBT_LINK:
681 iprocess_link(&req);
682 break;
683 case FBT_UNLINK:
684 iprocess_unlink(&req);
685 break;
686 case FBT_RENAME:
687 iprocess_rename(&req);
688 break;
689 case FBT_STATFS:
690 iprocess_statfs(&req);
691 break;
692 default:
693 DPRINTF("Opcode: %i not supported\t", fbuf->fb_type);
694 fuse_reply_err(&req, ENOSYS);
695 }
696 DPRINTF("\n");
697}
698DEF(fuse_session_process);