libfuse: example/passthrough_ll.c Source File

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 GPL.
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 saverr = ENOMEM;
391
392 res = mknod_wrapper(dir->fd, name, link, mode, rdev);
393
394 saverr = errno;
395 if (res == -1)
396 goto out;
397
398 saverr = lo_do_lookup(req, parent, name, &e);
399 if (saverr)
400 goto out;
401
402 if (lo_debug(req))
403 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
404 (unsigned long long) parent, name, (unsigned long long) e.ino);
405
406 fuse_reply_entry(req, &e);
407 return;
408
409 out:
410 fuse_reply_err(req, saverr);
411 }
412
413 static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
414 const char *name, mode_t mode, dev_t rdev)
415 {
416 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
417 }
418
419 static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
420 mode_t mode)
421 {
422 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
423 }
424
425 static void lo_symlink(fuse_req_t req, const char *link,
426 fuse_ino_t parent, const char *name)
427 {
428 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
429 }
430
431 static int linkat_empty_nofollow(struct lo_inode *inode, int dfd,
432 const char *name)
433 {
434 int res;
435 char procname[64];
436
437 if (inode->is_symlink) {
438 res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH);
439 if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
440 /* Sorry, no race free way to hard-link a symlink. */
441 errno = EPERM;
442 }
443 return res;
444 }
445
446 sprintf(procname, "/proc/self/fd/%i", inode->fd);
447
448 return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
449 }
450
451 static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
452 const char *name)
453 {
454 int res;
455 struct lo_data *lo = lo_data(req);
456 struct lo_inode *inode = lo_inode(req, ino);
457 struct fuse_entry_param e;
458 int saverr;
459
460 memset(&e, 0, sizeof(struct fuse_entry_param));
461 e.attr_timeout = lo->timeout;
462 e.entry_timeout = lo->timeout;
463
464 res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
465 if (res == -1)
466 goto out_err;
467
468 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
469 if (res == -1)
470 goto out_err;
471
472 pthread_mutex_lock(&lo->mutex);
473 inode->refcount++;
474 pthread_mutex_unlock(&lo->mutex);
475 e.ino = (uintptr_t) inode;
476
477 if (lo_debug(req))
478 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
479 (unsigned long long) parent, name,
480 (unsigned long long) e.ino);
481
482 fuse_reply_entry(req, &e);
483 return;
484
485 out_err:
486 saverr = errno;
487 fuse_reply_err(req, saverr);
488 }
489
490 static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
491 {
492 int res;
493
494 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
495
496 fuse_reply_err(req, res == -1 ? errno : 0);
497 }
498
499 static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
500 fuse_ino_t newparent, const char *newname,
501 unsigned int flags)
502 {
503 int res;
504
505 if (flags) {
506 fuse_reply_err(req, EINVAL);
507 return;
508 }
509
510 res = renameat(lo_fd(req, parent), name,
511 lo_fd(req, newparent), newname);
512
513 fuse_reply_err(req, res == -1 ? errno : 0);
514 }
515
516 static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
517 {
518 int res;
519
520 res = unlinkat(lo_fd(req, parent), name, 0);
521
522 fuse_reply_err(req, res == -1 ? errno : 0);
523 }
524
525 static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
526 {
527 if (!inode)
528 return;
529
530 pthread_mutex_lock(&lo->mutex);
531 assert(inode->refcount >= n);
532 inode->refcount -= n;
533 if (!inode->refcount) {
534 struct lo_inode *prev, *next;
535
536 prev = inode->prev;
537 next = inode->next;
538 next->prev = prev;
539 prev->next = next;
540
541 pthread_mutex_unlock(&lo->mutex);
542 close(inode->fd);
543 free(inode);
544
545 } else {
546 pthread_mutex_unlock(&lo->mutex);
547 }
548 }
549
550 static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
551 {
552 struct lo_data *lo = lo_data(req);
553 struct lo_inode *inode = lo_inode(req, ino);
554
555 if (lo_debug(req)) {
556 fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
557 (unsigned long long) ino,
558 (unsigned long long) inode->refcount,
559 (unsigned long long) nlookup);
560 }
561
562 unref_inode(lo, inode, nlookup);
563 }
564
565 static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
566 {
567 lo_forget_one(req, ino, nlookup);
568 fuse_reply_none(req);
569 }
570
571 static void lo_forget_multi(fuse_req_t req, size_t count,
572 struct fuse_forget_data *forgets)
573 {
574 int i;
575
576 for (i = 0; i < count; i++)
577 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
578 fuse_reply_none(req);
579 }
580
581 static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
582 {
583 char buf[PATH_MAX + 1];
584 int res;
585
586 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
587 if (res == -1)
588 return (void) fuse_reply_err(req, errno);
589
590 if (res == sizeof(buf))
591 return (void) fuse_reply_err(req, ENAMETOOLONG);
592
593 buf[res] = '\0';
594
595 fuse_reply_readlink(req, buf);
596 }
597
598 struct lo_dirp {
599 DIR *dp;
600 struct dirent *entry;
601 off_t offset;
602 };
603
604 static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
605 {
606 return (struct lo_dirp *) (uintptr_t) fi->fh;
607 }
608
609 static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
610 {
611 int error = ENOMEM;
612 struct lo_data *lo = lo_data(req);
613 struct lo_dirp *d;
614 int fd;
615
616 d = calloc(1, sizeof(struct lo_dirp));
617 if (d == NULL)
618 goto out_err;
619
620 fd = openat(lo_fd(req, ino), ".", O_RDONLY);
621 if (fd == -1)
622 goto out_errno;
623
624 d->dp = fdopendir(fd);
625 if (d->dp == NULL)
626 goto out_errno;
627
628 d->offset = 0;
629 d->entry = NULL;
630
631 fi->fh = (uintptr_t) d;
632 if (lo->cache == CACHE_ALWAYS)
633 fi->keep_cache = 1;
634 fuse_reply_open(req, fi);
635 return;
636
637 out_errno:
638 error = errno;
639 out_err:
640 if (d) {
641 if (fd != -1)
642 close(fd);
643 free(d);
644 }
645 fuse_reply_err(req, error);
646 }
647
648 static int is_dot_or_dotdot(const char *name)
649 {
650 return name[0] == '.' && (name[1] == '\0' ||
651 (name[1] == '.' && name[2] == '\0'));
652 }
653
654 static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
655 off_t offset, struct fuse_file_info *fi, int plus)
656 {
657 struct lo_dirp *d = lo_dirp(fi);
658 char *buf;
659 char *p;
660 size_t rem = size;
661 int err;
662
663 (void) ino;
664
665 buf = calloc(1, size);
666 if (!buf) {
667 err = ENOMEM;
668 goto error;
669 }
670 p = buf;
671
672 if (offset != d->offset) {
673 seekdir(d->dp, offset);
674 d->entry = NULL;
675 d->offset = offset;
676 }
677 while (1) {
678 size_t entsize;
679 off_t nextoff;
680 const char *name;
681
682 if (!d->entry) {
683 errno = 0;
684 d->entry = readdir(d->dp);
685 if (!d->entry) {
686 if (errno) { // Error
687 err = errno;
688 goto error;
689 } else { // End of stream
690 break;
691 }
692 }
693 }
694 nextoff = d->entry->d_off;
695 name = d->entry->d_name;
696 fuse_ino_t entry_ino = 0;
697 if (plus) {
698 struct fuse_entry_param e;
699 if (is_dot_or_dotdot(name)) {
700 e = (struct fuse_entry_param) {
701 .attr.st_ino = d->entry->d_ino,
702 .attr.st_mode = d->entry->d_type << 12,
703 };
704 } else {
705 err = lo_do_lookup(req, ino, name, &e);
706 if (err)
707 goto error;
708 entry_ino = e.ino;
709 }
710
711 entsize = fuse_add_direntry_plus(req, p, rem, name,
712 &e, nextoff);
713 } else {
714 struct stat st = {
715 .st_ino = d->entry->d_ino,
716 .st_mode = d->entry->d_type << 12,
717 };
718 entsize = fuse_add_direntry(req, p, rem, name,
719 &st, nextoff);
720 }
721 if (entsize > rem) {
722 if (entry_ino != 0)
723 lo_forget_one(req, entry_ino, 1);
724 break;
725 }
726
727 p += entsize;
728 rem -= entsize;
729
730 d->entry = NULL;
731 d->offset = nextoff;
732 }
733
734 err = 0;
735 error:
736 // If there's an error, we can only signal it if we haven't stored
737 // any entries yet - otherwise we'd end up with wrong lookup
738 // counts for the entries that are already in the buffer. So we
739 // return what we've collected until that point.
740 if (err && rem == size)
741 fuse_reply_err(req, err);
742 else
743 fuse_reply_buf(req, buf, size - rem);
744 free(buf);
745 }
746
747 static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
748 off_t offset, struct fuse_file_info *fi)
749 {
750 lo_do_readdir(req, ino, size, offset, fi, 0);
751 }
752
753 static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
754 off_t offset, struct fuse_file_info *fi)
755 {
756 lo_do_readdir(req, ino, size, offset, fi, 1);
757 }
758
759 static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
760 {
761 struct lo_dirp *d = lo_dirp(fi);
762 (void) ino;
763 closedir(d->dp);
764 free(d);
765 fuse_reply_err(req, 0);
766 }
767
768 static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
769 mode_t mode, struct fuse_file_info *fi)
770 {
771 int fd;
772 struct lo_data *lo = lo_data(req);
773 struct fuse_entry_param e;
774 int err;
775
776 if (lo_debug(req))
777 fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
778 parent, name);
779
780 fd = openat(lo_fd(req, parent), name,
781 (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
782 if (fd == -1)
783 return (void) fuse_reply_err(req, errno);
784
785 fi->fh = fd;
786 if (lo->cache == CACHE_NEVER)
787 fi->direct_io = 1;
788 else if (lo->cache == CACHE_ALWAYS)
789 fi->keep_cache = 1;
790
791 err = lo_do_lookup(req, parent, name, &e);
792 if (err)
793 fuse_reply_err(req, err);
794 else
795 fuse_reply_create(req, &e, fi);
796 }
797
798 static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
799 struct fuse_file_info *fi)
800 {
801 int res;
802 int fd = dirfd(lo_dirp(fi)->dp);
803 (void) ino;
804 if (datasync)
805 res = fdatasync(fd);
806 else
807 res = fsync(fd);
808 fuse_reply_err(req, res == -1 ? errno : 0);
809 }
810
811 static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
812 {
813 int fd;
814 char buf[64];
815 struct lo_data *lo = lo_data(req);
816
817 if (lo_debug(req))
818 fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
819 ino, fi->flags);
820
821 /* With writeback cache, kernel may send read requests even
822 when userspace opened write-only */
823 if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
824 fi->flags &= ~O_ACCMODE;
825 fi->flags |= O_RDWR;
826 }
827
828 /* With writeback cache, O_APPEND is handled by the kernel.
829 This breaks atomicity (since the file may change in the
830 underlying filesystem, so that the kernel's idea of the
831 end of the file isn't accurate anymore). In this example,
832 we just accept that. A more rigorous filesystem may want
833 to return an error here */
834 if (lo->writeback && (fi->flags & O_APPEND))
835 fi->flags &= ~O_APPEND;
836
837 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
838 fd = open(buf, fi->flags & ~O_NOFOLLOW);
839 if (fd == -1)
840 return (void) fuse_reply_err(req, errno);
841
842 fi->fh = fd;
843 if (lo->cache == CACHE_NEVER)
844 fi->direct_io = 1;
845 else if (lo->cache == CACHE_ALWAYS)
846 fi->keep_cache = 1;
847 fuse_reply_open(req, fi);
848 }
849
850 static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
851 {
852 (void) ino;
853
854 close(fi->fh);
855 fuse_reply_err(req, 0);
856 }
857
858 static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
859 {
860 int res;
861 (void) ino;
862 res = close(dup(fi->fh));
863 fuse_reply_err(req, res == -1 ? errno : 0);
864 }
865
866 static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
867 struct fuse_file_info *fi)
868 {
869 int res;
870 (void) ino;
871 if (datasync)
872 res = fdatasync(fi->fh);
873 else
874 res = fsync(fi->fh);
875 fuse_reply_err(req, res == -1 ? errno : 0);
876 }
877
878 static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
879 off_t offset, struct fuse_file_info *fi)
880 {
881 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
882
883 if (lo_debug(req))
884 fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
885 "off=%lu)\n", ino, size, (unsigned long) offset);
886
887 buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
888 buf.buf[0].fd = fi->fh;
889 buf.buf[0].pos = offset;
890
891 fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
892 }
893
894 static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
895 struct fuse_bufvec *in_buf, off_t off,
896 struct fuse_file_info *fi)
897 {
898 (void) ino;
899 ssize_t res;
900 struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
901
902 out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
903 out_buf.buf[0].fd = fi->fh;
904 out_buf.buf[0].pos = off;
905
906 if (lo_debug(req))
907 fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n",
908 ino, out_buf.buf[0].size, (unsigned long) off);
909
910 res = fuse_buf_copy(&out_buf, in_buf, 0);
911 if(res < 0)
912 fuse_reply_err(req, -res);
913 else
914 fuse_reply_write(req, (size_t) res);
915 }
916
917 static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
918 {
919 int res;
920 struct statvfs stbuf;
921
922 res = fstatvfs(lo_fd(req, ino), &stbuf);
923 if (res == -1)
924 fuse_reply_err(req, errno);
925 else
926 fuse_reply_statfs(req, &stbuf);
927 }
928
929 static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
930 off_t offset, off_t length, struct fuse_file_info *fi)
931 {
932 int err = EOPNOTSUPP;
933 (void) ino;
934
935 #ifdef HAVE_FALLOCATE
936 err = fallocate(fi->fh, mode, offset, length);
937 if (err < 0)
938 err = errno;
939
940 #elif defined(HAVE_POSIX_FALLOCATE)
941 if (mode) {
942 fuse_reply_err(req, EOPNOTSUPP);
943 return;
944 }
945
946 err = posix_fallocate(fi->fh, offset, length);
947 #endif
948
949 fuse_reply_err(req, err);
950 }
951
952 static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
953 int op)
954 {
955 int res;
956 (void) ino;
957
958 res = flock(fi->fh, op);
959
960 fuse_reply_err(req, res == -1 ? errno : 0);
961 }
962
963 static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
964 size_t size)
965 {
966 char *value = NULL;
967 char procname[64];
968 struct lo_inode *inode = lo_inode(req, ino);
969 ssize_t ret;
970 int saverr;
971
972 saverr = ENOSYS;
973 if (!lo_data(req)->xattr)
974 goto out;
975
976 if (lo_debug(req)) {
977 fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
978 ino, name, size);
979 }
980
981 if (inode->is_symlink) {
982 /* Sorry, no race free way to getxattr on symlink. */
983 saverr = EPERM;
984 goto out;
985 }
986
987 sprintf(procname, "/proc/self/fd/%i", inode->fd);
988
989 if (size) {
990 value = malloc(size);
991 if (!value)
992 goto out_err;
993
994 ret = getxattr(procname, name, value, size);
995 if (ret == -1)
996 goto out_err;
997 saverr = 0;
998 if (ret == 0)
999 goto out;
1000
1001 fuse_reply_buf(req, value, ret);
1002 } else {
1003 ret = getxattr(procname, name, NULL, 0);
1004 if (ret == -1)
1005 goto out_err;
1006
1007 fuse_reply_xattr(req, ret);
1008 }
1009 out_free:
1010 free(value);
1011 return;
1012
1013 out_err:
1014 saverr = errno;
1015 out:
1016 fuse_reply_err(req, saverr);
1017 goto out_free;
1018 }
1019
1020 static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
1021 {
1022 char *value = NULL;
1023 char procname[64];
1024 struct lo_inode *inode = lo_inode(req, ino);
1025 ssize_t ret;
1026 int saverr;
1027
1028 saverr = ENOSYS;
1029 if (!lo_data(req)->xattr)
1030 goto out;
1031
1032 if (lo_debug(req)) {
1033 fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
1034 ino, size);
1035 }
1036
1037 if (inode->is_symlink) {
1038 /* Sorry, no race free way to listxattr on symlink. */
1039 saverr = EPERM;
1040 goto out;
1041 }
1042
1043 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1044
1045 if (size) {
1046 value = malloc(size);
1047 if (!value)
1048 goto out_err;
1049
1050 ret = listxattr(procname, value, size);
1051 if (ret == -1)
1052 goto out_err;
1053 saverr = 0;
1054 if (ret == 0)
1055 goto out;
1056
1057 fuse_reply_buf(req, value, ret);
1058 } else {
1059 ret = listxattr(procname, NULL, 0);
1060 if (ret == -1)
1061 goto out_err;
1062
1063 fuse_reply_xattr(req, ret);
1064 }
1065 out_free:
1066 free(value);
1067 return;
1068
1069 out_err:
1070 saverr = errno;
1071 out:
1072 fuse_reply_err(req, saverr);
1073 goto out_free;
1074 }
1075
1076 static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1077 const char *value, size_t size, int flags)
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_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
1090 ino, name, value, size);
1091 }
1092
1093 if (inode->is_symlink) {
1094 /* Sorry, no race free way to setxattr on symlink. */
1095 saverr = EPERM;
1096 goto out;
1097 }
1098
1099 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1100
1101 ret = setxattr(procname, name, value, size, flags);
1102 saverr = ret == -1 ? errno : 0;
1103
1104 out:
1105 fuse_reply_err(req, saverr);
1106 }
1107
1108 static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
1109 {
1110 char procname[64];
1111 struct lo_inode *inode = lo_inode(req, ino);
1112 ssize_t ret;
1113 int saverr;
1114
1115 saverr = ENOSYS;
1116 if (!lo_data(req)->xattr)
1117 goto out;
1118
1119 if (lo_debug(req)) {
1120 fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
1121 ino, name);
1122 }
1123
1124 if (inode->is_symlink) {
1125 /* Sorry, no race free way to setxattr on symlink. */
1126 saverr = EPERM;
1127 goto out;
1128 }
1129
1130 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1131
1132 ret = removexattr(procname, name);
1133 saverr = ret == -1 ? errno : 0;
1134
1135 out:
1136 fuse_reply_err(req, saverr);
1137 }
1138
1139 #ifdef HAVE_COPY_FILE_RANGE
1140 static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
1141 struct fuse_file_info *fi_in,
1142 fuse_ino_t ino_out, off_t off_out,
1143 struct fuse_file_info *fi_out, size_t len,
1144 int flags)
1145 {
1146 ssize_t res;
1147
1148 if (lo_debug(req))
1149 fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
1150 "off=%lu, ino=%" PRIu64 "/fd=%lu, "
1151 "off=%lu, size=%zd, flags=0x%x)\n",
1152 ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out,
1153 len, flags);
1154
1155 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
1156 flags);
1157 if (res < 0)
1158 fuse_reply_err(req, -errno);
1159 else
1160 fuse_reply_write(req, res);
1161 }
1162 #endif
1163
1164 static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1165 struct fuse_file_info *fi)
1166 {
1167 off_t res;
1168
1169 (void)ino;
1170 res = lseek(fi->fh, off, whence);
1171 if (res != -1)
1172 fuse_reply_lseek(req, res);
1173 else
1174 fuse_reply_err(req, errno);
1175 }
1176
1177 static struct fuse_lowlevel_ops lo_oper = {
1178 .init = lo_init,
1179 .lookup = lo_lookup,
1180 .mkdir = lo_mkdir,
1181 .mknod = lo_mknod,
1182 .symlink = lo_symlink,
1183 .link = lo_link,
1184 .unlink = lo_unlink,
1185 .rmdir = lo_rmdir,
1186 .rename = lo_rename,
1187 .forget = lo_forget,
1188 .forget_multi = lo_forget_multi,
1189 .getattr = lo_getattr,
1190 .setattr = lo_setattr,
1191 .readlink = lo_readlink,
1192 .opendir = lo_opendir,
1193 .readdir = lo_readdir,
1194 .readdirplus = lo_readdirplus,
1195 .releasedir = lo_releasedir,
1196 .fsyncdir = lo_fsyncdir,
1197 .create = lo_create,
1198 .open = lo_open,
1199 .release = lo_release,
1200 .flush = lo_flush,
1201 .fsync = lo_fsync,
1202 .read = lo_read,
1203 .write_buf = lo_write_buf,
1204 .statfs = lo_statfs,
1205 .fallocate = lo_fallocate,
1206 .flock = lo_flock,
1207 .getxattr = lo_getxattr,
1208 .listxattr = lo_listxattr,
1209 .setxattr = lo_setxattr,
1210 .removexattr = lo_removexattr,
1211 #ifdef HAVE_COPY_FILE_RANGE
1212 .copy_file_range = lo_copy_file_range,
1213 #endif
1214 .lseek = lo_lseek,
1215 };
1216
1217 int main(int argc, char *argv[])
1218 {
1219 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1220 struct fuse_session *se;
1221 struct fuse_cmdline_opts opts;
1222 struct lo_data lo = { .debug = 0,
1223 .writeback = 0 };
1224 int ret = -1;
1225
1226 /* Don't mask creation mode, kernel already did that */
1227 umask(0);
1228
1229 pthread_mutex_init(&lo.mutex, NULL);
1230 lo.root.next = lo.root.prev = &lo.root;
1231 lo.root.fd = -1;
1232 lo.cache = CACHE_NORMAL;
1233
1234 if (fuse_parse_cmdline(&args, &opts) != 0)
1235 return 1;
1236 if (opts.show_help) {
1237 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
1238 fuse_cmdline_help();
1239 fuse_lowlevel_help();
1240 ret = 0;
1241 goto err_out1;
1242 } else if (opts.show_version) {
1243 printf("FUSE library version %s\n", fuse_pkgversion());
1244 fuse_lowlevel_version();
1245 ret = 0;
1246 goto err_out1;
1247 }
1248
1249 if(opts.mountpoint == NULL) {
1250 printf("usage: %s [options] <mountpoint>\n", argv[0]);
1251 printf(" %s --help\n", argv[0]);
1252 ret = 1;
1253 goto err_out1;
1254 }
1255
1256 if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
1257 return 1;
1258
1259 lo.debug = opts.debug;
1260 lo.root.refcount = 2;
1261 if (lo.source) {
1262 struct stat stat;
1263 int res;
1264
1265 res = lstat(lo.source, &stat);
1266 if (res == -1) {
1267 fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
1268 lo.source);
1269 exit(1);
1270 }
1271 if (!S_ISDIR(stat.st_mode)) {
1272 fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
1273 exit(1);
1274 }
1275
1276 } else {
1277 lo.source = "/";
1278 }
1279 lo.root.is_symlink = false;
1280 if (!lo.timeout_set) {
1281 switch (lo.cache) {
1282 case CACHE_NEVER:
1283 lo.timeout = 0.0;
1284 break;
1285
1286 case CACHE_NORMAL:
1287 lo.timeout = 1.0;
1288 break;
1289
1290 case CACHE_ALWAYS:
1291 lo.timeout = 86400.0;
1292 break;
1293 }
1294 } else if (lo.timeout < 0) {
1295 fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
1296 lo.timeout);
1297 exit(1);
1298 }
1299
1300 lo.root.fd = open(lo.source, O_PATH);
1301 if (lo.root.fd == -1) {
1302 fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
1303 lo.source);
1304 exit(1);
1305 }
1306
1307 se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
1308 if (se == NULL)
1309 goto err_out1;
1310
1311 if (fuse_set_signal_handlers(se) != 0)
1312 goto err_out2;
1313
1314 if (fuse_session_mount(se, opts.mountpoint) != 0)
1315 goto err_out3;
1316
1317 fuse_daemonize(opts.foreground);
1318
1319 /* Block until ctrl+c or fusermount -u */
1320 if (opts.singlethread)
1321 ret = fuse_session_loop(se);
1322 else
1323 ret = fuse_session_loop_mt(se, opts.clone_fd);
1324
1325 fuse_session_unmount(se);
1326 err_out3:
1327 fuse_remove_signal_handlers(se);
1328 err_out2:
1329 fuse_session_destroy(se);
1330 err_out1:
1331 free(opts.mountpoint);
1332 fuse_opt_free_args(&args);
1333
1334 if (lo.root.fd >= 0)
1335 close(lo.root.fd);
1336
1337 return ret ? 1 : 0;
1338 }
fuse_session_destroy
void fuse_session_destroy(struct fuse_session *se)
Definition: fuse_lowlevel.c:2700
fuse_reply_err
int fuse_reply_err(fuse_req_t req, int err)
Definition: fuse_lowlevel.c:312
fuse_bufvec::off
size_t off
Definition: fuse_common.h:721
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_conn_info::capable
unsigned capable
Definition: fuse_common.h:423
fuse_file_info::fh
uint64_t fh
Definition: fuse_common.h:93
fuse_session_loop
int fuse_session_loop(struct fuse_session *se)
Definition: fuse_loop.c:19
fuse_conn_info
Definition: fuse_common.h:385
fuse_lowlevel_help
void fuse_lowlevel_help(void)
Definition: fuse_lowlevel.c:2690
fuse_file_info::direct_io
unsigned int direct_io
Definition: fuse_common.h:57
FUSE_BUF_IS_FD
Definition: fuse_common.h:597
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_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_daemonize
int fuse_daemonize(int foreground)
Definition: helper.c:225
fuse_session_mount
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
Definition: fuse_lowlevel.c:2981
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_req_t
struct fuse_req * fuse_req_t
Definition: fuse_lowlevel.h:49
fuse_entry_param::attr
struct stat attr
Definition: fuse_lowlevel.h:88
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_BUF_SPLICE_MOVE
Definition: fuse_common.h:648
fuse_file_info::keep_cache
unsigned int keep_cache
Definition: fuse_common.h:64
fuse_entry_param
Definition: fuse_lowlevel.h:59
FUSE_CAP_EXPORT_SUPPORT
#define FUSE_CAP_EXPORT_SUPPORT
Definition: fuse_common.h:165
fuse_entry_param::ino
fuse_ino_t ino
Definition: fuse_lowlevel.h:67
fuse_parse_cmdline
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition: helper.c:202
fuse_reply_xattr
int fuse_reply_xattr(fuse_req_t req, size_t count)
Definition: fuse_lowlevel.c:888
fuse_cmdline_help
void fuse_cmdline_help(void)
Definition: helper.c:129
fuse_reply_entry
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
Definition: fuse_lowlevel.c:401
fuse_set_signal_handlers
int fuse_set_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:62
fuse_reply_lseek
int fuse_reply_lseek(fuse_req_t req, off_t off)
Definition: fuse_lowlevel.c:1068
fuse_buf::pos
off_t pos
Definition: fuse_common.h:696
fuse_remove_signal_handlers
void fuse_remove_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:79
fuse_file_info
Definition: fuse_common.h:44
fuse_lowlevel_version
void fuse_lowlevel_version(void)
Definition: fuse_lowlevel.c:2683
fuse_reply_none
void fuse_reply_none(fuse_req_t req)
Definition: fuse_lowlevel.c:317
fuse_file_info::flags
int flags
Definition: fuse_common.h:46
fuse_bufvec
Definition: fuse_common.h:707
fuse_opt_free_args
void fuse_opt_free_args(struct fuse_args *args)
Definition: fuse_opt.c:34
fuse_reply_attr
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
Definition: fuse_lowlevel.c:433
fuse_session_unmount
void fuse_session_unmount(struct fuse_session *se)
Definition: fuse_lowlevel.c:3036
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_reply_statfs
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
Definition: fuse_lowlevel.c:876
FUSE_ROOT_ID
#define FUSE_ROOT_ID
Definition: fuse_lowlevel.h:43
FUSE_CAP_FLOCK_LOCKS
#define FUSE_CAP_FLOCK_LOCKS
Definition: fuse_common.h:211
fuse_ino_t
uint64_t fuse_ino_t
Definition: fuse_lowlevel.h:46
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_write
int fuse_reply_write(fuse_req_t req, size_t count)
Definition: fuse_lowlevel.c:462
FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_WRITEBACK_CACHE
Definition: fuse_common.h:296
fuse_reply_open
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
Definition: fuse_lowlevel.c:453
fuse_lowlevel.h
fuse_log
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition: fuse_log.c:33
fuse_lowlevel_ops
Definition: fuse_lowlevel.h:172
fuse_bufvec::buf
struct fuse_buf buf[1]
Definition: fuse_common.h:726
fuse_opt
Definition: fuse_opt.h:77
fuse_conn_info::want
unsigned want
Definition: fuse_common.h:431
fuse_args
Definition: fuse_opt.h:109
fuse_pkgversion
const char * fuse_pkgversion(void)
Definition: fuse.c:5117
FUSE_ARGS_INIT
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
FUSE_BUF_FD_SEEK
Definition: fuse_common.h:606
fuse_buf_size
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition: buffer.c:22
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::size
size_t size
Definition: fuse_common.h:670
fuse_entry_param::entry_timeout
double entry_timeout
Definition: fuse_lowlevel.h:100
fuse_buf::fd
int fd
Definition: fuse_common.h:689
fuse_entry_param::attr_timeout
double attr_timeout
Definition: fuse_lowlevel.h:94
fuse_reply_readlink
int fuse_reply_readlink(fuse_req_t req, const char *link)
Definition: fuse_lowlevel.c:448
fuse_reply_buf
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
Definition: fuse_lowlevel.c:472
fuse_lowlevel_ops::init
void(* init)(void *userdata, struct fuse_conn_info *conn)
Definition: fuse_lowlevel.h:189