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