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