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