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 *dir = lo_inode(req, parent);
388 struct fuse_entry_param e;
389
390 saverr = ENOMEM;
391
392 if (S_ISDIR(mode))
393 res = mkdirat(dir->fd, name, mode);
394 else if (S_ISLNK(mode))
395 res = symlinkat(link, dir->fd, name);
396 else
397 res = mknodat(dir->fd, name, mode, rdev);
398 saverr = errno;
399 if (res == -1)
400 goto out;
401
402 saverr = lo_do_lookup(req, parent, name, &e);
403 if (saverr)
404 goto out;
405
406 if (lo_debug(req))
407 fprintf(stderr, " %lli/%s -> %lli\n",
408 (unsigned long long) parent, name, (unsigned long long) e.ino);
409
410 fuse_reply_entry(req, &e);
411 return;
412
413 out:
414 if (newfd != -1)
415 close(newfd);
416 fuse_reply_err(req, saverr);
417 }
418
419 static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
420 const char *name, mode_t mode, dev_t rdev)
421 {
422 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
423 }
424
425 static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
426 mode_t mode)
427 {
428 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
429 }
430
431 static void lo_symlink(fuse_req_t req, const char *link,
432 fuse_ino_t parent, const char *name)
433 {
434 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
435 }
436
437 static int linkat_empty_nofollow(struct lo_inode *inode, int dfd,
438 const char *name)
439 {
440 int res;
441 char procname[64];
442
443 if (inode->is_symlink) {
444 res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH);
445 if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
446 /* Sorry, no race free way to hard-link a symlink. */
447 errno = EPERM;
448 }
449 return res;
450 }
451
452 sprintf(procname, "/proc/self/fd/%i", inode->fd);
453
454 return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
455 }
456
457 static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
458 const char *name)
459 {
460 int res;
461 struct lo_data *lo = lo_data(req);
462 struct lo_inode *inode = lo_inode(req, ino);
463 struct fuse_entry_param e;
464 int saverr;
465
466 memset(&e, 0, sizeof(struct fuse_entry_param));
467 e.attr_timeout = lo->timeout;
468 e.entry_timeout = lo->timeout;
469
470 res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
471 if (res == -1)
472 goto out_err;
473
474 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
475 if (res == -1)
476 goto out_err;
477
478 pthread_mutex_lock(&lo->mutex);
479 inode->refcount++;
480 pthread_mutex_unlock(&lo->mutex);
481 e.ino = (uintptr_t) inode;
482
483 if (lo_debug(req))
484 fprintf(stderr, " %lli/%s -> %lli\n",
485 (unsigned long long) parent, name,
486 (unsigned long long) e.ino);
487
488 fuse_reply_entry(req, &e);
489 return;
490
491 out_err:
492 saverr = errno;
493 fuse_reply_err(req, saverr);
494 }
495
496 static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
497 {
498 int res;
499
500 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
501
502 fuse_reply_err(req, res == -1 ? errno : 0);
503 }
504
505 static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
506 fuse_ino_t newparent, const char *newname,
507 unsigned int flags)
508 {
509 int res;
510
511 if (flags) {
512 fuse_reply_err(req, EINVAL);
513 return;
514 }
515
516 res = renameat(lo_fd(req, parent), name,
517 lo_fd(req, newparent), newname);
518
519 fuse_reply_err(req, res == -1 ? errno : 0);
520 }
521
522 static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
523 {
524 int res;
525
526 res = unlinkat(lo_fd(req, parent), name, 0);
527
528 fuse_reply_err(req, res == -1 ? errno : 0);
529 }
530
531 static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
532 {
533 if (!inode)
534 return;
535
536 pthread_mutex_lock(&lo->mutex);
537 assert(inode->refcount >= n);
538 inode->refcount -= n;
539 if (!inode->refcount) {
540 struct lo_inode *prev, *next;
541
542 prev = inode->prev;
543 next = inode->next;
544 next->prev = prev;
545 prev->next = next;
546
547 pthread_mutex_unlock(&lo->mutex);
548 close(inode->fd);
549 free(inode);
550
551 } else {
552 pthread_mutex_unlock(&lo->mutex);
553 }
554 }
555
556 static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
557 {
558 struct lo_data *lo = lo_data(req);
559 struct lo_inode *inode = lo_inode(req, ino);
560
561 if (lo_debug(req)) {
562 fprintf(stderr, " forget %lli %lli -%lli\n",
563 (unsigned long long) ino,
564 (unsigned long long) inode->refcount,
565 (unsigned long long) nlookup);
566 }
567
568 unref_inode(lo, inode, nlookup);
569 }
570
571 static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
572 {
573 lo_forget_one(req, ino, nlookup);
574 fuse_reply_none(req);
575 }
576
577 static void lo_forget_multi(fuse_req_t req, size_t count,
578 struct fuse_forget_data *forgets)
579 {
580 int i;
581
582 for (i = 0; i < count; i++)
583 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
584 fuse_reply_none(req);
585 }
586
587 static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
588 {
589 char buf[PATH_MAX + 1];
590 int res;
591
592 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
593 if (res == -1)
594 return (void) fuse_reply_err(req, errno);
595
596 if (res == sizeof(buf))
597 return (void) fuse_reply_err(req, ENAMETOOLONG);
598
599 buf[res] = '\0';
600
601 fuse_reply_readlink(req, buf);
602 }
603
604 struct lo_dirp {
605 int fd;
606 DIR *dp;
607 struct dirent *entry;
608 off_t offset;
609 };
610
611 static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
612 {
613 return (struct lo_dirp *) (uintptr_t) fi->fh;
614 }
615
616 static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
617 {
618 int error = ENOMEM;
619 struct lo_data *lo = lo_data(req);
620 struct lo_dirp *d = calloc(1, sizeof(struct lo_dirp));
621 if (d == NULL)
622 goto out_err;
623
624 d->fd = openat(lo_fd(req, ino), ".", O_RDONLY);
625 if (d->fd == -1)
626 goto out_errno;
627
628 d->dp = fdopendir(d->fd);
629 if (d->dp == NULL)
630 goto out_errno;
631
632 d->offset = 0;
633 d->entry = NULL;
634
635 fi->fh = (uintptr_t) d;
636 if (lo->cache == CACHE_ALWAYS)
637 fi->keep_cache = 1;
638 fuse_reply_open(req, fi);
639 return;
640
641 out_errno:
642 error = errno;
643 out_err:
644 if (d) {
645 if (d->fd != -1)
646 close(d->fd);
647 free(d);
648 }
649 fuse_reply_err(req, error);
650 }
651
652 static int is_dot_or_dotdot(const char *name)
653 {
654 return name[0] == '.' && (name[1] == '\0' ||
655 (name[1] == '.' && name[2] == '\0'));
656 }
657
658 static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
659 off_t offset, struct fuse_file_info *fi, int plus)
660 {
661 struct lo_dirp *d = lo_dirp(fi);
662 char *buf;
663 char *p;
664 size_t rem = size;
665 int err;
666
667 (void) ino;
668
669 buf = calloc(1, size);
670 if (!buf) {
671 err = ENOMEM;
672 goto error;
673 }
674 p = buf;
675
676 if (offset != d->offset) {
677 seekdir(d->dp, offset);
678 d->entry = NULL;
679 d->offset = offset;
680 }
681 while (1) {
682 size_t entsize;
683 off_t nextoff;
684 const char *name;
685
686 if (!d->entry) {
687 errno = 0;
688 d->entry = readdir(d->dp);
689 if (!d->entry) {
690 if (errno) { // Error
691 err = errno;
692 goto error;
693 } else { // End of stream
694 break;
695 }
696 }
697 }
698 nextoff = d->entry->d_off;
699 name = d->entry->d_name;
700 fuse_ino_t entry_ino = 0;
701 if (plus) {
702 struct fuse_entry_param e;
703 if (is_dot_or_dotdot(name)) {
704 e = (struct fuse_entry_param) {
705 .attr.st_ino = d->entry->d_ino,
706 .attr.st_mode = d->entry->d_type << 12,
707 };
708 } else {
709 err = lo_do_lookup(req, ino, name, &e);
710 if (err)
711 goto error;
712 entry_ino = e.ino;
713 }
714
715 entsize = fuse_add_direntry_plus(req, p, rem, name,
716 &e, nextoff);
717 } else {
718 struct stat st = {
719 .st_ino = d->entry->d_ino,
720 .st_mode = d->entry->d_type << 12,
721 };
722 entsize = fuse_add_direntry(req, p, rem, name,
723 &st, nextoff);
724 }
725 if (entsize > rem) {
726 if (entry_ino != 0)
727 lo_forget_one(req, entry_ino, 1);
728 break;
729 }
730
731 p += entsize;
732 rem -= entsize;
733
734 d->entry = NULL;
735 d->offset = nextoff;
736 }
737
738 err = 0;
739 error:
740 // If there's an error, we can only signal it if we haven't stored
741 // any entries yet - otherwise we'd end up with wrong lookup
742 // counts for the entries that are already in the buffer. So we
743 // return what we've collected until that point.
744 if (err && rem == size)
745 fuse_reply_err(req, err);
746 else
747 fuse_reply_buf(req, buf, size - rem);
748 free(buf);
749 }
750
751 static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
752 off_t offset, struct fuse_file_info *fi)
753 {
754 lo_do_readdir(req, ino, size, offset, fi, 0);
755 }
756
757 static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
758 off_t offset, struct fuse_file_info *fi)
759 {
760 lo_do_readdir(req, ino, size, offset, fi, 1);
761 }
762
763 static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
764 {
765 struct lo_dirp *d = lo_dirp(fi);
766 (void) ino;
767 closedir(d->dp);
768 free(d);
769 fuse_reply_err(req, 0);
770 }
771
772 static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
773 mode_t mode, struct fuse_file_info *fi)
774 {
775 int fd;
776 struct lo_data *lo = lo_data(req);
777 struct fuse_entry_param e;
778 int err;
779
780 if (lo_debug(req))
781 fprintf(stderr, "lo_create(parent=%" PRIu64 ", name=%s)\n",
782 parent, name);
783
784 fd = openat(lo_fd(req, parent), name,
785 (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
786 if (fd == -1)
787 return (void) fuse_reply_err(req, errno);
788
789 fi->fh = fd;
790 if (lo->cache == CACHE_NEVER)
791 fi->direct_io = 1;
792 else if (lo->cache == CACHE_ALWAYS)
793 fi->keep_cache = 1;
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:2627
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:710
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:2815
fuse_conn_info::capable
unsigned capable
Definition: fuse_common.h:412
fuse_file_info::fh
uint64_t fh
Definition: fuse_common.h:91
fuse_session_loop
int fuse_session_loop(struct fuse_session *se)
Definition: fuse_loop.c:19
fuse_conn_info
Definition: fuse_common.h:374
fuse_lowlevel_help
void fuse_lowlevel_help(void)
Definition: fuse_lowlevel.c:2617
fuse_file_info::direct_io
unsigned int direct_io
Definition: fuse_common.h:56
FUSE_BUF_IS_FD
Definition: fuse_common.h:586
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:822
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:2909
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:2344
FUSE_BUF_SPLICE_MOVE
Definition: fuse_common.h:637
fuse_file_info::keep_cache
unsigned int keep_cache
Definition: fuse_common.h:63
fuse_entry_param
Definition: fuse_lowlevel.h:59
FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_EXPORT_SUPPORT
Definition: fuse_common.h:163
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:856
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:401
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:685
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:43
fuse_lowlevel_version
void fuse_lowlevel_version(void)
Definition: fuse_lowlevel.c:2610
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:45
fuse_bufvec
Definition: fuse_common.h:696
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:433
fuse_session_unmount
void fuse_session_unmount(struct fuse_session *se)
Definition: fuse_lowlevel.c:2964
FUSE_OPT_END
#define FUSE_OPT_END
Definition: fuse_opt.h:104
fuse_buf::flags
enum fuse_buf_flags flags
Definition: fuse_common.h:664
fuse_reply_statfs
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
Definition: fuse_lowlevel.c:844
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:209
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:417
fuse_reply_write
int fuse_reply_write(fuse_req_t req, size_t count)
Definition: fuse_lowlevel.c:462
FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_WRITEBACK_CACHE
Definition: fuse_common.h:285
fuse_reply_open
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
Definition: fuse_lowlevel.c:453
fuse_lowlevel.h
fuse_lowlevel_ops
Definition: fuse_lowlevel.h:175
fuse_bufvec::buf
struct fuse_buf buf[1]
Definition: fuse_common.h:715
fuse_opt
Definition: fuse_opt.h:77
fuse_conn_info::want
unsigned want
Definition: fuse_common.h:420
fuse_args
Definition: fuse_opt.h:109
fuse_pkgversion
const char * fuse_pkgversion(void)
Definition: fuse.c:5073
FUSE_ARGS_INIT
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
FUSE_BUF_FD_SEEK
Definition: fuse_common.h:595
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:659
fuse_entry_param::entry_timeout
double entry_timeout
Definition: fuse_lowlevel.h:103
fuse_buf::fd
int fd
Definition: fuse_common.h:678
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:448
fuse_reply_buf
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
Definition: fuse_lowlevel.c:472
fuse_lowlevel_ops::init
void(* init)(void *userdata, struct fuse_conn_info *conn)
Definition: fuse_lowlevel.h:192