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