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