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