libfuse: example/passthrough_ll.c Source File

libfuse

passthrough_ll.c
Go to the documentation of this file.
1 /*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7 */
8
37 #define _GNU_SOURCE
38 #define FUSE_USE_VERSION 31
39
40 #include "config.h"
41
42 #include <fuse_lowlevel.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <stddef.h>
47 #include <stdbool.h>
48 #include <string.h>
49 #include <limits.h>
50 #include <dirent.h>
51 #include <assert.h>
52 #include <errno.h>
53 #include <err.h>
54 #include <inttypes.h>
55 #include <pthread.h>
56 #include <sys/file.h>
57 #include <sys/xattr.h>
58
59 /* We are re-using pointers to our `struct lo_inode` and `struct
60 lo_dirp` elements as inodes. This means that we must be able to
61 store uintptr_t values in a fuse_ino_t variable. The following
62 incantation checks this condition at compile time. */
63 #if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
64 _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
65 "fuse_ino_t too small to hold uintptr_t values!");
66 #else
67 struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
68 { unsigned _uintptr_to_must_hold_fuse_ino_t:
69 ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
70 #endif
71
72 struct lo_inode {
73 struct lo_inode *next; /* protected by lo->mutex */
74 struct lo_inode *prev; /* protected by lo->mutex */
75 int fd;
76 bool is_symlink;
77 ino_t ino;
78 dev_t dev;
79 uint64_t refcount; /* protected by lo->mutex */
80 };
81
82 enum {
83 CACHE_NEVER,
84 CACHE_NORMAL,
85 CACHE_ALWAYS,
86 };
87
88 struct lo_data {
89 pthread_mutex_t mutex;
90 int debug;
91 int writeback;
92 int flock;
93 int xattr;
94 const char *source;
95 double timeout;
96 int cache;
97 int timeout_set;
98 struct lo_inode root; /* protected by lo->mutex */
99 };
100
101 static const struct fuse_opt lo_opts[] = {
102 { "writeback",
103 offsetof(struct lo_data, writeback), 1 },
104 { "no_writeback",
105 offsetof(struct lo_data, writeback), 0 },
106 { "source=%s",
107 offsetof(struct lo_data, source), 0 },
108 { "flock",
109 offsetof(struct lo_data, flock), 1 },
110 { "no_flock",
111 offsetof(struct lo_data, flock), 0 },
112 { "xattr",
113 offsetof(struct lo_data, xattr), 1 },
114 { "no_xattr",
115 offsetof(struct lo_data, xattr), 0 },
116 { "timeout=%lf",
117 offsetof(struct lo_data, timeout), 0 },
118 { "timeout=",
119 offsetof(struct lo_data, timeout_set), 1 },
120 { "cache=never",
121 offsetof(struct lo_data, cache), CACHE_NEVER },
122 { "cache=auto",
123 offsetof(struct lo_data, cache), CACHE_NORMAL },
124 { "cache=always",
125 offsetof(struct lo_data, cache), CACHE_ALWAYS },
126
127 FUSE_OPT_END
128 };
129
130 static struct lo_data *lo_data(fuse_req_t req)
131 {
132 return (struct lo_data *) fuse_req_userdata(req);
133 }
134
135 static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
136 {
137 if (ino == FUSE_ROOT_ID)
138 return &lo_data(req)->root;
139 else
140 return (struct lo_inode *) (uintptr_t) ino;
141 }
142
143 static int lo_fd(fuse_req_t req, fuse_ino_t ino)
144 {
145 return lo_inode(req, ino)->fd;
146 }
147
148 static bool lo_debug(fuse_req_t req)
149 {
150 return lo_data(req)->debug != 0;
151 }
152
153 static void lo_init(void *userdata,
154 struct fuse_conn_info *conn)
155 {
156 struct lo_data *lo = (struct lo_data*) userdata;
157
158 if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
159 conn->want |= FUSE_CAP_EXPORT_SUPPORT;
160
161 if (lo->writeback &&
162 conn->capable & FUSE_CAP_WRITEBACK_CACHE) {
163 if (lo->debug)
164 fprintf(stderr, "lo_init: activating writeback\n");
165 conn->want |= FUSE_CAP_WRITEBACK_CACHE;
166 }
167 if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
168 if (lo->debug)
169 fprintf(stderr, "lo_init: activating flock locks\n");
170 conn->want |= FUSE_CAP_FLOCK_LOCKS;
171 }
172 }
173
174 static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
175 struct fuse_file_info *fi)
176 {
177 int res;
178 struct stat buf;
179 struct lo_data *lo = lo_data(req);
180
181 (void) fi;
182
183 res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
184 if (res == -1)
185 return (void) fuse_reply_err(req, errno);
186
187 fuse_reply_attr(req, &buf, lo->timeout);
188 }
189
190 static int utimensat_empty_nofollow(struct lo_inode *inode,
191 const struct timespec *tv)
192 {
193 int res;
194 char procname[64];
195
196 if (inode->is_symlink) {
197 res = utimensat(inode->fd, "", tv,
198 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
199 if (res == -1 && errno == EINVAL) {
200 /* Sorry, no race free way to set times on symlink. */
201 errno = EPERM;
202 }
203 return res;
204 }
205 sprintf(procname, "/proc/self/fd/%i", inode->fd);
206
207 return utimensat(AT_FDCWD, procname, tv, 0);
208 }
209
210 static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
211 int valid, struct fuse_file_info *fi)
212 {
213 int saverr;
214 char procname[64];
215 struct lo_inode *inode = lo_inode(req, ino);
216 int ifd = inode->fd;
217 int res;
218
219 if (valid & FUSE_SET_ATTR_MODE) {
220 if (fi) {
221 res = fchmod(fi->fh, attr->st_mode);
222 } else {
223 sprintf(procname, "/proc/self/fd/%i", ifd);
224 res = chmod(procname, attr->st_mode);
225 }
226 if (res == -1)
227 goto out_err;
228 }
229 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
230 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
231 attr->st_uid : (uid_t) -1;
232 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
233 attr->st_gid : (gid_t) -1;
234
235 res = fchownat(ifd, "", uid, gid,
236 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
237 if (res == -1)
238 goto out_err;
239 }
240 if (valid & FUSE_SET_ATTR_SIZE) {
241 if (fi) {
242 res = ftruncate(fi->fh, attr->st_size);
243 } else {
244 sprintf(procname, "/proc/self/fd/%i", ifd);
245 res = truncate(procname, attr->st_size);
246 }
247 if (res == -1)
248 goto out_err;
249 }
250 if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
251 struct timespec tv[2];
252
253 tv[0].tv_sec = 0;
254 tv[1].tv_sec = 0;
255 tv[0].tv_nsec = UTIME_OMIT;
256 tv[1].tv_nsec = UTIME_OMIT;
257
258 if (valid & FUSE_SET_ATTR_ATIME_NOW)
259 tv[0].tv_nsec = UTIME_NOW;
260 else if (valid & FUSE_SET_ATTR_ATIME)
261 tv[0] = attr->st_atim;
262
263 if (valid & FUSE_SET_ATTR_MTIME_NOW)
264 tv[1].tv_nsec = UTIME_NOW;
265 else if (valid & FUSE_SET_ATTR_MTIME)
266 tv[1] = attr->st_mtim;
267
268 if (fi)
269 res = futimens(fi->fh, tv);
270 else
271 res = utimensat_empty_nofollow(inode, tv);
272 if (res == -1)
273 goto out_err;
274 }
275
276 return lo_getattr(req, ino, fi);
277
278 out_err:
279 saverr = errno;
280 fuse_reply_err(req, saverr);
281 }
282
283 static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
284 {
285 struct lo_inode *p;
286 struct lo_inode *ret = NULL;
287
288 pthread_mutex_lock(&lo->mutex);
289 for (p = lo->root.next; p != &lo->root; p = p->next) {
290 if (p->ino == st->st_ino && p->dev == st->st_dev) {
291 assert(p->refcount > 0);
292 ret = p;
293 ret->refcount++;
294 break;
295 }
296 }
297 pthread_mutex_unlock(&lo->mutex);
298 return ret;
299 }
300
301 static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
302 struct fuse_entry_param *e)
303 {
304 int newfd;
305 int res;
306 int saverr;
307 struct lo_data *lo = lo_data(req);
308 struct lo_inode *inode;
309
310 memset(e, 0, sizeof(*e));
311 e->attr_timeout = lo->timeout;
312 e->entry_timeout = lo->timeout;
313
314 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
315 if (newfd == -1)
316 goto out_err;
317
318 res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
319 if (res == -1)
320 goto out_err;
321
322 inode = lo_find(lo_data(req), &e->attr);
323 if (inode) {
324 close(newfd);
325 newfd = -1;
326 } else {
327 struct lo_inode *prev, *next;
328
329 saverr = ENOMEM;
330 inode = calloc(1, sizeof(struct lo_inode));
331 if (!inode)
332 goto out_err;
333
334 inode->is_symlink = S_ISLNK(e->attr.st_mode);
335 inode->refcount = 1;
336 inode->fd = newfd;
337 inode->ino = e->attr.st_ino;
338 inode->dev = e->attr.st_dev;
339
340 pthread_mutex_lock(&lo->mutex);
341 prev = &lo->root;
342 next = prev->next;
343 next->prev = inode;
344 inode->next = next;
345 inode->prev = prev;
346 prev->next = inode;
347 pthread_mutex_unlock(&lo->mutex);
348 }
349 e->ino = (uintptr_t) inode;
350
351 if (lo_debug(req))
352 fprintf(stderr, " %lli/%s -> %lli\n",
353 (unsigned long long) parent, name, (unsigned long long) e->ino);
354
355 return 0;
356
357 out_err:
358 saverr = errno;
359 if (newfd != -1)
360 close(newfd);
361 return saverr;
362 }
363
364 static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
365 {
366 struct fuse_entry_param e;
367 int err;
368
369 if (lo_debug(req))
370 fprintf(stderr, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
371 parent, name);
372
373 err = lo_do_lookup(req, parent, name, &e);
374 if (err)
375 fuse_reply_err(req, err);
376 else
377 fuse_reply_entry(req, &e);
378 }
379
380 static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
381 const char *name, mode_t mode, dev_t rdev,
382 const char *link)
383 {
384 int newfd = -1;
385 int res;
386 int saverr;
387 struct lo_inode *inode;
388 struct lo_inode *dir = lo_inode(req, parent);
389 struct fuse_entry_param e;
390
391 saverr = ENOMEM;
392 inode = calloc(1, sizeof(struct lo_inode));
393 if (!inode)
394 goto out;
395
396 if (S_ISDIR(mode))
397 res = mkdirat(dir->fd, name, mode);
398 else if (S_ISLNK(mode))
399 res = symlinkat(link, dir->fd, name);
400 else
401 res = mknodat(dir->fd, name, mode, rdev);
402 saverr = errno;
403 if (res == -1)
404 goto out;
405
406 saverr = lo_do_lookup(req, parent, name, &e);
407 if (saverr)
408 goto out;
409
410 if (lo_debug(req))
411 fprintf(stderr, " %lli/%s -> %lli\n",
412 (unsigned long long) parent, name, (unsigned long long) e.ino);
413
414 fuse_reply_entry(req, &e);
415 return;
416
417 out:
418 if (newfd != -1)
419 close(newfd);
420 free(inode);
421 fuse_reply_err(req, saverr);
422 }
423
424 static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
425 const char *name, mode_t mode, dev_t rdev)
426 {
427 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
428 }
429
430 static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
431 mode_t mode)
432 {
433 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
434 }
435
436 static void lo_symlink(fuse_req_t req, const char *link,
437 fuse_ino_t parent, const char *name)
438 {
439 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
440 }
441
442 static int linkat_empty_nofollow(struct lo_inode *inode, int dfd,
443 const char *name)
444 {
445 int res;
446 char procname[64];
447
448 if (inode->is_symlink) {
449 res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH);
450 if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
451 /* Sorry, no race free way to hard-link a symlink. */
452 errno = EPERM;
453 }
454 return res;
455 }
456
457 sprintf(procname, "/proc/self/fd/%i", inode->fd);
458
459 return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
460 }
461
462 static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
463 const char *name)
464 {
465 int res;
466 struct lo_data *lo = lo_data(req);
467 struct lo_inode *inode = lo_inode(req, ino);
468 struct fuse_entry_param e;
469 int saverr;
470
471 memset(&e, 0, sizeof(struct fuse_entry_param));
472 e.attr_timeout = lo->timeout;
473 e.entry_timeout = lo->timeout;
474
475 res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
476 if (res == -1)
477 goto out_err;
478
479 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
480 if (res == -1)
481 goto out_err;
482
483 pthread_mutex_lock(&lo->mutex);
484 inode->refcount++;
485 pthread_mutex_unlock(&lo->mutex);
486 e.ino = (uintptr_t) inode;
487
488 if (lo_debug(req))
489 fprintf(stderr, " %lli/%s -> %lli\n",
490 (unsigned long long) parent, name,
491 (unsigned long long) e.ino);
492
493 fuse_reply_entry(req, &e);
494 return;
495
496 out_err:
497 saverr = errno;
498 fuse_reply_err(req, saverr);
499 }
500
501 static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
502 {
503 int res;
504
505 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
506
507 fuse_reply_err(req, res == -1 ? errno : 0);
508 }
509
510 static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
511 fuse_ino_t newparent, const char *newname,
512 unsigned int flags)
513 {
514 int res;
515
516 if (flags) {
517 fuse_reply_err(req, EINVAL);
518 return;
519 }
520
521 res = renameat(lo_fd(req, parent), name,
522 lo_fd(req, newparent), newname);
523
524 fuse_reply_err(req, res == -1 ? errno : 0);
525 }
526
527 static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
528 {
529 int res;
530
531 res = unlinkat(lo_fd(req, parent), name, 0);
532
533 fuse_reply_err(req, res == -1 ? errno : 0);
534 }
535
536 static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
537 {
538 if (!inode)
539 return;
540
541 pthread_mutex_lock(&lo->mutex);
542 assert(inode->refcount >= n);
543 inode->refcount -= n;
544 if (!inode->refcount) {
545 struct lo_inode *prev, *next;
546
547 prev = inode->prev;
548 next = inode->next;
549 next->prev = prev;
550 prev->next = next;
551
552 pthread_mutex_unlock(&lo->mutex);
553 close(inode->fd);
554 free(inode);
555
556 } else {
557 pthread_mutex_unlock(&lo->mutex);
558 }
559 }
560
561 static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
562 {
563 struct lo_data *lo = lo_data(req);
564 struct lo_inode *inode = lo_inode(req, ino);
565
566 if (lo_debug(req)) {
567 fprintf(stderr, " forget %lli %lli -%lli\n",
568 (unsigned long long) ino,
569 (unsigned long long) inode->refcount,
570 (unsigned long long) nlookup);
571 }
572
573 unref_inode(lo, inode, nlookup);
574 }
575
576 static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
577 {
578 lo_forget_one(req, ino, nlookup);
579 fuse_reply_none(req);
580 }
581
582 static void lo_forget_multi(fuse_req_t req, size_t count,
583 struct fuse_forget_data *forgets)
584 {
585 int i;
586
587 for (i = 0; i < count; i++)
588 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
589 fuse_reply_none(req);
590 }
591
592 static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
593 {
594 char buf[PATH_MAX + 1];
595 int res;
596
597 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
598 if (res == -1)
599 return (void) fuse_reply_err(req, errno);
600
601 if (res == sizeof(buf))
602 return (void) fuse_reply_err(req, ENAMETOOLONG);
603
604 buf[res] = '\0';
605
606 fuse_reply_readlink(req, buf);
607 }
608
609 struct lo_dirp {
610 int fd;
611 DIR *dp;
612 struct dirent *entry;
613 off_t offset;
614 };
615
616 static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
617 {
618 return (struct lo_dirp *) (uintptr_t) fi->fh;
619 }
620
621 static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
622 {
623 int error = ENOMEM;
624 struct lo_data *lo = lo_data(req);
625 struct lo_dirp *d = calloc(1, sizeof(struct lo_dirp));
626 if (d == NULL)
627 goto out_err;
628
629 d->fd = openat(lo_fd(req, ino), ".", O_RDONLY);
630 if (d->fd == -1)
631 goto out_errno;
632
633 d->dp = fdopendir(d->fd);
634 if (d->dp == NULL)
635 goto out_errno;
636
637 d->offset = 0;
638 d->entry = NULL;
639
640 fi->fh = (uintptr_t) d;
641 if (lo->cache == CACHE_ALWAYS)
642 fi->keep_cache = 1;
643 fuse_reply_open(req, fi);
644 return;
645
646 out_errno:
647 error = errno;
648 out_err:
649 if (d) {
650 if (d->fd != -1)
651 close(d->fd);
652 free(d);
653 }
654 fuse_reply_err(req, error);
655 }
656
657 static int is_dot_or_dotdot(const char *name)
658 {
659 return name[0] == '.' && (name[1] == '\0' ||
660 (name[1] == '.' && name[2] == '\0'));
661 }
662
663 static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
664 off_t offset, struct fuse_file_info *fi, int plus)
665 {
666 struct lo_dirp *d = lo_dirp(fi);
667 char *buf;
668 char *p;
669 size_t rem = size;
670 int err;
671
672 (void) ino;
673
674 buf = calloc(1, size);
675 if (!buf) {
676 err = ENOMEM;
677 goto error;
678 }
679 p = buf;
680
681 if (offset != d->offset) {
682 seekdir(d->dp, offset);
683 d->entry = NULL;
684 d->offset = offset;
685 }
686 while (1) {
687 size_t entsize;
688 off_t nextoff;
689 const char *name;
690
691 if (!d->entry) {
692 errno = 0;
693 d->entry = readdir(d->dp);
694 if (!d->entry) {
695 if (errno) { // Error
696 err = errno;
697 goto error;
698 } else { // End of stream
699 break;
700 }
701 }
702 }
703 nextoff = d->entry->d_off;
704 name = d->entry->d_name;
705 fuse_ino_t entry_ino = 0;
706 if (plus) {
707 struct fuse_entry_param e;
708 if (is_dot_or_dotdot(name)) {
709 e = (struct fuse_entry_param) {
710 .attr.st_ino = d->entry->d_ino,
711 .attr.st_mode = d->entry->d_type << 12,
712 };
713 } else {
714 err = lo_do_lookup(req, ino, name, &e);
715 if (err)
716 goto error;
717 entry_ino = e.ino;
718 }
719
720 entsize = fuse_add_direntry_plus(req, p, rem, name,
721 &e, nextoff);
722 } else {
723 struct stat st = {
724 .st_ino = d->entry->d_ino,
725 .st_mode = d->entry->d_type << 12,
726 };
727 entsize = fuse_add_direntry(req, p, rem, name,
728 &st, nextoff);
729 }
730 if (entsize > rem) {
731 if (entry_ino != 0)
732 lo_forget_one(req, entry_ino, 1);
733 break;
734 }
735
736 p += entsize;
737 rem -= entsize;
738
739 d->entry = NULL;
740 d->offset = nextoff;
741 }
742
743 err = 0;
744 error:
745 // If there's an error, we can only signal it if we haven't stored
746 // any entries yet - otherwise we'd end up with wrong lookup
747 // counts for the entries that are already in the buffer. So we
748 // return what we've collected until that point.
749 if (err && rem == size)
750 fuse_reply_err(req, err);
751 else
752 fuse_reply_buf(req, buf, size - rem);
753 free(buf);
754 }
755
756 static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
757 off_t offset, struct fuse_file_info *fi)
758 {
759 lo_do_readdir(req, ino, size, offset, fi, 0);
760 }
761
762 static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
763 off_t offset, struct fuse_file_info *fi)
764 {
765 lo_do_readdir(req, ino, size, offset, fi, 1);
766 }
767
768 static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
769 {
770 struct lo_dirp *d = lo_dirp(fi);
771 (void) ino;
772 closedir(d->dp);
773 free(d);
774 fuse_reply_err(req, 0);
775 }
776
777 static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
778 mode_t mode, struct fuse_file_info *fi)
779 {
780 int fd;
781 struct fuse_entry_param e;
782 int err;
783
784 if (lo_debug(req))
785 fprintf(stderr, "lo_create(parent=%" PRIu64 ", name=%s)\n",
786 parent, name);
787
788 fd = openat(lo_fd(req, parent), name,
789 (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
790 if (fd == -1)
791 return (void) fuse_reply_err(req, errno);
792
793 fi->fh = fd;
794
795 err = lo_do_lookup(req, parent, name, &e);
796 if (err)
797 fuse_reply_err(req, err);
798 else
799 fuse_reply_create(req, &e, fi);
800 }
801
802 static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
803 struct fuse_file_info *fi)
804 {
805 int res;
806 int fd = dirfd(lo_dirp(fi)->dp);
807 (void) ino;
808 if (datasync)
809 res = fdatasync(fd);
810 else
811 res = fsync(fd);
812 fuse_reply_err(req, res == -1 ? errno : 0);
813 }
814
815 static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
816 {
817 int fd;
818 char buf[64];
819 struct lo_data *lo = lo_data(req);
820
821 if (lo_debug(req))
822 fprintf(stderr, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
823 ino, fi->flags);
824
825 /* With writeback cache, kernel may send read requests even
826 when userspace opened write-only */
827 if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
828 fi->flags &= ~O_ACCMODE;
829 fi->flags |= O_RDWR;
830 }
831
832 /* With writeback cache, O_APPEND is handled by the kernel.
833 This breaks atomicity (since the file may change in the
834 underlying filesystem, so that the kernel's idea of the
835 end of the file isn't accurate anymore). In this example,
836 we just accept that. A more rigorous filesystem may want
837 to return an error here */
838 if (lo->writeback && (fi->flags & O_APPEND))
839 fi->flags &= ~O_APPEND;
840
841 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
842 fd = open(buf, fi->flags & ~O_NOFOLLOW);
843 if (fd == -1)
844 return (void) fuse_reply_err(req, errno);
845
846 fi->fh = fd;
847 if (lo->cache == CACHE_NEVER)
848 fi->direct_io = 1;
849 else if (lo->cache == CACHE_ALWAYS)
850 fi->keep_cache = 1;
851 fuse_reply_open(req, fi);
852 }
853
854 static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
855 {
856 (void) ino;
857
858 close(fi->fh);
859 fuse_reply_err(req, 0);
860 }
861
862 static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
863 {
864 int res;
865 (void) ino;
866 res = close(dup(fi->fh));
867 fuse_reply_err(req, res == -1 ? errno : 0);
868 }
869
870 static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
871 struct fuse_file_info *fi)
872 {
873 int res;
874 (void) ino;
875 if (datasync)
876 res = fdatasync(fi->fh);
877 else
878 res = fsync(fi->fh);
879 fuse_reply_err(req, res == -1 ? errno : 0);
880 }
881
882 static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
883 off_t offset, struct fuse_file_info *fi)
884 {
885 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
886
887 if (lo_debug(req))
888 fprintf(stderr, "lo_read(ino=%" PRIu64 ", size=%zd, "
889 "off=%lu)\n", ino, size, (unsigned long) offset);
890
891 buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
892 buf.buf[0].fd = fi->fh;
893 buf.buf[0].pos = offset;
894
895 fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
896 }
897
898 static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
899 struct fuse_bufvec *in_buf, off_t off,
900 struct fuse_file_info *fi)
901 {
902 (void) ino;
903 ssize_t res;
904 struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
905
906 out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
907 out_buf.buf[0].fd = fi->fh;
908 out_buf.buf[0].pos = off;
909
910 if (lo_debug(req))
911 fprintf(stderr, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n",
912 ino, out_buf.buf[0].size, (unsigned long) off);
913
914 res = fuse_buf_copy(&out_buf, in_buf, 0);
915 if(res < 0)
916 fuse_reply_err(req, -res);
917 else
918 fuse_reply_write(req, (size_t) res);
919 }
920
921 static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
922 {
923 int res;
924 struct statvfs stbuf;
925
926 res = fstatvfs(lo_fd(req, ino), &stbuf);
927 if (res == -1)
928 fuse_reply_err(req, errno);
929 else
930 fuse_reply_statfs(req, &stbuf);
931 }
932
933 static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
934 off_t offset, off_t length, struct fuse_file_info *fi)
935 {
936 int err;
937 (void) ino;
938
939 if (mode) {
940 fuse_reply_err(req, EOPNOTSUPP);
941 return;
942 }
943
944 err = posix_fallocate(fi->fh, offset, length);
945
946 fuse_reply_err(req, err);
947 }
948
949 static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
950 int op)
951 {
952 int res;
953 (void) ino;
954
955 res = flock(fi->fh, op);
956
957 fuse_reply_err(req, res == -1 ? errno : 0);
958 }
959
960 static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
961 size_t size)
962 {
963 char *value = NULL;
964 char procname[64];
965 struct lo_inode *inode = lo_inode(req, ino);
966 ssize_t ret;
967 int saverr;
968
969 saverr = ENOSYS;
970 if (!lo_data(req)->xattr)
971 goto out;
972
973 if (lo_debug(req)) {
974 fprintf(stderr, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
975 ino, name, size);
976 }
977
978 if (inode->is_symlink) {
979 /* Sorry, no race free way to getxattr on symlink. */
980 saverr = EPERM;
981 goto out;
982 }
983
984 sprintf(procname, "/proc/self/fd/%i", inode->fd);
985
986 if (size) {
987 value = malloc(size);
988 if (!value)
989 goto out_err;
990
991 ret = getxattr(procname, name, value, size);
992 if (ret == -1)
993 goto out_err;
994 saverr = 0;
995 if (ret == 0)
996 goto out;
997
998 fuse_reply_buf(req, value, ret);
999 } else {
1000 ret = getxattr(procname, name, NULL, 0);
1001 if (ret == -1)
1002 goto out_err;
1003
1004 fuse_reply_xattr(req, ret);
1005 }
1006 out_free:
1007 free(value);
1008 return;
1009
1010 out_err:
1011 saverr = errno;
1012 out:
1013 fuse_reply_err(req, saverr);
1014 goto out_free;
1015 }
1016
1017 static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
1018 {
1019 char *value = NULL;
1020 char procname[64];
1021 struct lo_inode *inode = lo_inode(req, ino);
1022 ssize_t ret;
1023 int saverr;
1024
1025 saverr = ENOSYS;
1026 if (!lo_data(req)->xattr)
1027 goto out;
1028
1029 if (lo_debug(req)) {
1030 fprintf(stderr, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
1031 ino, size);
1032 }
1033
1034 if (inode->is_symlink) {
1035 /* Sorry, no race free way to listxattr on symlink. */
1036 saverr = EPERM;
1037 goto out;
1038 }
1039
1040 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1041
1042 if (size) {
1043 value = malloc(size);
1044 if (!value)
1045 goto out_err;
1046
1047 ret = listxattr(procname, value, size);
1048 if (ret == -1)
1049 goto out_err;
1050 saverr = 0;
1051 if (ret == 0)
1052 goto out;
1053
1054 fuse_reply_buf(req, value, ret);
1055 } else {
1056 ret = listxattr(procname, NULL, 0);
1057 if (ret == -1)
1058 goto out_err;
1059
1060 fuse_reply_xattr(req, ret);
1061 }
1062 out_free:
1063 free(value);
1064 return;
1065
1066 out_err:
1067 saverr = errno;
1068 out:
1069 fuse_reply_err(req, saverr);
1070 goto out_free;
1071 }
1072
1073 static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1074 const char *value, size_t size, int flags)
1075 {
1076 char procname[64];
1077 struct lo_inode *inode = lo_inode(req, ino);
1078 ssize_t ret;
1079 int saverr;
1080
1081 saverr = ENOSYS;
1082 if (!lo_data(req)->xattr)
1083 goto out;
1084
1085 if (lo_debug(req)) {
1086 fprintf(stderr, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
1087 ino, name, value, size);
1088 }
1089
1090 if (inode->is_symlink) {
1091 /* Sorry, no race free way to setxattr on symlink. */
1092 saverr = EPERM;
1093 goto out;
1094 }
1095
1096 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1097
1098 ret = setxattr(procname, name, value, size, flags);
1099 saverr = ret == -1 ? errno : 0;
1100
1101 out:
1102 fuse_reply_err(req, saverr);
1103 }
1104
1105 static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
1106 {
1107 char procname[64];
1108 struct lo_inode *inode = lo_inode(req, ino);
1109 ssize_t ret;
1110 int saverr;
1111
1112 saverr = ENOSYS;
1113 if (!lo_data(req)->xattr)
1114 goto out;
1115
1116 if (lo_debug(req)) {
1117 fprintf(stderr, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
1118 ino, name);
1119 }
1120
1121 if (inode->is_symlink) {
1122 /* Sorry, no race free way to setxattr on symlink. */
1123 saverr = EPERM;
1124 goto out;
1125 }
1126
1127 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1128
1129 ret = removexattr(procname, name);
1130 saverr = ret == -1 ? errno : 0;
1131
1132 out:
1133 fuse_reply_err(req, saverr);
1134 }
1135
1136 #ifdef HAVE_COPY_FILE_RANGE
1137 static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
1138 struct fuse_file_info *fi_in,
1139 fuse_ino_t ino_out, off_t off_out,
1140 struct fuse_file_info *fi_out, size_t len,
1141 int flags)
1142 {
1143 ssize_t res;
1144
1145 if (lo_debug(req))
1146 fprintf(stderr, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
1147 "off=%lu, ino=%" PRIu64 "/fd=%lu, "
1148 "off=%lu, size=%zd, flags=0x%x)\n",
1149 ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out,
1150 len, flags);
1151
1152 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
1153 flags);
1154 if (res < 0)
1155 fuse_reply_err(req, -errno);
1156 else
1157 fuse_reply_write(req, res);
1158 }
1159 #endif
1160
1161 static struct fuse_lowlevel_ops lo_oper = {
1162 .init = lo_init,
1163 .lookup = lo_lookup,
1164 .mkdir = lo_mkdir,
1165 .mknod = lo_mknod,
1166 .symlink = lo_symlink,
1167 .link = lo_link,
1168 .unlink = lo_unlink,
1169 .rmdir = lo_rmdir,
1170 .rename = lo_rename,
1171 .forget = lo_forget,
1172 .forget_multi = lo_forget_multi,
1173 .getattr = lo_getattr,
1174 .setattr = lo_setattr,
1175 .readlink = lo_readlink,
1176 .opendir = lo_opendir,
1177 .readdir = lo_readdir,
1178 .readdirplus = lo_readdirplus,
1179 .releasedir = lo_releasedir,
1180 .fsyncdir = lo_fsyncdir,
1181 .create = lo_create,
1182 .open = lo_open,
1183 .release = lo_release,
1184 .flush = lo_flush,
1185 .fsync = lo_fsync,
1186 .read = lo_read,
1187 .write_buf = lo_write_buf,
1188 .statfs = lo_statfs,
1189 .fallocate = lo_fallocate,
1190 .flock = lo_flock,
1191 .getxattr = lo_getxattr,
1192 .listxattr = lo_listxattr,
1193 .setxattr = lo_setxattr,
1194 .removexattr = lo_removexattr,
1195 #ifdef HAVE_COPY_FILE_RANGE
1196 .copy_file_range = lo_copy_file_range,
1197 #endif
1198 };
1199
1200 int main(int argc, char *argv[])
1201 {
1202 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1203 struct fuse_session *se;
1204 struct fuse_cmdline_opts opts;
1205 struct lo_data lo = { .debug = 0,
1206 .writeback = 0 };
1207 int ret = -1;
1208
1209 /* Don't mask creation mode, kernel already did that */
1210 umask(0);
1211
1212 pthread_mutex_init(&lo.mutex, NULL);
1213 lo.root.next = lo.root.prev = &lo.root;
1214 lo.root.fd = -1;
1215 lo.cache = CACHE_NORMAL;
1216
1217 if (fuse_parse_cmdline(&args, &opts) != 0)
1218 return 1;
1219 if (opts.show_help) {
1220 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
1221 fuse_cmdline_help();
1222 fuse_lowlevel_help();
1223 ret = 0;
1224 goto err_out1;
1225 } else if (opts.show_version) {
1226 printf("FUSE library version %s\n", fuse_pkgversion());
1227 fuse_lowlevel_version();
1228 ret = 0;
1229 goto err_out1;
1230 }
1231
1232 if(opts.mountpoint == NULL) {
1233 printf("usage: %s [options] <mountpoint>\n", argv[0]);
1234 printf(" %s --help\n", argv[0]);
1235 ret = 1;
1236 goto err_out1;
1237 }
1238
1239 if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
1240 return 1;
1241
1242 lo.debug = opts.debug;
1243 lo.root.refcount = 2;
1244 if (lo.source) {
1245 struct stat stat;
1246 int res;
1247
1248 res = lstat(lo.source, &stat);
1249 if (res == -1)
1250 err(1, "failed to stat source (\"%s\")", lo.source);
1251 if (!S_ISDIR(stat.st_mode))
1252 errx(1, "source is not a directory");
1253
1254 } else {
1255 lo.source = "/";
1256 }
1257 lo.root.is_symlink = false;
1258 if (!lo.timeout_set) {
1259 switch (lo.cache) {
1260 case CACHE_NEVER:
1261 lo.timeout = 0.0;
1262 break;
1263
1264 case CACHE_NORMAL:
1265 lo.timeout = 1.0;
1266 break;
1267
1268 case CACHE_ALWAYS:
1269 lo.timeout = 86400.0;
1270 break;
1271 }
1272 } else if (lo.timeout < 0) {
1273 errx(1, "timeout is negative (%lf)", lo.timeout);
1274 }
1275
1276 lo.root.fd = open(lo.source, O_PATH);
1277 if (lo.root.fd == -1)
1278 err(1, "open(\"%s\", O_PATH)", lo.source);
1279
1280 se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
1281 if (se == NULL)
1282 goto err_out1;
1283
1284 if (fuse_set_signal_handlers(se) != 0)
1285 goto err_out2;
1286
1287 if (fuse_session_mount(se, opts.mountpoint) != 0)
1288 goto err_out3;
1289
1290 fuse_daemonize(opts.foreground);
1291
1292 /* Block until ctrl+c or fusermount -u */
1293 if (opts.singlethread)
1294 ret = fuse_session_loop(se);
1295 else
1296 ret = fuse_session_loop_mt(se, opts.clone_fd);
1297
1298 fuse_session_unmount(se);
1299 err_out3:
1300 fuse_remove_signal_handlers(se);
1301 err_out2:
1302 fuse_session_destroy(se);
1303 err_out1:
1304 free(opts.mountpoint);
1305 fuse_opt_free_args(&args);
1306
1307 if (lo.root.fd >= 0)
1308 close(lo.root.fd);
1309
1310 return ret ? 1 : 0;
1311 }
fuse_session_destroy
void fuse_session_destroy(struct fuse_session *se)
Definition: fuse_lowlevel.c:2621
fuse_reply_err
int fuse_reply_err(fuse_req_t req, int err)
Definition: fuse_lowlevel.c:312
fuse_bufvec::off
size_t off
Definition: fuse_common.h:679
fuse_session_new
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
Definition: fuse_lowlevel.c:2809
fuse_conn_info::capable
unsigned capable
Definition: fuse_common.h:381
fuse_file_info::fh
uint64_t fh
Definition: fuse_common.h:72
fuse_session_loop
int fuse_session_loop(struct fuse_session *se)
Definition: fuse_loop.c:19
fuse_conn_info
Definition: fuse_common.h:343
fuse_lowlevel_help
void fuse_lowlevel_help(void)
Definition: fuse_lowlevel.c:2611
fuse_file_info::direct_io
unsigned int direct_io
Definition: fuse_common.h:46
FUSE_BUF_IS_FD
Definition: fuse_common.h:555
fuse_add_direntry_plus
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
Definition: fuse_lowlevel.c:357
fuse_reply_data
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
Definition: fuse_lowlevel.c:820
fuse_daemonize
int fuse_daemonize(int foreground)
Definition: helper.c:225
fuse_session_mount
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
Definition: fuse_lowlevel.c:2903
fuse_opt_parse
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:397
fuse_req_t
struct fuse_req * fuse_req_t
Definition: fuse_lowlevel.h:49
fuse_entry_param::attr
struct stat attr
Definition: fuse_lowlevel.h:91
fuse_add_direntry
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
Definition: fuse_lowlevel.c:267
fuse_req_userdata
void * fuse_req_userdata(fuse_req_t req)
Definition: fuse_lowlevel.c:2338
FUSE_BUF_SPLICE_MOVE
Definition: fuse_common.h:606
fuse_file_info::keep_cache
unsigned int keep_cache
Definition: fuse_common.h:51
fuse_entry_param
Definition: fuse_lowlevel.h:59
FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_EXPORT_SUPPORT
Definition: fuse_common.h:144
fuse_entry_param::ino
fuse_ino_t ino
Definition: fuse_lowlevel.h:67
fuse_parse_cmdline
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition: helper.c:202
fuse_reply_xattr
int fuse_reply_xattr(fuse_req_t req, size_t count)
Definition: fuse_lowlevel.c:854
fuse_cmdline_help
void fuse_cmdline_help(void)
Definition: helper.c:129
fuse_reply_entry
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
Definition: fuse_lowlevel.c:399
fuse_set_signal_handlers
int fuse_set_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:62
fuse_buf::pos
off_t pos
Definition: fuse_common.h:654
fuse_remove_signal_handlers
void fuse_remove_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:79
fuse_file_info
Definition: fuse_common.h:37
fuse_lowlevel_version
void fuse_lowlevel_version(void)
Definition: fuse_lowlevel.c:2604
fuse_reply_none
void fuse_reply_none(fuse_req_t req)
Definition: fuse_lowlevel.c:317
fuse_file_info::flags
int flags
Definition: fuse_common.h:39
fuse_bufvec
Definition: fuse_common.h:665
fuse_opt_free_args
void fuse_opt_free_args(struct fuse_args *args)
Definition: fuse_opt.c:33
fuse_reply_attr
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
Definition: fuse_lowlevel.c:431
fuse_session_unmount
void fuse_session_unmount(struct fuse_session *se)
Definition: fuse_lowlevel.c:2958
FUSE_OPT_END
#define FUSE_OPT_END
Definition: fuse_opt.h:104
fuse_buf::flags
enum fuse_buf_flags flags
Definition: fuse_common.h:633
fuse_reply_statfs
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
Definition: fuse_lowlevel.c:842
FUSE_ROOT_ID
#define FUSE_ROOT_ID
Definition: fuse_lowlevel.h:43
FUSE_CAP_FLOCK_LOCKS
#define FUSE_CAP_FLOCK_LOCKS
Definition: fuse_common.h:190
fuse_ino_t
uint64_t fuse_ino_t
Definition: fuse_lowlevel.h:46
fuse_reply_create
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
Definition: fuse_lowlevel.c:415
fuse_reply_write
int fuse_reply_write(fuse_req_t req, size_t count)
Definition: fuse_lowlevel.c:460
FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_WRITEBACK_CACHE
Definition: fuse_common.h:266
fuse_reply_open
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
Definition: fuse_lowlevel.c:451
fuse_lowlevel.h
fuse_lowlevel_ops
Definition: fuse_lowlevel.h:175
fuse_bufvec::buf
struct fuse_buf buf[1]
Definition: fuse_common.h:684
fuse_opt
Definition: fuse_opt.h:77
fuse_conn_info::want
unsigned want
Definition: fuse_common.h:389
fuse_args
Definition: fuse_opt.h:109
fuse_pkgversion
const char * fuse_pkgversion(void)
Definition: fuse.c:5071
FUSE_ARGS_INIT
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
FUSE_BUF_FD_SEEK
Definition: fuse_common.h:564
fuse_buf_size
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition: buffer.c:22
fuse_buf_copy
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition: buffer.c:281
fuse_buf::size
size_t size
Definition: fuse_common.h:628
fuse_entry_param::entry_timeout
double entry_timeout
Definition: fuse_lowlevel.h:103
fuse_buf::fd
int fd
Definition: fuse_common.h:647
fuse_entry_param::attr_timeout
double attr_timeout
Definition: fuse_lowlevel.h:97
fuse_reply_readlink
int fuse_reply_readlink(fuse_req_t req, const char *link)
Definition: fuse_lowlevel.c:446
fuse_reply_buf
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
Definition: fuse_lowlevel.c:470
fuse_lowlevel_ops::init
void(* init)(void *userdata, struct fuse_conn_info *conn)
Definition: fuse_lowlevel.h:192