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