]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_fusemount.c
build fixes; initial read/write methods
[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/alloc_foreground.h"
15 #include "libbcachefs/btree_iter.h"
16 #include "libbcachefs/buckets.h"
17 #include "libbcachefs/dirent.h"
18 #include "libbcachefs/error.h"
19 #include "libbcachefs/fs-common.h"
20 #include "libbcachefs/inode.h"
21 #include "libbcachefs/io.h"
22 #include "libbcachefs/opts.h"
23 #include "libbcachefs/super.h"
24
25 /* mode_to_type(): */
26 #include "libbcachefs/fs.h"
27
28 #include <linux/dcache.h>
29
30 /* XXX cut and pasted from fsck.c */
31 #define QSTR(n) { { { .len = strlen(n) } }, .name = n }
32
33 static inline u64 map_root_ino(u64 ino)
34 {
35         return ino == 1 ? 4096 : ino;
36 }
37
38 static inline u64 unmap_root_ino(u64 ino)
39 {
40         return ino == 4096 ? 1 : ino;
41 }
42
43 static struct stat inode_to_stat(struct bch_fs *c,
44                                  struct bch_inode_unpacked *bi)
45 {
46         return (struct stat) {
47                 .st_size        = bi->bi_size,
48                 .st_mode        = bi->bi_mode,
49                 .st_uid         = bi->bi_uid,
50                 .st_gid         = bi->bi_gid,
51                 .st_nlink       = bch2_inode_nlink_get(bi),
52                 .st_rdev        = bi->bi_dev,
53                 .st_blksize     = block_bytes(c),
54                 .st_blocks      = bi->bi_sectors,
55                 .st_atim        = bch2_time_to_timespec(c, bi->bi_atime),
56                 .st_mtim        = bch2_time_to_timespec(c, bi->bi_mtime),
57                 .st_ctim        = bch2_time_to_timespec(c, bi->bi_ctime),
58         };
59 }
60
61 static struct fuse_entry_param inode_to_entry(struct bch_fs *c,
62                                               struct bch_inode_unpacked *bi)
63 {
64         return (struct fuse_entry_param) {
65                 .ino            = bi->bi_inum,
66                 .generation     = bi->bi_generation,
67                 .attr           = inode_to_stat(c, bi),
68                 .attr_timeout   = DBL_MAX,
69                 .entry_timeout  = DBL_MAX,
70         };
71 }
72
73 static void bcachefs_fuse_init(void *arg, struct fuse_conn_info *conn)
74 {
75         conn->want |= FUSE_CAP_WRITEBACK_CACHE;
76
77         //conn->want |= FUSE_CAP_POSIX_ACL;
78 }
79
80 static void bcachefs_fuse_destroy(void *arg)
81 {
82         struct bch_fs *c = arg;
83
84         bch2_fs_stop(c);
85 }
86
87 static void bcachefs_fuse_lookup(fuse_req_t req, fuse_ino_t dir,
88                                  const char *name)
89 {
90         struct bch_fs *c = fuse_req_userdata(req);
91         struct bch_inode_unpacked bi;
92         struct qstr qstr = QSTR(name);
93         u64 inum;
94         int ret;
95
96         dir = map_root_ino(dir);
97
98         ret = bch2_inode_find_by_inum(c, dir, &bi);
99         if (ret) {
100                 fuse_reply_err(req, -ret);
101                 return;
102         }
103
104         struct bch_hash_info hash_info = bch2_hash_info_init(c, &bi);
105
106         inum = bch2_dirent_lookup(c, dir, &hash_info, &qstr);
107         if (!inum) {
108                 ret = -ENOENT;
109                 goto err;
110         }
111
112         ret = bch2_inode_find_by_inum(c, inum, &bi);
113         if (ret)
114                 goto err;
115
116         bi.bi_inum = unmap_root_ino(bi.bi_inum);
117
118         struct fuse_entry_param e = inode_to_entry(c, &bi);
119         fuse_reply_entry(req, &e);
120         return;
121 err:
122         fuse_reply_err(req, -ret);
123 }
124
125 static void bcachefs_fuse_getattr(fuse_req_t req, fuse_ino_t inum,
126                                   struct fuse_file_info *fi)
127 {
128         struct bch_fs *c = fuse_req_userdata(req);
129         struct bch_inode_unpacked bi;
130         struct stat attr;
131         int ret;
132
133         inum = map_root_ino(inum);
134
135         ret = bch2_inode_find_by_inum(c, inum, &bi);
136         if (ret) {
137                 fuse_reply_err(req, -ret);
138                 return;
139         }
140
141         bi.bi_inum = unmap_root_ino(bi.bi_inum);
142
143         attr = inode_to_stat(c, &bi);
144         fuse_reply_attr(req, &attr, DBL_MAX);
145 }
146
147 static void bcachefs_fuse_setattr(fuse_req_t req, fuse_ino_t inum,
148                                   struct stat *attr, int to_set,
149                                   struct fuse_file_info *fi)
150 {
151         struct bch_fs *c = fuse_req_userdata(req);
152         struct bch_inode_unpacked inode_u;
153         struct btree_trans trans;
154         struct btree_iter *iter;
155         u64 now;
156         int ret;
157
158         inum = map_root_ino(inum);
159
160         bch2_trans_init(&trans, c, 0, 0);
161 retry:
162         bch2_trans_begin(&trans);
163         now = bch2_current_time(c);
164
165         iter = bch2_inode_peek(&trans, &inode_u, inum, BTREE_ITER_INTENT);
166         ret = PTR_ERR_OR_ZERO(iter);
167         if (ret)
168                 goto err;
169
170         if (to_set & FUSE_SET_ATTR_MODE)
171                 inode_u.bi_mode = attr->st_mode;
172         if (to_set & FUSE_SET_ATTR_UID)
173                 inode_u.bi_uid  = attr->st_uid;
174         if (to_set & FUSE_SET_ATTR_GID)
175                 inode_u.bi_gid  = attr->st_gid;
176         if (to_set & FUSE_SET_ATTR_SIZE)
177                 inode_u.bi_size = attr->st_size;
178         if (to_set & FUSE_SET_ATTR_ATIME)
179                 inode_u.bi_atime = timespec_to_bch2_time(c, attr->st_atim);
180         if (to_set & FUSE_SET_ATTR_MTIME)
181                 inode_u.bi_mtime = timespec_to_bch2_time(c, attr->st_mtim);
182         if (to_set & FUSE_SET_ATTR_ATIME_NOW)
183                 inode_u.bi_atime = now;
184         if (to_set & FUSE_SET_ATTR_MTIME_NOW)
185                 inode_u.bi_mtime = now;
186
187         ret   = bch2_inode_write(&trans, iter, &inode_u) ?:
188                 bch2_trans_commit(&trans, NULL, NULL,
189                                   BTREE_INSERT_ATOMIC|
190                                   BTREE_INSERT_NOFAIL);
191 err:
192         if (ret == -EINTR)
193                 goto retry;
194
195         bch2_trans_exit(&trans);
196
197         if (!ret) {
198                 *attr = inode_to_stat(c, &inode_u);
199                 fuse_reply_attr(req, attr, DBL_MAX);
200         } else {
201                 fuse_reply_err(req, -ret);
202         }
203 }
204
205 static void bcachefs_fuse_readlink(fuse_req_t req, fuse_ino_t inum)
206 {
207         //struct bch_fs *c = fuse_req_userdata(req);
208
209         //char *link = malloc();
210
211         //fuse_reply_readlink(req, link);
212 }
213
214 static int do_create(struct bch_fs *c, u64 dir,
215                      const char *name, mode_t mode, dev_t rdev,
216                      struct bch_inode_unpacked *new_inode)
217 {
218         struct qstr qstr = QSTR(name);
219         struct bch_inode_unpacked dir_u;
220
221         dir = map_root_ino(dir);
222
223         bch2_inode_init_early(c, new_inode);
224
225         return bch2_trans_do(c, NULL, BTREE_INSERT_ATOMIC,
226                         bch2_create_trans(&trans,
227                                 dir, &dir_u,
228                                 new_inode, &qstr,
229                                 0, 0, mode, rdev, NULL, NULL));
230 }
231
232 static void bcachefs_fuse_mknod(fuse_req_t req, fuse_ino_t dir,
233                                 const char *name, mode_t mode,
234                                 dev_t rdev)
235 {
236         struct bch_fs *c = fuse_req_userdata(req);
237         struct bch_inode_unpacked new_inode;
238         int ret;
239
240         ret = do_create(c, dir, name, mode, rdev, &new_inode);
241         if (ret)
242                 goto err;
243
244         struct fuse_entry_param e = inode_to_entry(c, &new_inode);
245         fuse_reply_entry(req, &e);
246         return;
247 err:
248         fuse_reply_err(req, -ret);
249 }
250
251 static void bcachefs_fuse_mkdir(fuse_req_t req, fuse_ino_t dir,
252                                 const char *name, mode_t mode)
253 {
254         bcachefs_fuse_mknod(req, dir, name, mode, 0);
255 }
256
257 static void bcachefs_fuse_unlink(fuse_req_t req, fuse_ino_t dir,
258                                  const char *name)
259 {
260         struct bch_fs *c = fuse_req_userdata(req);
261         struct bch_inode_unpacked dir_u, inode_u;
262         struct qstr qstr = QSTR(name);
263         int ret;
264
265         dir = map_root_ino(dir);
266
267         ret = bch2_trans_do(c, NULL, BTREE_INSERT_ATOMIC|BTREE_INSERT_NOFAIL,
268                             bch2_unlink_trans(&trans, dir, &dir_u,
269                                               &inode_u, &qstr));
270
271         fuse_reply_err(req, -ret);
272 }
273
274 static void bcachefs_fuse_rmdir(fuse_req_t req, fuse_ino_t dir,
275                                 const char *name)
276 {
277         dir = map_root_ino(dir);
278
279         bcachefs_fuse_unlink(req, dir, name);
280 }
281
282 #if 0
283 static void bcachefs_fuse_symlink(fuse_req_t req, const char *link,
284                                   fuse_ino_t parent, const char *name)
285 {
286         struct bch_fs *c = fuse_req_userdata(req);
287 }
288 #endif
289
290 static void bcachefs_fuse_rename(fuse_req_t req,
291                                  fuse_ino_t src_dir, const char *srcname,
292                                  fuse_ino_t dst_dir, const char *dstname,
293                                  unsigned flags)
294 {
295         struct bch_fs *c = fuse_req_userdata(req);
296         struct bch_inode_unpacked dst_dir_u, src_dir_u;
297         struct bch_inode_unpacked src_inode_u, dst_inode_u;
298         struct qstr dst_name = QSTR(srcname);
299         struct qstr src_name = QSTR(dstname);
300         int ret;
301
302         src_dir = map_root_ino(src_dir);
303         dst_dir = map_root_ino(dst_dir);
304
305         /* XXX handle overwrites */
306         ret = bch2_trans_do(c, NULL, BTREE_INSERT_ATOMIC,
307                 bch2_rename_trans(&trans,
308                                   src_dir, &src_dir_u,
309                                   dst_dir, &dst_dir_u,
310                                   &src_inode_u, &dst_inode_u,
311                                   &src_name, &dst_name,
312                                   BCH_RENAME));
313
314         fuse_reply_err(req, -ret);
315 }
316
317 static void bcachefs_fuse_link(fuse_req_t req, fuse_ino_t inum,
318                                fuse_ino_t newparent, const char *newname)
319 {
320         struct bch_fs *c = fuse_req_userdata(req);
321         struct bch_inode_unpacked inode_u;
322         struct qstr qstr = QSTR(newname);
323         int ret;
324
325         ret = bch2_trans_do(c, NULL, BTREE_INSERT_ATOMIC,
326                             bch2_link_trans(&trans, newparent,
327                                             inum, &inode_u, &qstr));
328
329         if (!ret) {
330                 struct fuse_entry_param e = inode_to_entry(c, &inode_u);
331                 fuse_reply_entry(req, &e);
332         } else {
333                 fuse_reply_err(req, -ret);
334         }
335 }
336
337 static void bcachefs_fuse_open(fuse_req_t req, fuse_ino_t inum,
338                                struct fuse_file_info *fi)
339 {
340         fi->direct_io           = false;
341         fi->keep_cache          = true;
342         fi->cache_readdir       = true;
343
344         fuse_reply_open(req, fi);
345 }
346
347 static void userbio_init(struct bio *bio, struct bio_vec *bv,
348                          void *buf, size_t size)
349 {
350         bio_init(bio, bv, 1);
351         bio->bi_iter.bi_size    = size;
352         bv->bv_page             = buf;
353         bv->bv_len              = size;
354         bv->bv_offset           = 0;
355 }
356
357 static int get_inode_io_opts(struct bch_fs *c, u64 inum,
358                              struct bch_io_opts *opts)
359 {
360         struct bch_inode_unpacked inode;
361         if (bch2_inode_find_by_inum(c, inum, &inode))
362                 return -EINVAL;
363
364         *opts = bch2_opts_to_inode_opts(c->opts);
365         bch2_io_opts_apply(opts, bch2_inode_opts_get(&inode));
366         return 0;
367 }
368
369 static void bcachefs_fuse_read_endio(struct bio *bio)
370 {
371         closure_put(bio->bi_private);
372 }
373
374 static void bcachefs_fuse_read(fuse_req_t req, fuse_ino_t inum,
375                                size_t size, off_t offset,
376                                struct fuse_file_info *fi)
377 {
378         struct bch_fs *c = fuse_req_userdata(req);
379
380         if ((size|offset) & block_bytes(c)) {
381                 fuse_reply_err(req, EINVAL);
382                 return;
383         }
384
385         struct bch_io_opts io_opts;
386         if (get_inode_io_opts(c, inum, &io_opts)) {
387                 fuse_reply_err(req, ENOENT);
388                 return;
389         }
390
391         void *buf = aligned_alloc(max(PAGE_SIZE, size), size);
392         if (!buf) {
393                 fuse_reply_err(req, ENOMEM);
394                 return;
395         }
396
397         struct bch_read_bio     rbio;
398         struct bio_vec          bv;
399         struct closure          cl;
400
401         closure_init_stack(&cl);
402         userbio_init(&rbio.bio, &bv, buf, size);
403         bio_set_op_attrs(&rbio.bio, REQ_OP_READ, REQ_SYNC);
404         rbio.bio.bi_iter.bi_sector      = offset >> 9;
405         rbio.bio.bi_end_io              = bcachefs_fuse_read_endio;
406         rbio.bio.bi_private             = &cl;
407
408         bch2_read(c, rbio_init(&rbio.bio, io_opts), inum);
409
410         closure_sync(&cl);
411
412         if (likely(!rbio.bio.bi_status)) {
413                 fuse_reply_buf(req, buf, size);
414         } else {
415                 fuse_reply_err(req, -blk_status_to_errno(rbio.bio.bi_status));
416         }
417
418         free(buf);
419 }
420
421 static void bcachefs_fuse_write(fuse_req_t req, fuse_ino_t inum,
422                                 const char *buf, size_t size,
423                                 off_t offset,
424                                 struct fuse_file_info *fi)
425 {
426         struct bch_fs *c        = fuse_req_userdata(req);
427         struct bch_io_opts      io_opts;
428         struct bch_write_op     op;
429         struct bio_vec          bv;
430         struct closure          cl;
431
432         if ((size|offset) & block_bytes(c)) {
433                 fuse_reply_err(req, EINVAL);
434                 return;
435         }
436
437         closure_init_stack(&cl);
438
439         if (get_inode_io_opts(c, inum, &io_opts)) {
440                 fuse_reply_err(req, ENOENT);
441                 return;
442         }
443
444         bch2_write_op_init(&op, c, io_opts);
445         op.write_point  = writepoint_hashed(0);
446         op.nr_replicas  = io_opts.data_replicas;
447         op.target       = io_opts.foreground_target;
448
449         userbio_init(&op.wbio.bio, &bv, (void *) buf, size);
450         bio_set_op_attrs(&op.wbio.bio, REQ_OP_WRITE, REQ_SYNC);
451         op.wbio.bio.bi_iter.bi_sector = offset >> 9;
452
453         if (bch2_disk_reservation_get(c, &op.res, size >> 9,
454                                       op.nr_replicas, 0)) {
455                 /* XXX: use check_range_allocated like dio write path */
456                 fuse_reply_err(req, ENOSPC);
457                 return;
458         }
459
460         closure_call(&op.cl, bch2_write, NULL, &cl);
461         closure_sync(&cl);
462
463         if (op.written) {
464                 fuse_reply_write(req, (size_t) op.written << 9);
465         } else {
466                 BUG_ON(!op.error);
467                 fuse_reply_err(req, -op.error);
468         }
469 }
470
471 #if 0
472 static void bcachefs_fuse_flush(fuse_req_t req, fuse_ino_t inum,
473                                 struct fuse_file_info *fi)
474 {
475         struct bch_fs *c = fuse_req_userdata(req);
476 }
477
478 static void bcachefs_fuse_release(fuse_req_t req, fuse_ino_t inum,
479                                   struct fuse_file_info *fi)
480 {
481         struct bch_fs *c = fuse_req_userdata(req);
482 }
483
484 static void bcachefs_fuse_fsync(fuse_req_t req, fuse_ino_t inum, int datasync,
485                                 struct fuse_file_info *fi)
486 {
487         struct bch_fs *c = fuse_req_userdata(req);
488 }
489
490 static void bcachefs_fuse_opendir(fuse_req_t req, fuse_ino_t inum,
491                                   struct fuse_file_info *fi)
492 {
493         struct bch_fs *c = fuse_req_userdata(req);
494 }
495 #endif
496
497 struct fuse_dir_entry {
498         u64             ino;
499         unsigned        type;
500         char            name[0];
501 };
502
503 struct fuse_dir_context {
504         struct dir_context      ctx;
505         fuse_req_t              req;
506         char                    *buf;
507         size_t                  bufsize;
508
509         struct fuse_dir_entry   *prev;
510 };
511
512 static int fuse_send_dir_entry(struct fuse_dir_context *ctx, loff_t pos)
513 {
514         struct fuse_dir_entry *de = ctx->prev;
515         ctx->prev = NULL;
516
517         struct stat statbuf = {
518                 .st_ino         = unmap_root_ino(de->ino),
519                 .st_mode        = de->type << 12,
520         };
521
522         size_t len = fuse_add_direntry(ctx->req, ctx->buf, ctx->bufsize,
523                                        de->name, &statbuf, pos);
524
525         free(de);
526
527         if (len > ctx->bufsize)
528                 return -EINVAL;
529
530         ctx->buf        += len;
531         ctx->bufsize    -= len;
532
533         return 0;
534 }
535
536 static int fuse_filldir(struct dir_context *_ctx,
537                         const char *name, int namelen,
538                         loff_t pos, u64 ino, unsigned type)
539 {
540         struct fuse_dir_context *ctx =
541                 container_of(_ctx, struct fuse_dir_context, ctx);
542
543         fuse_log(FUSE_LOG_DEBUG, "fuse_filldir(ctx={.ctx={.pos=%llu}}, "
544                  "name=%s, namelen=%d, pos=%lld, dir=%llu, type=%u)\n",
545                  ctx->ctx.pos, name, namelen, pos, ino, type);
546
547         /*
548          * We have to emit directory entries after reading the next entry,
549          * because the previous entry contains a pointer to next.
550          */
551         if (ctx->prev) {
552                 int ret = fuse_send_dir_entry(ctx, pos);
553                 if (ret)
554                         return ret;
555         }
556
557         struct fuse_dir_entry *cur = malloc(sizeof *cur + namelen + 1);
558         cur->ino = ino;
559         cur->type = type;
560         memcpy(cur->name, name, namelen);
561         cur->name[namelen] = 0;
562
563         ctx->prev = cur;
564
565         return 0;
566 }
567
568 static bool handle_dots(struct fuse_dir_context *ctx, fuse_ino_t dir)
569 {
570         int ret = 0;
571
572         if (ctx->ctx.pos == 0) {
573                 ret = fuse_filldir(&ctx->ctx, ".", 1, ctx->ctx.pos,
574                                    unmap_root_ino(dir), DT_DIR);
575                 if (ret < 0)
576                         return false;
577                 ctx->ctx.pos = 1;
578         }
579
580         if (ctx->ctx.pos == 1) {
581                 ret = fuse_filldir(&ctx->ctx, "..", 2, ctx->ctx.pos,
582                                    /*TODO: parent*/ 1, DT_DIR);
583                 if (ret < 0)
584                         return false;
585                 ctx->ctx.pos = 2;
586         }
587
588         return true;
589 }
590
591 static void bcachefs_fuse_readdir(fuse_req_t req, fuse_ino_t dir,
592                                   size_t size, off_t off,
593                                   struct fuse_file_info *fi)
594 {
595         struct bch_fs *c = fuse_req_userdata(req);
596         struct bch_inode_unpacked bi;
597         char *buf = calloc(size, 1);
598         struct fuse_dir_context ctx = {
599                 .ctx.actor      = fuse_filldir,
600                 .ctx.pos        = off,
601                 .req            = req,
602                 .buf            = buf,
603                 .bufsize        = size,
604         };
605         int ret = 0;
606
607         fuse_log(FUSE_LOG_DEBUG, "bcachefs_fuse_readdir(dir=%llu, size=%zu, "
608                  "off=%lld)\n", dir, size, off);
609
610         dir = map_root_ino(dir);
611
612         ret = bch2_inode_find_by_inum(c, dir, &bi);
613         if (ret)
614                 goto reply;
615
616         if (!S_ISDIR(bi.bi_mode)) {
617                 ret = -ENOTDIR;
618                 goto reply;
619         }
620
621         if (!handle_dots(&ctx, dir))
622                 goto reply;
623
624         ret = bch2_readdir(c, dir, &ctx.ctx);
625
626 reply:
627         /*
628          * If we have something to send, the error above doesn't matter.
629          *
630          * Alternatively, if this send fails, but we previously sent something,
631          * then this is a success.
632          */
633         if (ctx.prev) {
634                 ret = fuse_send_dir_entry(&ctx, ctx.ctx.pos);
635                 if (ret && ctx.buf != buf)
636                         ret = 0;
637         }
638
639         if (!ret) {
640                 fuse_log(FUSE_LOG_DEBUG, "bcachefs_fuse_readdir reply %zd\n",
641                                         ctx.buf - buf);
642                 fuse_reply_buf(req, buf, ctx.buf - buf);
643         } else {
644                 fuse_reply_err(req, -ret);
645         }
646
647         free(buf);
648 }
649
650 #if 0
651 static void bcachefs_fuse_readdirplus(fuse_req_t req, fuse_ino_t dir,
652                                       size_t size, off_t off,
653                                       struct fuse_file_info *fi)
654 {
655
656 }
657
658 static void bcachefs_fuse_releasedir(fuse_req_t req, fuse_ino_t inum,
659                                      struct fuse_file_info *fi)
660 {
661         struct bch_fs *c = fuse_req_userdata(req);
662 }
663
664 static void bcachefs_fuse_fsyncdir(fuse_req_t req, fuse_ino_t inum, int datasync,
665                                    struct fuse_file_info *fi)
666 {
667         struct bch_fs *c = fuse_req_userdata(req);
668 }
669 #endif
670
671 static void bcachefs_fuse_statfs(fuse_req_t req, fuse_ino_t inum)
672 {
673         struct bch_fs *c = fuse_req_userdata(req);
674         struct bch_fs_usage_short usage = bch2_fs_usage_read_short(c);
675         unsigned shift = c->block_bits;
676         struct statvfs statbuf = {
677                 .f_bsize        = block_bytes(c),
678                 .f_frsize       = block_bytes(c),
679                 .f_blocks       = usage.capacity >> shift,
680                 .f_bfree        = (usage.capacity - usage.used) >> shift,
681                 //.f_bavail     = statbuf.f_bfree,
682                 .f_files        = usage.nr_inodes,
683                 .f_ffree        = U64_MAX,
684                 .f_namemax      = BCH_NAME_MAX,
685         };
686
687         fuse_reply_statfs(req, &statbuf);
688 }
689
690 #if 0
691 static void bcachefs_fuse_setxattr(fuse_req_t req, fuse_ino_t inum,
692                                    const char *name, const char *value,
693                                    size_t size, int flags)
694 {
695         struct bch_fs *c = fuse_req_userdata(req);
696 }
697
698 static void bcachefs_fuse_getxattr(fuse_req_t req, fuse_ino_t inum,
699                                    const char *name, size_t size)
700 {
701         struct bch_fs *c = fuse_req_userdata(req);
702
703         fuse_reply_xattr(req, );
704 }
705
706 static void bcachefs_fuse_listxattr(fuse_req_t req, fuse_ino_t inum, size_t size)
707 {
708         struct bch_fs *c = fuse_req_userdata(req);
709 }
710
711 static void bcachefs_fuse_removexattr(fuse_req_t req, fuse_ino_t inum,
712                                       const char *name)
713 {
714         struct bch_fs *c = fuse_req_userdata(req);
715 }
716 #endif
717
718 static void bcachefs_fuse_create(fuse_req_t req, fuse_ino_t dir,
719                                  const char *name, mode_t mode,
720                                  struct fuse_file_info *fi)
721 {
722         struct bch_fs *c = fuse_req_userdata(req);
723         struct bch_inode_unpacked new_inode;
724         int ret;
725
726         ret = do_create(c, dir, name, mode, 0, &new_inode);
727         if (ret)
728                 goto err;
729
730         struct fuse_entry_param e = inode_to_entry(c, &new_inode);
731         fuse_reply_create(req, &e, fi);
732         return;
733 err:
734         fuse_reply_err(req, -ret);
735
736 }
737
738 #if 0
739 static void bcachefs_fuse_write_buf(fuse_req_t req, fuse_ino_t inum,
740                                     struct fuse_bufvec *bufv, off_t off,
741                                     struct fuse_file_info *fi)
742 {
743         struct bch_fs *c = fuse_req_userdata(req);
744 }
745
746 static void bcachefs_fuse_fallocate(fuse_req_t req, fuse_ino_t inum, int mode,
747                                     off_t offset, off_t length,
748                                     struct fuse_file_info *fi)
749 {
750         struct bch_fs *c = fuse_req_userdata(req);
751 }
752 #endif
753
754 static const struct fuse_lowlevel_ops bcachefs_fuse_ops = {
755         .init           = bcachefs_fuse_init,
756         .destroy        = bcachefs_fuse_destroy,
757         .lookup         = bcachefs_fuse_lookup,
758         .getattr        = bcachefs_fuse_getattr,
759         .setattr        = bcachefs_fuse_setattr,
760         .readlink       = bcachefs_fuse_readlink,
761         .mknod          = bcachefs_fuse_mknod,
762         .mkdir          = bcachefs_fuse_mkdir,
763         .unlink         = bcachefs_fuse_unlink,
764         .rmdir          = bcachefs_fuse_rmdir,
765         //.symlink      = bcachefs_fuse_symlink,
766         .rename         = bcachefs_fuse_rename,
767         .link           = bcachefs_fuse_link,
768         .open           = bcachefs_fuse_open,
769         .read           = bcachefs_fuse_read,
770         .write          = bcachefs_fuse_write,
771         //.flush        = bcachefs_fuse_flush,
772         //.release      = bcachefs_fuse_release,
773         //.fsync        = bcachefs_fuse_fsync,
774         //.opendir      = bcachefs_fuse_opendir,
775         .readdir        = bcachefs_fuse_readdir,
776         //.readdirplus  = bcachefs_fuse_readdirplus,
777         //.releasedir   = bcachefs_fuse_releasedir,
778         //.fsyncdir     = bcachefs_fuse_fsyncdir,
779         .statfs         = bcachefs_fuse_statfs,
780         //.setxattr     = bcachefs_fuse_setxattr,
781         //.getxattr     = bcachefs_fuse_getxattr,
782         //.listxattr    = bcachefs_fuse_listxattr,
783         //.removexattr  = bcachefs_fuse_removexattr,
784         .create         = bcachefs_fuse_create,
785
786         /* posix locks: */
787 #if 0
788         .getlk          = bcachefs_fuse_getlk,
789         .setlk          = bcachefs_fuse_setlk,
790 #endif
791         //.write_buf    = bcachefs_fuse_write_buf,
792         //.fallocate    = bcachefs_fuse_fallocate,
793
794 };
795
796 /*
797  * Setup and command parsing.
798  */
799
800 struct bf_context {
801         char            *devices_str;
802         char            **devices;
803         int             nr_devices;
804 };
805
806 static void bf_context_free(struct bf_context *ctx)
807 {
808         int i;
809
810         free(ctx->devices_str);
811         for (i = 0; i < ctx->nr_devices; ++i)
812                 free(ctx->devices[i]);
813         free(ctx->devices);
814 }
815
816 static struct fuse_opt bf_opts[] = {
817         FUSE_OPT_END
818 };
819
820 /*
821  * Fuse option parsing helper -- returning 0 means we consumed the argument, 1
822  * means we did not.
823  */
824 static int bf_opt_proc(void *data, const char *arg, int key,
825     struct fuse_args *outargs)
826 {
827         struct bf_context *ctx = data;
828
829         switch (key) {
830         case FUSE_OPT_KEY_NONOPT:
831                 /* Just extract the first non-option string. */
832                 if (!ctx->devices_str) {
833                         ctx->devices_str = strdup(arg);
834                         return 0;
835                 }
836                 return 1;
837         }
838
839         return 1;
840 }
841
842 /*
843  * dev1:dev2 -> [ dev1, dev2 ]
844  * dev       -> [ dev ]
845  */
846 static void tokenize_devices(struct bf_context *ctx)
847 {
848         char *devices_str = strdup(ctx->devices_str);
849         char *devices_tmp = devices_str;
850         char **devices = NULL;
851         int nr = 0;
852         char *dev = NULL;
853
854         while ((dev = strsep(&devices_tmp, ":"))) {
855                 if (strlen(dev) > 0) {
856                         devices = realloc(devices, (nr + 1) * sizeof *devices);
857                         devices[nr] = strdup(dev);
858                         nr++;
859                 }
860         }
861
862         if (!devices) {
863                 devices = malloc(sizeof *devices);
864                 devices[0] = strdup(ctx->devices_str);
865                 nr = 1;
866         }
867
868         ctx->devices = devices;
869         ctx->nr_devices = nr;
870
871         free(devices_str);
872 }
873
874 static void usage(char *argv[])
875 {
876         printf("Usage: %s fusemount [options] <dev>[:dev2:...] <mountpoint>\n",
877                argv[0]);
878         printf("\n");
879 }
880
881 int cmd_fusemount(int argc, char *argv[])
882 {
883         struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
884         struct bch_opts bch_opts = bch2_opts_empty();
885         struct bf_context ctx = { 0 };
886         struct bch_fs *c = NULL;
887         int ret = 0, i;
888
889         /* Parse arguments. */
890         if (fuse_opt_parse(&args, &ctx, bf_opts, bf_opt_proc) < 0)
891                 die("fuse_opt_parse err: %m");
892
893         struct fuse_cmdline_opts fuse_opts;
894         if (fuse_parse_cmdline(&args, &fuse_opts) < 0)
895                 die("fuse_parse_cmdline err: %m");
896
897         if (fuse_opts.show_help) {
898                 usage(argv);
899                 fuse_cmdline_help();
900                 fuse_lowlevel_help();
901                 ret = 0;
902                 goto out;
903         }
904         if (fuse_opts.show_version) {
905                 /* TODO: Show bcachefs version. */
906                 printf("FUSE library version %s\n", fuse_pkgversion());
907                 fuse_lowlevel_version();
908                 ret = 0;
909                 goto out;
910         }
911         if (!fuse_opts.mountpoint) {
912                 usage(argv);
913                 printf("Please supply a mountpoint.\n");
914                 ret = 1;
915                 goto out;
916         }
917         if (!ctx.devices_str) {
918                 usage(argv);
919                 printf("Please specify a device or device1:device2:...\n");
920                 ret = 1;
921                 goto out;
922         }
923         tokenize_devices(&ctx);
924
925         /* Open bch */
926         printf("Opening bcachefs filesystem on:\n");
927         for (i = 0; i < ctx.nr_devices; ++i)
928                 printf("\t%s\n", ctx.devices[i]);
929
930         c = bch2_fs_open(ctx.devices, ctx.nr_devices, bch_opts);
931         if (IS_ERR(c))
932                 die("error opening %s: %s", ctx.devices_str,
933                     strerror(-PTR_ERR(c)));
934
935         /* Fuse */
936         struct fuse_session *se =
937                 fuse_session_new(&args, &bcachefs_fuse_ops,
938                                  sizeof(bcachefs_fuse_ops), c);
939         if (!se)
940                 die("fuse_lowlevel_new err: %m");
941
942         if (fuse_set_signal_handlers(se) < 0)
943                 die("fuse_set_signal_handlers err: %m");
944
945         if (fuse_session_mount(se, fuse_opts.mountpoint))
946                 die("fuse_mount err: %m");
947
948         fuse_daemonize(fuse_opts.foreground);
949
950         ret = fuse_session_loop(se);
951
952         /* Cleanup */
953         fuse_session_unmount(se);
954         fuse_remove_signal_handlers(se);
955         fuse_session_destroy(se);
956
957 out:
958         free(fuse_opts.mountpoint);
959         fuse_opt_free_args(&args);
960         bf_context_free(&ctx);
961
962         return ret ? 1 : 0;
963 }