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