jcs's openbsd hax
openbsd
1/* $OpenBSD: fuse.c,v 1.58 2026/01/29 06:04:27 helg Exp $ */
2/*
3 * Copyright (c) 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/uio.h>
19
20#include <errno.h>
21#include <signal.h>
22#include <stddef.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include "fuse_private.h"
28#include "debug.h"
29
30static struct fuse_context *ictx = NULL;
31
32enum {
33 KEY_DEBUG,
34 KEY_FOREGROUND,
35 KEY_HELP,
36 KEY_HELP_WITHOUT_HEADER,
37 KEY_VERSION,
38 KEY_MAXREAD,
39 KEY_STUB
40};
41
42/* options supported by fuse_parse_cmdline */
43static struct fuse_opt fuse_core_opts[] = {
44 FUSE_OPT_KEY("-d", KEY_DEBUG),
45 FUSE_OPT_KEY("debug", KEY_DEBUG),
46 FUSE_OPT_KEY("-f", KEY_FOREGROUND),
47 FUSE_OPT_KEY("-h", KEY_HELP),
48 FUSE_OPT_KEY("--help", KEY_HELP),
49 FUSE_OPT_KEY("-ho", KEY_HELP_WITHOUT_HEADER),
50 FUSE_OPT_KEY("-s", KEY_STUB),
51 FUSE_OPT_KEY("-V", KEY_VERSION),
52 FUSE_OPT_KEY("--version", KEY_VERSION),
53 FUSE_OPT_END
54};
55
56/* options supported by fuse_new */
57#define FUSE_LIB_OPT(o, m) {o, offsetof(struct fuse_config, m), 1}
58static struct fuse_opt fuse_lib_opts[] = {
59 FUSE_OPT_KEY("ac_attr_timeout=", KEY_STUB),
60 FUSE_OPT_KEY("attr_timeout=", KEY_STUB),
61 FUSE_OPT_KEY("auto_cache", KEY_STUB),
62 FUSE_OPT_KEY("noauto_cache", KEY_STUB),
63 FUSE_OPT_KEY("big_writes", KEY_STUB),
64 FUSE_OPT_KEY("debug", KEY_DEBUG),
65 FUSE_OPT_KEY("-d", KEY_DEBUG),
66 FUSE_OPT_KEY("entry_timeout=", KEY_STUB),
67 FUSE_LIB_OPT("gid=", set_gid),
68 FUSE_LIB_OPT("gid=%u", gid),
69 FUSE_OPT_KEY("hard_remove", KEY_STUB),
70 FUSE_OPT_KEY("intr_signal", KEY_STUB),
71 FUSE_OPT_KEY("kernel_cache", KEY_STUB),
72 FUSE_OPT_KEY("large_read", KEY_STUB),
73 FUSE_OPT_KEY("modules=", KEY_STUB),
74 FUSE_OPT_KEY("negative_timeout=", KEY_STUB),
75 FUSE_OPT_KEY("readdir_ino", KEY_STUB),
76 FUSE_OPT_KEY("relatime", KEY_STUB),
77 FUSE_OPT_KEY("subtype=", KEY_STUB),
78 FUSE_LIB_OPT("uid=", set_uid),
79 FUSE_LIB_OPT("uid=%u", uid),
80 FUSE_LIB_OPT("use_ino", use_ino),
81 FUSE_OPT_KEY("dmask=%o", KEY_STUB),
82 FUSE_OPT_KEY("fmask=%o", KEY_STUB),
83 FUSE_LIB_OPT("umask=", set_mode),
84 FUSE_LIB_OPT("umask=%o", umask),
85 FUSE_OPT_END
86};
87
88/* options supported by fuse_mount */
89#define FUSE_MOUNT_OPT(o, m) {o, offsetof(struct fuse_mount_opts, m), 1}
90static struct fuse_opt fuse_mount_opts[] = {
91 FUSE_MOUNT_OPT("allow_other", allow_other),
92 FUSE_OPT_KEY("allow_root", KEY_STUB),
93 FUSE_OPT_KEY("async_read", KEY_STUB),
94 FUSE_OPT_KEY("blkdev", KEY_STUB),
95 FUSE_OPT_KEY("blksize=", KEY_STUB),
96 FUSE_MOUNT_OPT("default_permissions", def_perms),
97 FUSE_OPT_KEY("direct_io", KEY_STUB),
98 FUSE_MOUNT_OPT("fsname=%s", fsname),
99 FUSE_MOUNT_OPT("max_read=%u", max_read),
100 FUSE_OPT_KEY("max_readahead", KEY_STUB),
101 FUSE_OPT_KEY("max_write", KEY_STUB),
102 FUSE_MOUNT_OPT("noatime", noatime),
103 FUSE_MOUNT_OPT("nonempty", nonempty),
104 FUSE_MOUNT_OPT("-r", rdonly),
105 FUSE_MOUNT_OPT("ro", rdonly),
106 FUSE_OPT_KEY("ro_fallback", KEY_STUB),
107 FUSE_OPT_KEY("sync_read", KEY_STUB),
108 FUSE_OPT_END
109};
110
111extern struct fuse_lowlevel_ops llops;
112
113int
114fuse_loop(struct fuse *fuse)
115{
116 return fuse_session_loop(fuse_get_session(fuse));
117}
118DEF(fuse_loop);
119
120struct fuse_chan *
121fuse_mount(const char *dir, struct fuse_args *args)
122{
123 struct fusefs_args fargs;
124 struct fuse_mount_opts opts;
125 struct fuse_chan *fc;
126 const char *errcause;
127 char *mnt_dir;
128 int mnt_flags;
129
130 if (dir == NULL)
131 return (NULL);
132
133 fc = calloc(1, sizeof(*fc));
134 if (fc == NULL)
135 return (NULL);
136
137 mnt_dir = realpath(dir, NULL);
138 if (mnt_dir == NULL)
139 goto bad;
140
141 if ((fc->fd = open("/dev/fuse0", O_RDWR)) == -1) {
142 perror("/dev/fuse0");
143 goto bad;
144 }
145
146 memset(&opts, 0, sizeof(opts));
147 if (fuse_opt_parse(args, &opts, fuse_mount_opts, NULL) == -1)
148 goto bad;
149
150 mnt_flags = 0;
151 if (opts.rdonly)
152 mnt_flags |= MNT_RDONLY;
153 if (opts.noatime)
154 mnt_flags |= MNT_NOATIME;
155
156 if (opts.max_read > FUSEBUFMAXSIZE) {
157 fprintf(stderr, "fuse: invalid max_read (%d > %d)\n",
158 opts.max_read, FUSEBUFMAXSIZE);
159 goto bad;
160 }
161
162 memset(&fargs, 0, sizeof(fargs));
163 fargs.fd = fc->fd;
164 fargs.max_read = opts.max_read;
165 fargs.allow_other = opts.allow_other;
166
167 if (mount(MOUNT_FUSEFS, mnt_dir, mnt_flags, &fargs)) {
168 switch (errno) {
169 case EMFILE:
170 errcause = "mount table full";
171 break;
172 case EOPNOTSUPP:
173 errcause = "filesystem not supported by kernel";
174 break;
175 default:
176 errcause = strerror(errno);
177 break;
178 }
179 fprintf(stderr, "%s on %s: %s\n", __func__, dir, errcause);
180 goto bad;
181 }
182
183 return (fc);
184bad:
185 if (fc->fd != -1)
186 close(fc->fd);
187 free(mnt_dir);
188 free(fc);
189 return (NULL);
190}
191DEF(fuse_mount);
192
193void
194fuse_unmount(const char *dir, struct fuse_chan *ch)
195{
196 if (ch == NULL)
197 return;
198
199 /*
200 * Close the device before unmounting to prevent deadlocks with
201 * FBT_DESTROY if fuse_loop() has already terminated.
202 */
203 if (close(ch->fd) == -1)
204 DPERROR(__func__);
205
206 if (!ch->dead)
207 if (unmount(dir, MNT_FORCE) == -1)
208 DPERROR(__func__);
209}
210DEF(fuse_unmount);
211
212int
213fuse_is_lib_option(const char *opt)
214{
215 return (fuse_opt_match(fuse_lib_opts, opt));
216}
217DEF(fuse_is_lib_option);
218
219struct fuse_session *
220fuse_get_session(struct fuse *f)
221{
222 return (f->se);
223}
224DEF(fuse_get_session);
225
226int
227fuse_loop_mt(unused struct fuse *fuse)
228{
229 return (-1);
230}
231DEF(fuse_loop_mt);
232
233static int
234ifuse_lib_opt_proc(void *data, const char *arg, int key,
235 unused struct fuse_args *args)
236{
237 switch (key) {
238 case KEY_STUB:
239 return (0);
240 case KEY_DEBUG:
241 ifuse_debug_init();
242 break;
243 default:
244 fprintf(stderr, "fuse: unrecognised option %s\n", arg);
245 return (-1);
246 }
247
248 /* Keep unknown options. */
249 return (1);
250}
251
252struct fuse *
253fuse_new(struct fuse_chan *fc, struct fuse_args *args,
254 const struct fuse_operations *ops, unused size_t size,
255 void *userdata)
256{
257 struct fuse *fuse;
258 struct fuse_vnode *root;
259
260 if (fc == NULL || ops == NULL)
261 return (NULL);
262
263 if ((fuse = calloc(1, sizeof(*fuse))) == NULL)
264 return (NULL);
265
266 /* copy fuse ops to their own structure */
267 memcpy(&fuse->op, ops, sizeof(fuse->op));
268
269 if (fuse_opt_parse(args, &fuse->conf, fuse_lib_opts,
270 ifuse_lib_opt_proc) == -1) {
271 free(fuse);
272 return (NULL);
273 }
274
275 fuse->max_ino = FUSE_ROOT_INO;
276 fuse->private_data = userdata;
277 fuse->se = fuse_lowlevel_new(args, &llops, sizeof(llops), fuse);
278 if (fuse->se == NULL) {
279 free(fuse);
280 return (NULL);
281 }
282
283 fuse_session_add_chan(fuse->se, fc);
284
285 if ((root = alloc_vn(fuse, "/", FUSE_ROOT_INO, 0)) == NULL) {
286 free(fuse);
287 return (NULL);
288 }
289
290 tree_init(&fuse->vnode_tree);
291 tree_init(&fuse->name_tree);
292 if (!set_vn(fuse, root)) {
293 free(fuse);
294 return (NULL);
295 }
296
297 /*
298 * Prepare the context that is available to file system operations via
299 * fuse_get_context(3). The pid, gid, uid and umask fields are set
300 * on demand when this is called in a requeset handle.
301 */
302 ictx = calloc(1, sizeof(*ictx));
303 if (ictx == NULL) {
304 free(fuse);
305 return (NULL);
306 }
307
308 ictx->fuse = fuse;
309 ictx->private_data = userdata;
310
311 return (fuse);
312}
313DEF(fuse_new);
314
315int
316fuse_daemonize(int foreground)
317{
318 if (foreground)
319 return (0);
320
321 return (daemon(0, 0));
322}
323DEF(fuse_daemonize);
324
325void
326fuse_destroy(struct fuse *fuse)
327{
328 fuse_session_destroy(fuse_get_session(fuse));
329 free(fuse);
330 free(ictx);
331 ictx = NULL;
332}
333DEF(fuse_destroy);
334
335static void
336ifuse_sig_handler(int signum)
337{
338 /* empty handler to dinstinguish between SIG_IGN */
339}
340
341void
342fuse_remove_signal_handlers(unused struct fuse_session *se)
343{
344 struct sigaction old_sa;
345
346 if (sigaction(SIGHUP, NULL, &old_sa) == 0)
347 if (old_sa.sa_handler == ifuse_sig_handler)
348 signal(SIGHUP, SIG_DFL);
349
350 if (sigaction(SIGINT, NULL, &old_sa) == 0)
351 if (old_sa.sa_handler == ifuse_sig_handler)
352 signal(SIGINT, SIG_DFL);
353
354 if (sigaction(SIGTERM, NULL, &old_sa) == 0)
355 if (old_sa.sa_handler == ifuse_sig_handler)
356 signal(SIGTERM, SIG_DFL);
357
358 if (sigaction(SIGPIPE, NULL, &old_sa) == 0)
359 if (old_sa.sa_handler == SIG_IGN)
360 signal(SIGPIPE, SIG_DFL);
361}
362DEF(fuse_remove_signal_handlers);
363
364int
365fuse_set_signal_handlers(unused struct fuse_session *se)
366{
367 struct sigaction old_sa;
368
369 if (sigaction(SIGHUP, NULL, &old_sa) == -1)
370 return (-1);
371 if (old_sa.sa_handler == SIG_DFL)
372 signal(SIGHUP, ifuse_sig_handler);
373
374 if (sigaction(SIGINT, NULL, &old_sa) == -1)
375 return (-1);
376 if (old_sa.sa_handler == SIG_DFL)
377 signal(SIGINT, ifuse_sig_handler);
378
379 if (sigaction(SIGTERM, NULL, &old_sa) == -1)
380 return (-1);
381 if (old_sa.sa_handler == SIG_DFL)
382 signal(SIGTERM, ifuse_sig_handler);
383
384 if (sigaction(SIGPIPE, NULL, &old_sa) == -1)
385 return (-1);
386 if (old_sa.sa_handler == SIG_DFL)
387 signal(SIGPIPE, SIG_IGN);
388
389 return (0);
390}
391DEF(fuse_set_signal_handlers);
392
393static void
394dump_help(void)
395{
396 fprintf(stderr, "FUSE options:\n"
397 " -d -o debug enable debug output (implies -f)\n"
398 " -f run in foreground\n"
399 " -V --version print fuse version\n"
400 "\n");
401}
402
403static void
404dump_version(void)
405{
406 fprintf(stderr, "FUSE library version: %d.%d\n", FUSE_MAJOR_VERSION,
407 FUSE_MINOR_VERSION);
408}
409
410static int
411ifuse_process_opt(void *data, const char *arg, int key,
412 unused struct fuse_args *args)
413{
414 struct fuse_core_opts *opt = data;
415 struct stat st;
416 int res;
417
418 switch (key) {
419 case KEY_STUB:
420 return (0);
421 case KEY_DEBUG:
422 ifuse_debug_init();
423 /* falls through */
424 case KEY_FOREGROUND:
425 opt->foreground = 1;
426 return (0);
427 case KEY_HELP:
428 case KEY_HELP_WITHOUT_HEADER:
429 dump_help();
430 return (-1);
431 case KEY_VERSION:
432 dump_version();
433 return (-1);
434 case FUSE_OPT_KEY_NONOPT:
435 if (opt->mp == NULL) {
436 opt->mp = realpath(arg, opt->mp);
437 if (opt->mp == NULL) {
438 fprintf(stderr, "fuse: realpath: "
439 "%s : %s\n", arg, strerror(errno));
440 return (-1);
441 }
442
443 res = stat(opt->mp, &st);
444 if (res == -1) {
445 fprintf(stderr, "fuse: bad mount point "
446 "%s : %s\n", arg, strerror(errno));
447 return (-1);
448 }
449
450 if (!S_ISDIR(st.st_mode)) {
451 fprintf(stderr, "fuse: bad mount point "
452 "%s : %s\n", arg, strerror(ENOTDIR));
453 return (-1);
454 }
455 }
456 return (0);
457 }
458
459 /* Pass through unknown options. */
460 return (1);
461}
462
463int
464fuse_parse_cmdline(struct fuse_args *args, char **mp, int *mt, int *fg)
465{
466 struct fuse_core_opts opt;
467
468 memset(&opt, 0, sizeof(opt));
469 if (fuse_opt_parse(args, &opt, fuse_core_opts, ifuse_process_opt) == -1)
470 return (-1);
471
472 if (opt.mp == NULL) {
473 fprintf(stderr, "fuse: missing mountpoint parameter\n");
474 return (-1);
475 }
476
477 if (mp != NULL) {
478 *mp = strdup(opt.mp);
479 if (*mp == NULL)
480 return (-1);
481 }
482
483 if (mt != NULL)
484 *mt = 0;
485
486 if (fg != NULL)
487 *fg = opt.foreground;
488
489 return (0);
490}
491DEF(fuse_parse_cmdline);
492
493struct fuse_context *
494fuse_get_context(void)
495{
496 const fuse_req_t req = ifuse_req();
497 const struct fuse_ctx *req_ctx = fuse_req_ctx(req);
498
499 if (req_ctx == NULL) {
500 ictx->uid = 0;
501 ictx->gid = 0;
502 ictx->pid = 0;
503 ictx->umask = 0;
504 } else {
505 ictx->uid = req_ctx->uid;
506 ictx->gid = req_ctx->gid;
507 ictx->pid = req_ctx->pid;
508 ictx->umask = req_ctx->umask;
509 }
510
511 return (ictx);
512}
513DEF(fuse_get_context);
514
515int
516fuse_version(void)
517{
518 return (FUSE_VERSION);
519}
520DEF(fuse_version);
521
522void
523fuse_teardown(struct fuse *fuse, char *mp)
524{
525 if (fuse == NULL || mp == NULL)
526 return;
527
528 fuse_remove_signal_handlers(fuse->se);
529 fuse_unmount(mp, fuse->se->chan);
530 fuse_destroy(fuse);
531}
532DEF(fuse_teardown);
533
534int
535fuse_invalidate(unused struct fuse *f, unused const char *path)
536{
537 return (EINVAL);
538}
539DEF(fuse_invalidate);
540
541struct fuse *
542fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
543 size_t size, char **mp, int *mt, void *data)
544{
545 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
546 struct fuse_chan *fc;
547 struct fuse *fuse;
548 char *dir;
549 int fg;
550
551 dir = NULL;
552 if (fuse_parse_cmdline(&args, &dir, mt, &fg))
553 goto err;
554
555 if ((fc = fuse_mount(dir, &args)) == NULL)
556 goto err;
557
558 fuse_daemonize(fg);
559
560 if ((fuse = fuse_new(fc, &args, ops, size, data)) == NULL) {
561 fuse_unmount(dir, fc);
562 free(fc);
563 goto err;
564 }
565
566 /* args are no longer needed */
567 fuse_opt_free_args(&args);
568
569 if (fuse_set_signal_handlers(fuse_get_session(fuse)) == -1) {
570 fuse_unmount(dir, fc);
571 fuse_destroy(fuse);
572 goto err;
573 }
574
575 /* the caller frees dir, but we do it if the caller doesn't want it */
576 if (mp == NULL)
577 free(dir);
578 else
579 *mp = dir;
580
581 return (fuse);
582err:
583 free(dir);
584 return (NULL);
585}
586DEF(fuse_setup);
587
588int
589fuse_main(int argc, char **argv, const struct fuse_operations *ops, void *data)
590{
591 struct fuse *fuse;
592 char *mp;
593 int ret;
594
595 fuse = fuse_setup(argc, argv, ops, sizeof(*ops), &mp, NULL, data);
596 if (fuse == NULL)
597 return (-1);
598
599 ret = fuse_loop(fuse);
600 fuse_teardown(fuse, mp);
601
602 return (ret == -1 ? 1 : 0);
603}
604DEF(fuse_main);