]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_fusemount.c
Implement option parsing for fuse. The syntax is:
[bcachefs-tools-debian] / cmd_fusemount.c
1 #include <errno.h>
2 #include <float.h>
3 #include <getopt.h>
4 #include <stdio.h>
5 #include <sys/statvfs.h>
6
7 #include <fuse_lowlevel.h>
8
9 #include "cmds.h"
10 #include "libbcachefs.h"
11 #include "tools-util.h"
12
13 #include "libbcachefs/bcachefs.h"
14 #include "libbcachefs/btree_iter.h"
15 #include "libbcachefs/buckets.h"
16 #include "libbcachefs/dirent.h"
17 #include "libbcachefs/error.h"
18 #include "libbcachefs/fs-common.h"
19 #include "libbcachefs/inode.h"
20 #include "libbcachefs/opts.h"
21 #include "libbcachefs/super.h"
22
23 /* mode_to_type(): */
24 #include "libbcachefs/fs.h"
25
26 #include <linux/dcache.h>
27
28 /* XXX cut and pasted from fsck.c */
29 #define QSTR(n) { { { .len = strlen(n) } }, .name = n }
30
31 static inline u64 map_root_ino(u64 ino)
32 {
33         return ino == 1 ? 4096 : ino;
34 }
35
36 static inline u64 unmap_root_ino(u64 ino)
37 {
38         return ino == 4096 ? 1 : ino;
39 }
40
41 static struct stat inode_to_stat(struct bch_fs *c,
42                                  struct bch_inode_unpacked *bi)
43 {
44         return (struct stat) {
45                 .st_size        = bi->bi_size,
46                 .st_mode        = bi->bi_mode,
47                 .st_uid         = bi->bi_uid,
48                 .st_gid         = bi->bi_gid,
49                 .st_nlink       = bch2_inode_nlink_get(bi),
50                 .st_rdev        = bi->bi_dev,
51                 .st_blksize     = block_bytes(c),
52                 .st_blocks      = bi->bi_sectors,
53                 .st_atim        = bch2_time_to_timespec(c, bi->bi_atime),
54                 .st_mtim        = bch2_time_to_timespec(c, bi->bi_mtime),
55                 .st_ctim        = bch2_time_to_timespec(c, bi->bi_ctime),
56         };
57 }
58
59 static struct fuse_entry_param inode_to_entry(struct bch_fs *c,
60                                               struct bch_inode_unpacked *bi)
61 {
62         return (struct fuse_entry_param) {
63                 .ino            = bi->bi_inum,
64                 .generation     = bi->bi_generation,
65                 .attr           = inode_to_stat(c, bi),
66                 .attr_timeout   = DBL_MAX,
67                 .entry_timeout  = DBL_MAX,
68         };
69 }
70
71 static void bcachefs_fuse_destroy(void *arg)
72 {
73         struct bch_fs *c = arg;
74
75         bch2_fs_stop(c);
76 }
77
78 static void bcachefs_fuse_lookup(fuse_req_t req, fuse_ino_t dir,
79                                  const char *name)
80 {
81         struct bch_fs *c = fuse_req_userdata(req);
82         struct bch_inode_unpacked bi;
83         struct qstr qstr = QSTR(name);
84         u64 inum;
85         int ret;
86
87         dir = map_root_ino(dir);
88
89         pr_info("dir %llu name %s", (u64) dir, name);
90
91         inum = bch2_dirent_lookup(c, dir, &qstr);
92         if (!inum) {
93                 ret = -ENOENT;
94                 goto err;
95         }
96
97         ret = bch2_inode_find_by_inum(c, inum, &bi);
98         if (ret)
99                 goto err;
100
101         bi.bi_inum = unmap_root_ino(bi.bi_inum);
102
103         struct fuse_entry_param e = inode_to_entry(c, &bi);
104         fuse_reply_entry(req, &e);
105         return;
106 err:
107         fuse_reply_err(req, -ret);
108 }
109
110 static void bcachefs_fuse_getattr(fuse_req_t req, fuse_ino_t inum,
111                                   struct fuse_file_info *fi)
112 {
113         struct bch_fs *c = fuse_req_userdata(req);
114         struct bch_inode_unpacked bi;
115         struct stat attr;
116         int ret;
117
118         inum = map_root_ino(inum);
119
120         pr_info("inum %llu", (u64) inum);
121
122         ret = bch2_inode_find_by_inum(c, inum, &bi);
123         if (ret) {
124                 fuse_reply_err(req, -ret);
125                 return;
126         }
127
128         bi.bi_inum = unmap_root_ino(bi.bi_inum);
129
130         attr = inode_to_stat(c, &bi);
131         fuse_reply_attr(req, &attr, DBL_MAX);
132 }
133
134 static void bcachefs_fuse_setattr(fuse_req_t req, fuse_ino_t inum,
135                                   struct stat *attr, int to_set,
136                                   struct fuse_file_info *fi)
137 {
138         struct bch_fs *c = fuse_req_userdata(req);
139         struct bch_inode_unpacked inode_u;
140         struct btree_trans trans;
141         struct btree_iter *iter;
142         u64 now;
143         int ret;
144
145         inum = map_root_ino(inum);
146
147         bch2_trans_init(&trans, c, 0, 0);
148 retry:
149         bch2_trans_begin(&trans);
150         now = bch2_current_time(c);
151
152         iter = bch2_inode_peek(&trans, &inode_u, inum, BTREE_ITER_INTENT);
153         ret = PTR_ERR_OR_ZERO(iter);
154         if (ret)
155                 goto err;
156
157         if (to_set & FUSE_SET_ATTR_MODE)
158                 inode_u.bi_mode = attr->st_mode;
159         if (to_set & FUSE_SET_ATTR_UID)
160                 inode_u.bi_uid  = attr->st_uid;
161         if (to_set & FUSE_SET_ATTR_GID)
162                 inode_u.bi_gid  = attr->st_gid;
163         if (to_set & FUSE_SET_ATTR_SIZE)
164                 inode_u.bi_size = attr->st_size;
165         if (to_set & FUSE_SET_ATTR_ATIME)
166                 inode_u.bi_atime = timespec_to_bch2_time(c, attr->st_atim);
167         if (to_set & FUSE_SET_ATTR_MTIME)
168                 inode_u.bi_mtime = timespec_to_bch2_time(c, attr->st_mtim);
169         if (to_set & FUSE_SET_ATTR_ATIME_NOW)
170                 inode_u.bi_atime = now;
171         if (to_set & FUSE_SET_ATTR_MTIME_NOW)
172                 inode_u.bi_mtime = now;
173
174         ret   = bch2_inode_write(&trans, iter, &inode_u) ?:
175                 bch2_trans_commit(&trans, NULL, NULL,
176                                   BTREE_INSERT_ATOMIC|
177                                   BTREE_INSERT_NOFAIL);
178 err:
179         if (ret == -EINTR)
180                 goto retry;
181
182         bch2_trans_exit(&trans);
183
184         if (!ret) {
185                 *attr = inode_to_stat(c, &inode_u);
186                 fuse_reply_attr(req, attr, DBL_MAX);
187         } else {
188                 fuse_reply_err(req, -ret);
189         }
190 }
191
192 static void bcachefs_fuse_readlink(fuse_req_t req, fuse_ino_t inum)
193 {
194         //struct bch_fs *c = fuse_req_userdata(req);
195
196         //char *link = malloc();
197
198         //fuse_reply_readlink(req, link);
199 }
200
201 static int do_create(struct bch_fs *c, u64 dir,
202                      const char *name, mode_t mode, dev_t rdev,
203                      struct bch_inode_unpacked *new_inode)
204 {
205         struct qstr qstr = QSTR(name);
206         struct bch_inode_unpacked dir_u;
207
208         dir = map_root_ino(dir);
209
210         bch2_inode_init_early(c, new_inode);
211
212         return bch2_trans_do(c, NULL, 0,
213                         bch2_create_trans(&trans,
214                                 dir, &dir_u,
215                                 new_inode, &qstr,
216                                 0, 0, mode, rdev, NULL, NULL));
217 }
218
219 static void bcachefs_fuse_mknod(fuse_req_t req, fuse_ino_t dir,
220                                 const char *name, mode_t mode,
221                                 dev_t rdev)
222 {
223         struct bch_fs *c = fuse_req_userdata(req);
224         struct bch_inode_unpacked new_inode;
225         int ret;
226
227         ret = do_create(c, dir, name, mode, rdev, &new_inode);
228         if (ret)
229                 goto err;
230
231         struct fuse_entry_param e = inode_to_entry(c, &new_inode);
232         fuse_reply_entry(req, &e);
233         return;
234 err:
235         fuse_reply_err(req, -ret);
236 }
237
238 static void bcachefs_fuse_mkdir(fuse_req_t req, fuse_ino_t dir,
239                                 const char *name, mode_t mode)
240 {
241         bcachefs_fuse_mknod(req, dir, name, mode, 0);
242 }
243
244 static void bcachefs_fuse_unlink(fuse_req_t req, fuse_ino_t dir,
245                                  const char *name)
246 {
247         struct bch_fs *c = fuse_req_userdata(req);
248         struct bch_inode_unpacked dir_u, inode_u;
249         struct qstr qstr = QSTR(name);
250         int ret;
251
252         dir = map_root_ino(dir);
253
254         ret = bch2_trans_do(c, NULL, BTREE_INSERT_ATOMIC|BTREE_INSERT_NOFAIL,
255                             bch2_unlink_trans(&trans, dir, &dir_u,
256                                               &inode_u, &qstr));
257
258         fuse_reply_err(req, -ret);
259 }
260
261 static void bcachefs_fuse_rmdir(fuse_req_t req, fuse_ino_t dir,
262                                 const char *name)
263 {
264         dir = map_root_ino(dir);
265
266         bcachefs_fuse_unlink(req, dir, name);
267 }
268
269 #if 0
270 static void bcachefs_fuse_symlink(fuse_req_t req, const char *link,
271                                   fuse_ino_t parent, const char *name)
272 {
273         struct bch_fs *c = fuse_req_userdata(req);
274 }
275 #endif
276
277 static void bcachefs_fuse_rename(fuse_req_t req,
278                                  fuse_ino_t src_dir, const char *srcname,
279                                  fuse_ino_t dst_dir, const char *dstname,
280                                  unsigned flags)
281 {
282         struct bch_fs *c = fuse_req_userdata(req);
283         struct bch_inode_unpacked dst_dir_u, src_dir_u;
284         struct bch_inode_unpacked src_inode_u, dst_inode_u;
285         struct qstr dst_name = QSTR(srcname);
286         struct qstr src_name = QSTR(dstname);
287         int ret;
288
289         src_dir = map_root_ino(src_dir);
290         dst_dir = map_root_ino(dst_dir);
291
292         /* XXX handle overwrites */
293         ret = bch2_trans_do(c, NULL, BTREE_INSERT_ATOMIC,
294                 bch2_rename_trans(&trans,
295                                   src_dir, &src_dir_u,
296                                   dst_dir, &dst_dir_u,
297                                   &src_inode_u, &dst_inode_u,
298                                   &src_name, &dst_name,
299                                   BCH_RENAME));
300
301         fuse_reply_err(req, -ret);
302 }
303
304 static void bcachefs_fuse_link(fuse_req_t req, fuse_ino_t inum,
305                                fuse_ino_t newparent, const char *newname)
306 {
307         struct bch_fs *c = fuse_req_userdata(req);
308         struct bch_inode_unpacked inode_u;
309         struct qstr qstr = QSTR(newname);
310         int ret;
311
312         ret = bch2_trans_do(c, NULL, BTREE_INSERT_ATOMIC,
313                             bch2_link_trans(&trans, newparent,
314                                             inum, &inode_u, &qstr));
315
316         if (!ret) {
317                 struct fuse_entry_param e = inode_to_entry(c, &inode_u);
318                 fuse_reply_entry(req, &e);
319         } else {
320                 fuse_reply_err(req, -ret);
321         }
322 }
323
324 #if 0
325 static void bcachefs_fuse_open(fuse_req_t req, fuse_ino_t inum,
326                                struct fuse_file_info *fi)
327 {
328         struct bch_fs *c = fuse_req_userdata(req);
329 }
330
331 static void bcachefs_fuse_read(fuse_req_t req, fuse_ino_t inum,
332                                size_t size, off_t off,
333                                struct fuse_file_info *fi)
334 {
335         struct bch_fs *c = fuse_req_userdata(req);
336 }
337
338 static void bcachefs_fuse_flush(fuse_req_t req, fuse_ino_t inum,
339                                 struct fuse_file_info *fi)
340 {
341         struct bch_fs *c = fuse_req_userdata(req);
342 }
343
344 static void bcachefs_fuse_release(fuse_req_t req, fuse_ino_t inum,
345                                   struct fuse_file_info *fi)
346 {
347         struct bch_fs *c = fuse_req_userdata(req);
348 }
349
350 static void bcachefs_fuse_fsync(fuse_req_t req, fuse_ino_t inum, int datasync,
351                                 struct fuse_file_info *fi)
352 {
353         struct bch_fs *c = fuse_req_userdata(req);
354 }
355
356 static void bcachefs_fuse_opendir(fuse_req_t req, fuse_ino_t inum,
357                                   struct fuse_file_info *fi)
358 {
359         struct bch_fs *c = fuse_req_userdata(req);
360 }
361 #endif
362
363 struct fuse_dir_context {
364         struct dir_context      ctx;
365         fuse_req_t              req;
366         char                    *buf;
367         size_t                  bufsize;
368 };
369
370 static int fuse_filldir(struct dir_context *_ctx,
371                         const char *name, int namelen,
372                         loff_t pos, u64 dir, unsigned type)
373 {
374         struct fuse_dir_context *ctx =
375                 container_of(_ctx, struct fuse_dir_context, ctx);
376
377         struct stat statbuf = {
378                 .st_ino         = map_root_ino(dir),
379                 .st_mode        = type << 12,
380         };
381
382         size_t len = fuse_add_direntry(ctx->req,
383                                        ctx->buf,
384                                        ctx->bufsize,
385                                        name,
386                                        &statbuf,
387                                        pos + 1);
388
389         if (len > ctx->bufsize)
390                 return 0;
391
392         ctx->buf        += len;
393         ctx->bufsize    -= len;
394         return 1;
395 }
396
397 static void bcachefs_fuse_readdir(fuse_req_t req, fuse_ino_t dir,
398                                   size_t size, off_t off,
399                                   struct fuse_file_info *fi)
400 {
401         struct bch_fs *c = fuse_req_userdata(req);
402         char buf[4096];
403         struct fuse_dir_context ctx = {
404                 .ctx.actor      = fuse_filldir,
405                 .ctx.pos        = off,
406                 .req            = req,
407                 .buf            = buf,
408                 .bufsize        = sizeof(buf),
409         };
410         int ret;
411
412         dir = map_root_ino(dir);
413
414         ret = bch2_readdir(c, dir, &ctx.ctx);
415         if (!ret) {
416                 fuse_reply_buf(req, buf, ctx.buf - buf);
417         } else {
418                 fuse_reply_err(req, -ret);
419         }
420 }
421
422 #if 0
423 static void bcachefs_fuse_releasedir(fuse_req_t req, fuse_ino_t inum,
424                                      struct fuse_file_info *fi)
425 {
426         struct bch_fs *c = fuse_req_userdata(req);
427 }
428
429 static void bcachefs_fuse_fsyncdir(fuse_req_t req, fuse_ino_t inum, int datasync,
430                                    struct fuse_file_info *fi)
431 {
432         struct bch_fs *c = fuse_req_userdata(req);
433 }
434 #endif
435
436 static void bcachefs_fuse_statfs(fuse_req_t req, fuse_ino_t inum)
437 {
438         struct bch_fs *c = fuse_req_userdata(req);
439         struct bch_fs_usage_short usage = bch2_fs_usage_read_short(c);
440         unsigned shift = c->block_bits;
441         struct statvfs statbuf = {
442                 .f_bsize        = block_bytes(c),
443                 .f_frsize       = block_bytes(c),
444                 .f_blocks       = usage.capacity >> shift,
445                 .f_bfree        = (usage.capacity - usage.used) >> shift,
446                 //.f_bavail     = statbuf.f_bfree,
447                 .f_files        = usage.nr_inodes,
448                 .f_ffree        = U64_MAX,
449                 .f_namemax      = BCH_NAME_MAX,
450         };
451
452         fuse_reply_statfs(req, &statbuf);
453 }
454
455 #if 0
456 static void bcachefs_fuse_setxattr(fuse_req_t req, fuse_ino_t inum,
457                                    const char *name, const char *value,
458                                    size_t size, int flags)
459 {
460         struct bch_fs *c = fuse_req_userdata(req);
461 }
462
463 static void bcachefs_fuse_getxattr(fuse_req_t req, fuse_ino_t inum,
464                                    const char *name, size_t size)
465 {
466         struct bch_fs *c = fuse_req_userdata(req);
467
468         fuse_reply_xattr(req, );
469 }
470
471 static void bcachefs_fuse_listxattr(fuse_req_t req, fuse_ino_t inum, size_t size)
472 {
473         struct bch_fs *c = fuse_req_userdata(req);
474 }
475
476 static void bcachefs_fuse_removexattr(fuse_req_t req, fuse_ino_t inum,
477                                       const char *name)
478 {
479         struct bch_fs *c = fuse_req_userdata(req);
480 }
481 #endif
482
483 static void bcachefs_fuse_create(fuse_req_t req, fuse_ino_t dir,
484                                  const char *name, mode_t mode,
485                                  struct fuse_file_info *fi)
486 {
487         struct bch_fs *c = fuse_req_userdata(req);
488         struct bch_inode_unpacked new_inode;
489         int ret;
490
491         ret = do_create(c, dir, name, mode, 0, &new_inode);
492         if (ret)
493                 goto err;
494
495         struct fuse_entry_param e = inode_to_entry(c, &new_inode);
496         fuse_reply_create(req, &e, fi);
497         return;
498 err:
499         fuse_reply_err(req, -ret);
500
501 }
502
503 #if 0
504 static void bcachefs_fuse_write_buf(fuse_req_t req, fuse_ino_t inum,
505                                     struct fuse_bufvec *bufv, off_t off,
506                                     struct fuse_file_info *fi)
507 {
508         struct bch_fs *c = fuse_req_userdata(req);
509 }
510
511 static void bcachefs_fuse_fallocate(fuse_req_t req, fuse_ino_t inum, int mode,
512                                     off_t offset, off_t length,
513                                     struct fuse_file_info *fi)
514 {
515         struct bch_fs *c = fuse_req_userdata(req);
516 }
517 #endif
518
519 static const struct fuse_lowlevel_ops bcachefs_fuse_ops = {
520         .destroy        = bcachefs_fuse_destroy,
521         .lookup         = bcachefs_fuse_lookup,
522         .getattr        = bcachefs_fuse_getattr,
523         .setattr        = bcachefs_fuse_setattr,
524         .readlink       = bcachefs_fuse_readlink,
525         .mknod          = bcachefs_fuse_mknod,
526         .mkdir          = bcachefs_fuse_mkdir,
527         .unlink         = bcachefs_fuse_unlink,
528         .rmdir          = bcachefs_fuse_rmdir,
529         //.symlink      = bcachefs_fuse_symlink,
530         .rename         = bcachefs_fuse_rename,
531         .link           = bcachefs_fuse_link,
532         //.open         = bcachefs_fuse_open,
533         //.read         = bcachefs_fuse_read,
534         //.write        = bcachefs_fuse_write,
535         //.flush        = bcachefs_fuse_flush,
536         //.release      = bcachefs_fuse_release,
537         //.fsync        = bcachefs_fuse_fsync,
538         //.opendir      = bcachefs_fuse_opendir,
539         .readdir        = bcachefs_fuse_readdir,
540         //.releasedir   = bcachefs_fuse_releasedir,
541         //.fsyncdir     = bcachefs_fuse_fsyncdir,
542         .statfs         = bcachefs_fuse_statfs,
543         //.setxattr     = bcachefs_fuse_setxattr,
544         //.getxattr     = bcachefs_fuse_getxattr,
545         //.listxattr    = bcachefs_fuse_listxattr,
546         //.removexattr  = bcachefs_fuse_removexattr,
547         .create         = bcachefs_fuse_create,
548
549         /* posix locks: */
550 #if 0
551         .getlk          = bcachefs_fuse_getlk,
552         .setlk          = bcachefs_fuse_setlk,
553 #endif
554         //.write_buf    = bcachefs_fuse_write_buf,
555         //.fallocate    = bcachefs_fuse_fallocate,
556
557 };
558
559 /*
560  * Setup and command parsing.
561  */
562
563 struct bf_context {
564         char            *devices_str;
565         char            **devices;
566         int             nr_devices;
567 };
568
569 static void bf_context_free(struct bf_context *ctx)
570 {
571         int i;
572
573         free(ctx->devices_str);
574         for (i = 0; i < ctx->nr_devices; ++i)
575                 free(ctx->devices[i]);
576         free(ctx->devices);
577 }
578
579 static struct fuse_opt bf_opts[] = {
580         FUSE_OPT_END
581 };
582
583 /*
584  * Fuse option parsing helper -- returning 0 means we consumed the argument, 1
585  * means we did not.
586  */
587 static int bf_opt_proc(void *data, const char *arg, int key,
588     struct fuse_args *outargs)
589 {
590         struct bf_context *ctx = data;
591
592         switch (key) {
593         case FUSE_OPT_KEY_NONOPT:
594                 /* Just extract the first non-option string. */
595                 if (!ctx->devices_str) {
596                         ctx->devices_str = strdup(arg);
597                         return 0;
598                 }
599                 return 1;
600         }
601
602         return 1;
603 }
604
605 /*
606  * dev1:dev2 -> [ dev1, dev2 ]
607  * dev       -> [ dev ]
608  */
609 static void tokenize_devices(struct bf_context *ctx)
610 {
611         char *devices_str = strdup(ctx->devices_str);
612         char *devices_tmp = devices_str;
613         char **devices = NULL;
614         int nr = 0;
615         char *dev = NULL;
616
617         while ((dev = strsep(&devices_tmp, ":"))) {
618                 if (strlen(dev) > 0) {
619                         devices = realloc(devices, (nr + 1) * sizeof *devices);
620                         devices[nr] = strdup(dev);
621                         nr++;
622                 }
623         }
624
625         if (!devices) {
626                 devices = malloc(sizeof *devices);
627                 devices[0] = strdup(ctx->devices_str);
628                 nr = 1;
629         }
630
631         ctx->devices = devices;
632         ctx->nr_devices = nr;
633
634         free(devices_str);
635 }
636
637 static void usage(char *argv[])
638 {
639         printf("Usage: %s fusemount [options] <dev>[:dev2:...] <mountpoint>\n",
640                argv[0]);
641         printf("\n");
642 }
643
644 int cmd_fusemount(int argc, char *argv[])
645 {
646         struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
647         struct bch_opts bch_opts = bch2_opts_empty();
648         struct bf_context ctx = { 0 };
649         struct bch_fs *c = NULL;
650         int ret = 0, i;
651
652         /* Parse arguments. */
653         if (fuse_opt_parse(&args, &ctx, bf_opts, bf_opt_proc) < 0)
654                 die("fuse_opt_parse err: %m");
655
656         struct fuse_cmdline_opts fuse_opts;
657         if (fuse_parse_cmdline(&args, &fuse_opts) < 0)
658                 die("fuse_parse_cmdline err: %m");
659
660         if (fuse_opts.show_help) {
661                 usage(argv);
662                 fuse_cmdline_help();
663                 fuse_lowlevel_help();
664                 ret = 0;
665                 goto out;
666         }
667         if (fuse_opts.show_version) {
668                 /* TODO: Show bcachefs version. */
669                 printf("FUSE library version %s\n", fuse_pkgversion());
670                 fuse_lowlevel_version();
671                 ret = 0;
672                 goto out;
673         }
674         if (!fuse_opts.mountpoint) {
675                 usage(argv);
676                 printf("Please supply a mountpoint.\n");
677                 ret = 1;
678                 goto out;
679         }
680         if (!ctx.devices_str) {
681                 usage(argv);
682                 printf("Please specify a device or device1:device2:...\n");
683                 ret = 1;
684                 goto out;
685         }
686         tokenize_devices(&ctx);
687
688         /* Open bch */
689         printf("Opening bcachefs filesystem on:\n");
690         for (i = 0; i < ctx.nr_devices; ++i)
691                 printf("\t%s\n", ctx.devices[i]);
692
693         c = bch2_fs_open(ctx.devices, ctx.nr_devices, bch_opts);
694         if (IS_ERR(c))
695                 die("error opening %s: %s", ctx.devices_str,
696                     strerror(-PTR_ERR(c)));
697
698         /* Fuse */
699         struct fuse_session *se =
700                 fuse_session_new(&args, &bcachefs_fuse_ops,
701                                  sizeof(bcachefs_fuse_ops), c);
702         if (!se)
703                 die("fuse_lowlevel_new err: %m");
704
705         if (fuse_set_signal_handlers(se) < 0)
706                 die("fuse_set_signal_handlers err: %m");
707
708         if (fuse_session_mount(se, fuse_opts.mountpoint))
709                 die("fuse_mount err: %m");
710
711         fuse_daemonize(fuse_opts.foreground);
712
713         ret = fuse_session_loop(se);
714
715         /* Cleanup */
716         fuse_session_unmount(se);
717         fuse_remove_signal_handlers(se);
718         fuse_session_destroy(se);
719
720 out:
721         free(fuse_opts.mountpoint);
722         fuse_opt_free_args(&args);
723         bf_context_free(&ctx);
724
725         return ret ? 1 : 0;
726 }