virtiofsd: passthrough_ll: add ino_map to hide lo_inode pointers
[qemu/kevin.git] / tools / virtiofsd / passthrough_ll.c
bloba3ebf74eab6f8ee75a2b976bac670e4a35d6a596
1 /*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 * This program can be distributed under the terms of the GNU GPLv2.
6 * See the file COPYING.
7 */
9 /*
11 * This file system mirrors the existing file system hierarchy of the
12 * system, starting at the root file system. This is implemented by
13 * just "passing through" all requests to the corresponding user-space
14 * libc functions. In contrast to passthrough.c and passthrough_fh.c,
15 * this implementation uses the low-level API. Its performance should
16 * be the least bad among the three, but many operations are not
17 * implemented. In particular, it is not possible to remove files (or
18 * directories) because the code necessary to defer actual removal
19 * until the file is not opened anymore would make the example much
20 * more complicated.
22 * When writeback caching is enabled (-o writeback mount option), it
23 * is only possible to write to files for which the mounting user has
24 * read permissions. This is because the writeback cache requires the
25 * kernel to be able to issue read requests for all files (which the
26 * passthrough filesystem cannot satisfy if it can't read the file in
27 * the underlying filesystem).
29 * Compile with:
31 * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o
32 * passthrough_ll
34 * ## Source code ##
35 * \include passthrough_ll.c
38 #include "qemu/osdep.h"
39 #include "fuse_virtio.h"
40 #include "fuse_lowlevel.h"
41 #include <assert.h>
42 #include <dirent.h>
43 #include <errno.h>
44 #include <inttypes.h>
45 #include <limits.h>
46 #include <pthread.h>
47 #include <stdbool.h>
48 #include <stddef.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <sys/file.h>
53 #include <sys/syscall.h>
54 #include <sys/xattr.h>
55 #include <unistd.h>
57 #include "passthrough_helpers.h"
60 * We are re-using pointers to our `struct lo_inode`
61 * 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.
65 #if defined(__GNUC__) && \
66 (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \
67 !defined __cplusplus
68 _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
69 "fuse_ino_t too small to hold uintptr_t values!");
70 #else
71 struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct {
72 unsigned _uintptr_to_must_hold_fuse_ino_t
73 : ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1);
75 #endif
77 struct lo_map_elem {
78 union {
79 struct lo_inode *inode;
80 ssize_t freelist;
82 bool in_use;
85 /* Maps FUSE fh or ino values to internal objects */
86 struct lo_map {
87 struct lo_map_elem *elems;
88 size_t nelems;
89 ssize_t freelist;
92 struct lo_inode {
93 struct lo_inode *next; /* protected by lo->mutex */
94 struct lo_inode *prev; /* protected by lo->mutex */
95 int fd;
96 bool is_symlink;
97 ino_t ino;
98 dev_t dev;
99 uint64_t refcount; /* protected by lo->mutex */
100 fuse_ino_t fuse_ino;
103 struct lo_cred {
104 uid_t euid;
105 gid_t egid;
108 enum {
109 CACHE_NEVER,
110 CACHE_NORMAL,
111 CACHE_ALWAYS,
114 struct lo_data {
115 pthread_mutex_t mutex;
116 int debug;
117 int writeback;
118 int flock;
119 int xattr;
120 const char *source;
121 double timeout;
122 int cache;
123 int timeout_set;
124 struct lo_inode root; /* protected by lo->mutex */
125 struct lo_map ino_map; /* protected by lo->mutex */
128 static const struct fuse_opt lo_opts[] = {
129 { "writeback", offsetof(struct lo_data, writeback), 1 },
130 { "no_writeback", offsetof(struct lo_data, writeback), 0 },
131 { "source=%s", offsetof(struct lo_data, source), 0 },
132 { "flock", offsetof(struct lo_data, flock), 1 },
133 { "no_flock", offsetof(struct lo_data, flock), 0 },
134 { "xattr", offsetof(struct lo_data, xattr), 1 },
135 { "no_xattr", offsetof(struct lo_data, xattr), 0 },
136 { "timeout=%lf", offsetof(struct lo_data, timeout), 0 },
137 { "timeout=", offsetof(struct lo_data, timeout_set), 1 },
138 { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER },
139 { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL },
140 { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS },
142 FUSE_OPT_END
145 static struct lo_data *lo_data(fuse_req_t req)
147 return (struct lo_data *)fuse_req_userdata(req);
150 static void lo_map_init(struct lo_map *map)
152 map->elems = NULL;
153 map->nelems = 0;
154 map->freelist = -1;
157 static void lo_map_destroy(struct lo_map *map)
159 free(map->elems);
162 static int lo_map_grow(struct lo_map *map, size_t new_nelems)
164 struct lo_map_elem *new_elems;
165 size_t i;
167 if (new_nelems <= map->nelems) {
168 return 1;
171 new_elems = realloc(map->elems, sizeof(map->elems[0]) * new_nelems);
172 if (!new_elems) {
173 return 0;
176 for (i = map->nelems; i < new_nelems; i++) {
177 new_elems[i].freelist = i + 1;
178 new_elems[i].in_use = false;
180 new_elems[new_nelems - 1].freelist = -1;
182 map->elems = new_elems;
183 map->freelist = map->nelems;
184 map->nelems = new_nelems;
185 return 1;
188 static struct lo_map_elem *lo_map_alloc_elem(struct lo_map *map)
190 struct lo_map_elem *elem;
192 if (map->freelist == -1 && !lo_map_grow(map, map->nelems + 256)) {
193 return NULL;
196 elem = &map->elems[map->freelist];
197 map->freelist = elem->freelist;
199 elem->in_use = true;
201 return elem;
204 static struct lo_map_elem *lo_map_reserve(struct lo_map *map, size_t key)
206 ssize_t *prev;
208 if (!lo_map_grow(map, key + 1)) {
209 return NULL;
212 for (prev = &map->freelist; *prev != -1;
213 prev = &map->elems[*prev].freelist) {
214 if (*prev == key) {
215 struct lo_map_elem *elem = &map->elems[key];
217 *prev = elem->freelist;
218 elem->in_use = true;
219 return elem;
222 return NULL;
225 static struct lo_map_elem *lo_map_get(struct lo_map *map, size_t key)
227 if (key >= map->nelems) {
228 return NULL;
230 if (!map->elems[key].in_use) {
231 return NULL;
233 return &map->elems[key];
236 static void lo_map_remove(struct lo_map *map, size_t key)
238 struct lo_map_elem *elem;
240 if (key >= map->nelems) {
241 return;
244 elem = &map->elems[key];
245 if (!elem->in_use) {
246 return;
249 elem->in_use = false;
251 elem->freelist = map->freelist;
252 map->freelist = key;
255 /* Assumes lo->mutex is held */
256 static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode)
258 struct lo_map_elem *elem;
260 elem = lo_map_alloc_elem(&lo_data(req)->ino_map);
261 if (!elem) {
262 return -1;
265 elem->inode = inode;
266 return elem - lo_data(req)->ino_map.elems;
269 static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
271 struct lo_data *lo = lo_data(req);
272 struct lo_map_elem *elem;
274 pthread_mutex_lock(&lo->mutex);
275 elem = lo_map_get(&lo->ino_map, ino);
276 pthread_mutex_unlock(&lo->mutex);
278 if (!elem) {
279 return NULL;
282 return elem->inode;
285 static int lo_fd(fuse_req_t req, fuse_ino_t ino)
287 struct lo_inode *inode = lo_inode(req, ino);
288 return inode ? inode->fd : -1;
291 static bool lo_debug(fuse_req_t req)
293 return lo_data(req)->debug != 0;
296 static void lo_init(void *userdata, struct fuse_conn_info *conn)
298 struct lo_data *lo = (struct lo_data *)userdata;
300 if (conn->capable & FUSE_CAP_EXPORT_SUPPORT) {
301 conn->want |= FUSE_CAP_EXPORT_SUPPORT;
304 if (lo->writeback && conn->capable & FUSE_CAP_WRITEBACK_CACHE) {
305 if (lo->debug) {
306 fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n");
308 conn->want |= FUSE_CAP_WRITEBACK_CACHE;
310 if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
311 if (lo->debug) {
312 fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n");
314 conn->want |= FUSE_CAP_FLOCK_LOCKS;
318 static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
319 struct fuse_file_info *fi)
321 int res;
322 struct stat buf;
323 struct lo_data *lo = lo_data(req);
325 (void)fi;
327 res =
328 fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
329 if (res == -1) {
330 return (void)fuse_reply_err(req, errno);
333 fuse_reply_attr(req, &buf, lo->timeout);
336 static int utimensat_empty_nofollow(struct lo_inode *inode,
337 const struct timespec *tv)
339 int res;
340 char procname[64];
342 if (inode->is_symlink) {
343 res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
344 if (res == -1 && errno == EINVAL) {
345 /* Sorry, no race free way to set times on symlink. */
346 errno = EPERM;
348 return res;
350 sprintf(procname, "/proc/self/fd/%i", inode->fd);
352 return utimensat(AT_FDCWD, procname, tv, 0);
355 static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
356 int valid, struct fuse_file_info *fi)
358 int saverr;
359 char procname[64];
360 struct lo_inode *inode;
361 int ifd;
362 int res;
364 inode = lo_inode(req, ino);
365 if (!inode) {
366 fuse_reply_err(req, EBADF);
367 return;
370 ifd = inode->fd;
372 if (valid & FUSE_SET_ATTR_MODE) {
373 if (fi) {
374 res = fchmod(fi->fh, attr->st_mode);
375 } else {
376 sprintf(procname, "/proc/self/fd/%i", ifd);
377 res = chmod(procname, attr->st_mode);
379 if (res == -1) {
380 goto out_err;
383 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
384 uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1;
385 gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1;
387 res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
388 if (res == -1) {
389 goto out_err;
392 if (valid & FUSE_SET_ATTR_SIZE) {
393 if (fi) {
394 res = ftruncate(fi->fh, attr->st_size);
395 } else {
396 sprintf(procname, "/proc/self/fd/%i", ifd);
397 res = truncate(procname, attr->st_size);
399 if (res == -1) {
400 goto out_err;
403 if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
404 struct timespec tv[2];
406 tv[0].tv_sec = 0;
407 tv[1].tv_sec = 0;
408 tv[0].tv_nsec = UTIME_OMIT;
409 tv[1].tv_nsec = UTIME_OMIT;
411 if (valid & FUSE_SET_ATTR_ATIME_NOW) {
412 tv[0].tv_nsec = UTIME_NOW;
413 } else if (valid & FUSE_SET_ATTR_ATIME) {
414 tv[0] = attr->st_atim;
417 if (valid & FUSE_SET_ATTR_MTIME_NOW) {
418 tv[1].tv_nsec = UTIME_NOW;
419 } else if (valid & FUSE_SET_ATTR_MTIME) {
420 tv[1] = attr->st_mtim;
423 if (fi) {
424 res = futimens(fi->fh, tv);
425 } else {
426 res = utimensat_empty_nofollow(inode, tv);
428 if (res == -1) {
429 goto out_err;
433 return lo_getattr(req, ino, fi);
435 out_err:
436 saverr = errno;
437 fuse_reply_err(req, saverr);
440 static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
442 struct lo_inode *p;
443 struct lo_inode *ret = NULL;
445 pthread_mutex_lock(&lo->mutex);
446 for (p = lo->root.next; p != &lo->root; p = p->next) {
447 if (p->ino == st->st_ino && p->dev == st->st_dev) {
448 assert(p->refcount > 0);
449 ret = p;
450 ret->refcount++;
451 break;
454 pthread_mutex_unlock(&lo->mutex);
455 return ret;
458 static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
459 struct fuse_entry_param *e)
461 int newfd;
462 int res;
463 int saverr;
464 struct lo_data *lo = lo_data(req);
465 struct lo_inode *inode;
467 memset(e, 0, sizeof(*e));
468 e->attr_timeout = lo->timeout;
469 e->entry_timeout = lo->timeout;
471 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
472 if (newfd == -1) {
473 goto out_err;
476 res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
477 if (res == -1) {
478 goto out_err;
481 inode = lo_find(lo_data(req), &e->attr);
482 if (inode) {
483 close(newfd);
484 newfd = -1;
485 } else {
486 struct lo_inode *prev, *next;
488 saverr = ENOMEM;
489 inode = calloc(1, sizeof(struct lo_inode));
490 if (!inode) {
491 goto out_err;
494 inode->is_symlink = S_ISLNK(e->attr.st_mode);
495 inode->refcount = 1;
496 inode->fd = newfd;
497 inode->ino = e->attr.st_ino;
498 inode->dev = e->attr.st_dev;
500 pthread_mutex_lock(&lo->mutex);
501 inode->fuse_ino = lo_add_inode_mapping(req, inode);
502 prev = &lo->root;
503 next = prev->next;
504 next->prev = inode;
505 inode->next = next;
506 inode->prev = prev;
507 prev->next = inode;
508 pthread_mutex_unlock(&lo->mutex);
510 e->ino = inode->fuse_ino;
512 if (lo_debug(req)) {
513 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
514 (unsigned long long)parent, name, (unsigned long long)e->ino);
517 return 0;
519 out_err:
520 saverr = errno;
521 if (newfd != -1) {
522 close(newfd);
524 return saverr;
527 static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
529 struct fuse_entry_param e;
530 int err;
532 if (lo_debug(req)) {
533 fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
534 parent, name);
537 err = lo_do_lookup(req, parent, name, &e);
538 if (err) {
539 fuse_reply_err(req, err);
540 } else {
541 fuse_reply_entry(req, &e);
546 * On some archs, setres*id is limited to 2^16 but they
547 * provide setres*id32 variants that allow 2^32.
548 * Others just let setres*id do 2^32 anyway.
550 #ifdef SYS_setresgid32
551 #define OURSYS_setresgid SYS_setresgid32
552 #else
553 #define OURSYS_setresgid SYS_setresgid
554 #endif
556 #ifdef SYS_setresuid32
557 #define OURSYS_setresuid SYS_setresuid32
558 #else
559 #define OURSYS_setresuid SYS_setresuid
560 #endif
563 * Change to uid/gid of caller so that file is created with
564 * ownership of caller.
565 * TODO: What about selinux context?
567 static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
569 int res;
571 old->euid = geteuid();
572 old->egid = getegid();
574 res = syscall(OURSYS_setresgid, -1, fuse_req_ctx(req)->gid, -1);
575 if (res == -1) {
576 return errno;
579 res = syscall(OURSYS_setresuid, -1, fuse_req_ctx(req)->uid, -1);
580 if (res == -1) {
581 int errno_save = errno;
583 syscall(OURSYS_setresgid, -1, old->egid, -1);
584 return errno_save;
587 return 0;
590 /* Regain Privileges */
591 static void lo_restore_cred(struct lo_cred *old)
593 int res;
595 res = syscall(OURSYS_setresuid, -1, old->euid, -1);
596 if (res == -1) {
597 fuse_log(FUSE_LOG_ERR, "seteuid(%u): %m\n", old->euid);
598 exit(1);
601 res = syscall(OURSYS_setresgid, -1, old->egid, -1);
602 if (res == -1) {
603 fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid);
604 exit(1);
608 static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
609 const char *name, mode_t mode, dev_t rdev,
610 const char *link)
612 int res;
613 int saverr;
614 struct lo_inode *dir;
615 struct fuse_entry_param e;
616 struct lo_cred old = {};
618 dir = lo_inode(req, parent);
619 if (!dir) {
620 fuse_reply_err(req, EBADF);
621 return;
624 saverr = ENOMEM;
626 saverr = lo_change_cred(req, &old);
627 if (saverr) {
628 goto out;
631 res = mknod_wrapper(dir->fd, name, link, mode, rdev);
633 saverr = errno;
635 lo_restore_cred(&old);
637 if (res == -1) {
638 goto out;
641 saverr = lo_do_lookup(req, parent, name, &e);
642 if (saverr) {
643 goto out;
646 if (lo_debug(req)) {
647 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
648 (unsigned long long)parent, name, (unsigned long long)e.ino);
651 fuse_reply_entry(req, &e);
652 return;
654 out:
655 fuse_reply_err(req, saverr);
658 static void lo_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
659 mode_t mode, dev_t rdev)
661 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
664 static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
665 mode_t mode)
667 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
670 static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent,
671 const char *name)
673 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
676 static int linkat_empty_nofollow(struct lo_inode *inode, int dfd,
677 const char *name)
679 int res;
680 char procname[64];
682 if (inode->is_symlink) {
683 res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH);
684 if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
685 /* Sorry, no race free way to hard-link a symlink. */
686 errno = EPERM;
688 return res;
691 sprintf(procname, "/proc/self/fd/%i", inode->fd);
693 return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
696 static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
697 const char *name)
699 int res;
700 struct lo_data *lo = lo_data(req);
701 struct lo_inode *inode;
702 struct fuse_entry_param e;
703 int saverr;
705 inode = lo_inode(req, ino);
706 if (!inode) {
707 fuse_reply_err(req, EBADF);
708 return;
711 memset(&e, 0, sizeof(struct fuse_entry_param));
712 e.attr_timeout = lo->timeout;
713 e.entry_timeout = lo->timeout;
715 res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
716 if (res == -1) {
717 goto out_err;
720 res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
721 if (res == -1) {
722 goto out_err;
725 pthread_mutex_lock(&lo->mutex);
726 inode->refcount++;
727 pthread_mutex_unlock(&lo->mutex);
728 e.ino = inode->fuse_ino;
730 if (lo_debug(req)) {
731 fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n",
732 (unsigned long long)parent, name, (unsigned long long)e.ino);
735 fuse_reply_entry(req, &e);
736 return;
738 out_err:
739 saverr = errno;
740 fuse_reply_err(req, saverr);
743 static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
745 int res;
747 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
749 fuse_reply_err(req, res == -1 ? errno : 0);
752 static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
753 fuse_ino_t newparent, const char *newname,
754 unsigned int flags)
756 int res;
758 if (flags) {
759 fuse_reply_err(req, EINVAL);
760 return;
763 res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname);
765 fuse_reply_err(req, res == -1 ? errno : 0);
768 static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
770 int res;
772 res = unlinkat(lo_fd(req, parent), name, 0);
774 fuse_reply_err(req, res == -1 ? errno : 0);
777 static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
779 if (!inode) {
780 return;
783 pthread_mutex_lock(&lo->mutex);
784 assert(inode->refcount >= n);
785 inode->refcount -= n;
786 if (!inode->refcount) {
787 struct lo_inode *prev, *next;
789 prev = inode->prev;
790 next = inode->next;
791 next->prev = prev;
792 prev->next = next;
794 lo_map_remove(&lo->ino_map, inode->fuse_ino);
795 pthread_mutex_unlock(&lo->mutex);
796 close(inode->fd);
797 free(inode);
798 } else {
799 pthread_mutex_unlock(&lo->mutex);
803 static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
805 struct lo_data *lo = lo_data(req);
806 struct lo_inode *inode;
808 inode = lo_inode(req, ino);
809 if (!inode) {
810 return;
813 if (lo_debug(req)) {
814 fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n",
815 (unsigned long long)ino, (unsigned long long)inode->refcount,
816 (unsigned long long)nlookup);
819 unref_inode(lo, inode, nlookup);
822 static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
824 lo_forget_one(req, ino, nlookup);
825 fuse_reply_none(req);
828 static void lo_forget_multi(fuse_req_t req, size_t count,
829 struct fuse_forget_data *forgets)
831 int i;
833 for (i = 0; i < count; i++) {
834 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
836 fuse_reply_none(req);
839 static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
841 char buf[PATH_MAX + 1];
842 int res;
844 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
845 if (res == -1) {
846 return (void)fuse_reply_err(req, errno);
849 if (res == sizeof(buf)) {
850 return (void)fuse_reply_err(req, ENAMETOOLONG);
853 buf[res] = '\0';
855 fuse_reply_readlink(req, buf);
858 struct lo_dirp {
859 DIR *dp;
860 struct dirent *entry;
861 off_t offset;
864 static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
866 return (struct lo_dirp *)(uintptr_t)fi->fh;
869 static void lo_opendir(fuse_req_t req, fuse_ino_t ino,
870 struct fuse_file_info *fi)
872 int error = ENOMEM;
873 struct lo_data *lo = lo_data(req);
874 struct lo_dirp *d;
875 int fd;
877 d = calloc(1, sizeof(struct lo_dirp));
878 if (d == NULL) {
879 goto out_err;
882 fd = openat(lo_fd(req, ino), ".", O_RDONLY);
883 if (fd == -1) {
884 goto out_errno;
887 d->dp = fdopendir(fd);
888 if (d->dp == NULL) {
889 goto out_errno;
892 d->offset = 0;
893 d->entry = NULL;
895 fi->fh = (uintptr_t)d;
896 if (lo->cache == CACHE_ALWAYS) {
897 fi->keep_cache = 1;
899 fuse_reply_open(req, fi);
900 return;
902 out_errno:
903 error = errno;
904 out_err:
905 if (d) {
906 if (fd != -1) {
907 close(fd);
909 free(d);
911 fuse_reply_err(req, error);
914 static int is_dot_or_dotdot(const char *name)
916 return name[0] == '.' &&
917 (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
920 static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
921 off_t offset, struct fuse_file_info *fi, int plus)
923 struct lo_dirp *d = lo_dirp(fi);
924 char *buf;
925 char *p;
926 size_t rem = size;
927 int err;
929 (void)ino;
931 buf = calloc(1, size);
932 if (!buf) {
933 err = ENOMEM;
934 goto error;
936 p = buf;
938 if (offset != d->offset) {
939 seekdir(d->dp, offset);
940 d->entry = NULL;
941 d->offset = offset;
943 while (1) {
944 size_t entsize;
945 off_t nextoff;
946 const char *name;
948 if (!d->entry) {
949 errno = 0;
950 d->entry = readdir(d->dp);
951 if (!d->entry) {
952 if (errno) { /* Error */
953 err = errno;
954 goto error;
955 } else { /* End of stream */
956 break;
960 nextoff = d->entry->d_off;
961 name = d->entry->d_name;
962 fuse_ino_t entry_ino = 0;
963 if (plus) {
964 struct fuse_entry_param e;
965 if (is_dot_or_dotdot(name)) {
966 e = (struct fuse_entry_param){
967 .attr.st_ino = d->entry->d_ino,
968 .attr.st_mode = d->entry->d_type << 12,
970 } else {
971 err = lo_do_lookup(req, ino, name, &e);
972 if (err) {
973 goto error;
975 entry_ino = e.ino;
978 entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff);
979 } else {
980 struct stat st = {
981 .st_ino = d->entry->d_ino,
982 .st_mode = d->entry->d_type << 12,
984 entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff);
986 if (entsize > rem) {
987 if (entry_ino != 0) {
988 lo_forget_one(req, entry_ino, 1);
990 break;
993 p += entsize;
994 rem -= entsize;
996 d->entry = NULL;
997 d->offset = nextoff;
1000 err = 0;
1001 error:
1003 * If there's an error, we can only signal it if we haven't stored
1004 * any entries yet - otherwise we'd end up with wrong lookup
1005 * counts for the entries that are already in the buffer. So we
1006 * return what we've collected until that point.
1008 if (err && rem == size) {
1009 fuse_reply_err(req, err);
1010 } else {
1011 fuse_reply_buf(req, buf, size - rem);
1013 free(buf);
1016 static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
1017 off_t offset, struct fuse_file_info *fi)
1019 lo_do_readdir(req, ino, size, offset, fi, 0);
1022 static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
1023 off_t offset, struct fuse_file_info *fi)
1025 lo_do_readdir(req, ino, size, offset, fi, 1);
1028 static void lo_releasedir(fuse_req_t req, fuse_ino_t ino,
1029 struct fuse_file_info *fi)
1031 struct lo_dirp *d = lo_dirp(fi);
1032 (void)ino;
1033 closedir(d->dp);
1034 free(d);
1035 fuse_reply_err(req, 0);
1038 static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
1039 mode_t mode, struct fuse_file_info *fi)
1041 int fd;
1042 struct lo_data *lo = lo_data(req);
1043 struct fuse_entry_param e;
1044 int err;
1045 struct lo_cred old = {};
1047 if (lo_debug(req)) {
1048 fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
1049 parent, name);
1052 err = lo_change_cred(req, &old);
1053 if (err) {
1054 goto out;
1057 fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW,
1058 mode);
1059 err = fd == -1 ? errno : 0;
1060 lo_restore_cred(&old);
1062 if (!err) {
1063 fi->fh = fd;
1064 err = lo_do_lookup(req, parent, name, &e);
1066 if (lo->cache == CACHE_NEVER) {
1067 fi->direct_io = 1;
1068 } else if (lo->cache == CACHE_ALWAYS) {
1069 fi->keep_cache = 1;
1072 out:
1073 if (err) {
1074 fuse_reply_err(req, err);
1075 } else {
1076 fuse_reply_create(req, &e, fi);
1080 static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
1081 struct fuse_file_info *fi)
1083 int res;
1084 int fd = dirfd(lo_dirp(fi)->dp);
1085 (void)ino;
1086 if (datasync) {
1087 res = fdatasync(fd);
1088 } else {
1089 res = fsync(fd);
1091 fuse_reply_err(req, res == -1 ? errno : 0);
1094 static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
1096 int fd;
1097 char buf[64];
1098 struct lo_data *lo = lo_data(req);
1100 if (lo_debug(req)) {
1101 fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino,
1102 fi->flags);
1106 * With writeback cache, kernel may send read requests even
1107 * when userspace opened write-only
1109 if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
1110 fi->flags &= ~O_ACCMODE;
1111 fi->flags |= O_RDWR;
1115 * With writeback cache, O_APPEND is handled by the kernel.
1116 * This breaks atomicity (since the file may change in the
1117 * underlying filesystem, so that the kernel's idea of the
1118 * end of the file isn't accurate anymore). In this example,
1119 * we just accept that. A more rigorous filesystem may want
1120 * to return an error here
1122 if (lo->writeback && (fi->flags & O_APPEND)) {
1123 fi->flags &= ~O_APPEND;
1126 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
1127 fd = open(buf, fi->flags & ~O_NOFOLLOW);
1128 if (fd == -1) {
1129 return (void)fuse_reply_err(req, errno);
1132 fi->fh = fd;
1133 if (lo->cache == CACHE_NEVER) {
1134 fi->direct_io = 1;
1135 } else if (lo->cache == CACHE_ALWAYS) {
1136 fi->keep_cache = 1;
1138 fuse_reply_open(req, fi);
1141 static void lo_release(fuse_req_t req, fuse_ino_t ino,
1142 struct fuse_file_info *fi)
1144 (void)ino;
1146 close(fi->fh);
1147 fuse_reply_err(req, 0);
1150 static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
1152 int res;
1153 (void)ino;
1154 res = close(dup(fi->fh));
1155 fuse_reply_err(req, res == -1 ? errno : 0);
1158 static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
1159 struct fuse_file_info *fi)
1161 int res;
1162 (void)ino;
1163 int fd;
1164 char *buf;
1166 fuse_log(FUSE_LOG_DEBUG, "lo_fsync(ino=%" PRIu64 ", fi=0x%p)\n", ino,
1167 (void *)fi);
1169 if (!fi) {
1170 res = asprintf(&buf, "/proc/self/fd/%i", lo_fd(req, ino));
1171 if (res == -1) {
1172 return (void)fuse_reply_err(req, errno);
1175 fd = open(buf, O_RDWR);
1176 free(buf);
1177 if (fd == -1) {
1178 return (void)fuse_reply_err(req, errno);
1180 } else {
1181 fd = fi->fh;
1184 if (datasync) {
1185 res = fdatasync(fd);
1186 } else {
1187 res = fsync(fd);
1189 if (!fi) {
1190 close(fd);
1192 fuse_reply_err(req, res == -1 ? errno : 0);
1195 static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset,
1196 struct fuse_file_info *fi)
1198 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
1200 if (lo_debug(req)) {
1201 fuse_log(FUSE_LOG_DEBUG,
1202 "lo_read(ino=%" PRIu64 ", size=%zd, "
1203 "off=%lu)\n",
1204 ino, size, (unsigned long)offset);
1207 buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
1208 buf.buf[0].fd = fi->fh;
1209 buf.buf[0].pos = offset;
1211 fuse_reply_data(req, &buf);
1214 static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
1215 struct fuse_bufvec *in_buf, off_t off,
1216 struct fuse_file_info *fi)
1218 (void)ino;
1219 ssize_t res;
1220 struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
1222 out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
1223 out_buf.buf[0].fd = fi->fh;
1224 out_buf.buf[0].pos = off;
1226 if (lo_debug(req)) {
1227 fuse_log(FUSE_LOG_DEBUG,
1228 "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino,
1229 out_buf.buf[0].size, (unsigned long)off);
1232 res = fuse_buf_copy(&out_buf, in_buf);
1233 if (res < 0) {
1234 fuse_reply_err(req, -res);
1235 } else {
1236 fuse_reply_write(req, (size_t)res);
1240 static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
1242 int res;
1243 struct statvfs stbuf;
1245 res = fstatvfs(lo_fd(req, ino), &stbuf);
1246 if (res == -1) {
1247 fuse_reply_err(req, errno);
1248 } else {
1249 fuse_reply_statfs(req, &stbuf);
1253 static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset,
1254 off_t length, struct fuse_file_info *fi)
1256 int err = EOPNOTSUPP;
1257 (void)ino;
1259 #ifdef CONFIG_FALLOCATE
1260 err = fallocate(fi->fh, mode, offset, length);
1261 if (err < 0) {
1262 err = errno;
1265 #elif defined(CONFIG_POSIX_FALLOCATE)
1266 if (mode) {
1267 fuse_reply_err(req, EOPNOTSUPP);
1268 return;
1271 err = posix_fallocate(fi->fh, offset, length);
1272 #endif
1274 fuse_reply_err(req, err);
1277 static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
1278 int op)
1280 int res;
1281 (void)ino;
1283 res = flock(fi->fh, op);
1285 fuse_reply_err(req, res == -1 ? errno : 0);
1288 static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1289 size_t size)
1291 char *value = NULL;
1292 char procname[64];
1293 struct lo_inode *inode;
1294 ssize_t ret;
1295 int saverr;
1297 inode = lo_inode(req, ino);
1298 if (!inode) {
1299 fuse_reply_err(req, EBADF);
1300 return;
1303 saverr = ENOSYS;
1304 if (!lo_data(req)->xattr) {
1305 goto out;
1308 if (lo_debug(req)) {
1309 fuse_log(FUSE_LOG_DEBUG,
1310 "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name,
1311 size);
1314 if (inode->is_symlink) {
1315 /* Sorry, no race free way to getxattr on symlink. */
1316 saverr = EPERM;
1317 goto out;
1320 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1322 if (size) {
1323 value = malloc(size);
1324 if (!value) {
1325 goto out_err;
1328 ret = getxattr(procname, name, value, size);
1329 if (ret == -1) {
1330 goto out_err;
1332 saverr = 0;
1333 if (ret == 0) {
1334 goto out;
1337 fuse_reply_buf(req, value, ret);
1338 } else {
1339 ret = getxattr(procname, name, NULL, 0);
1340 if (ret == -1) {
1341 goto out_err;
1344 fuse_reply_xattr(req, ret);
1346 out_free:
1347 free(value);
1348 return;
1350 out_err:
1351 saverr = errno;
1352 out:
1353 fuse_reply_err(req, saverr);
1354 goto out_free;
1357 static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
1359 char *value = NULL;
1360 char procname[64];
1361 struct lo_inode *inode;
1362 ssize_t ret;
1363 int saverr;
1365 inode = lo_inode(req, ino);
1366 if (!inode) {
1367 fuse_reply_err(req, EBADF);
1368 return;
1371 saverr = ENOSYS;
1372 if (!lo_data(req)->xattr) {
1373 goto out;
1376 if (lo_debug(req)) {
1377 fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
1378 ino, size);
1381 if (inode->is_symlink) {
1382 /* Sorry, no race free way to listxattr on symlink. */
1383 saverr = EPERM;
1384 goto out;
1387 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1389 if (size) {
1390 value = malloc(size);
1391 if (!value) {
1392 goto out_err;
1395 ret = listxattr(procname, value, size);
1396 if (ret == -1) {
1397 goto out_err;
1399 saverr = 0;
1400 if (ret == 0) {
1401 goto out;
1404 fuse_reply_buf(req, value, ret);
1405 } else {
1406 ret = listxattr(procname, NULL, 0);
1407 if (ret == -1) {
1408 goto out_err;
1411 fuse_reply_xattr(req, ret);
1413 out_free:
1414 free(value);
1415 return;
1417 out_err:
1418 saverr = errno;
1419 out:
1420 fuse_reply_err(req, saverr);
1421 goto out_free;
1424 static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1425 const char *value, size_t size, int flags)
1427 char procname[64];
1428 struct lo_inode *inode;
1429 ssize_t ret;
1430 int saverr;
1432 inode = lo_inode(req, ino);
1433 if (!inode) {
1434 fuse_reply_err(req, EBADF);
1435 return;
1438 saverr = ENOSYS;
1439 if (!lo_data(req)->xattr) {
1440 goto out;
1443 if (lo_debug(req)) {
1444 fuse_log(FUSE_LOG_DEBUG,
1445 "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
1446 ino, name, value, size);
1449 if (inode->is_symlink) {
1450 /* Sorry, no race free way to setxattr on symlink. */
1451 saverr = EPERM;
1452 goto out;
1455 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1457 ret = setxattr(procname, name, value, size, flags);
1458 saverr = ret == -1 ? errno : 0;
1460 out:
1461 fuse_reply_err(req, saverr);
1464 static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
1466 char procname[64];
1467 struct lo_inode *inode;
1468 ssize_t ret;
1469 int saverr;
1471 inode = lo_inode(req, ino);
1472 if (!inode) {
1473 fuse_reply_err(req, EBADF);
1474 return;
1477 saverr = ENOSYS;
1478 if (!lo_data(req)->xattr) {
1479 goto out;
1482 if (lo_debug(req)) {
1483 fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
1484 ino, name);
1487 if (inode->is_symlink) {
1488 /* Sorry, no race free way to setxattr on symlink. */
1489 saverr = EPERM;
1490 goto out;
1493 sprintf(procname, "/proc/self/fd/%i", inode->fd);
1495 ret = removexattr(procname, name);
1496 saverr = ret == -1 ? errno : 0;
1498 out:
1499 fuse_reply_err(req, saverr);
1502 #ifdef HAVE_COPY_FILE_RANGE
1503 static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
1504 struct fuse_file_info *fi_in, fuse_ino_t ino_out,
1505 off_t off_out, struct fuse_file_info *fi_out,
1506 size_t len, int flags)
1508 ssize_t res;
1510 if (lo_debug(req))
1511 fuse_log(FUSE_LOG_DEBUG,
1512 "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
1513 "off=%lu, ino=%" PRIu64 "/fd=%lu, "
1514 "off=%lu, size=%zd, flags=0x%x)\n",
1515 ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, len,
1516 flags);
1518 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags);
1519 if (res < 0) {
1520 fuse_reply_err(req, -errno);
1521 } else {
1522 fuse_reply_write(req, res);
1525 #endif
1527 static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
1528 struct fuse_file_info *fi)
1530 off_t res;
1532 (void)ino;
1533 res = lseek(fi->fh, off, whence);
1534 if (res != -1) {
1535 fuse_reply_lseek(req, res);
1536 } else {
1537 fuse_reply_err(req, errno);
1541 static struct fuse_lowlevel_ops lo_oper = {
1542 .init = lo_init,
1543 .lookup = lo_lookup,
1544 .mkdir = lo_mkdir,
1545 .mknod = lo_mknod,
1546 .symlink = lo_symlink,
1547 .link = lo_link,
1548 .unlink = lo_unlink,
1549 .rmdir = lo_rmdir,
1550 .rename = lo_rename,
1551 .forget = lo_forget,
1552 .forget_multi = lo_forget_multi,
1553 .getattr = lo_getattr,
1554 .setattr = lo_setattr,
1555 .readlink = lo_readlink,
1556 .opendir = lo_opendir,
1557 .readdir = lo_readdir,
1558 .readdirplus = lo_readdirplus,
1559 .releasedir = lo_releasedir,
1560 .fsyncdir = lo_fsyncdir,
1561 .create = lo_create,
1562 .open = lo_open,
1563 .release = lo_release,
1564 .flush = lo_flush,
1565 .fsync = lo_fsync,
1566 .read = lo_read,
1567 .write_buf = lo_write_buf,
1568 .statfs = lo_statfs,
1569 .fallocate = lo_fallocate,
1570 .flock = lo_flock,
1571 .getxattr = lo_getxattr,
1572 .listxattr = lo_listxattr,
1573 .setxattr = lo_setxattr,
1574 .removexattr = lo_removexattr,
1575 #ifdef HAVE_COPY_FILE_RANGE
1576 .copy_file_range = lo_copy_file_range,
1577 #endif
1578 .lseek = lo_lseek,
1581 /* Print vhost-user.json backend program capabilities */
1582 static void print_capabilities(void)
1584 printf("{\n");
1585 printf(" \"type\": \"fs\"\n");
1586 printf("}\n");
1589 int main(int argc, char *argv[])
1591 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1592 struct fuse_session *se;
1593 struct fuse_cmdline_opts opts;
1594 struct lo_data lo = { .debug = 0, .writeback = 0 };
1595 struct lo_map_elem *root_elem;
1596 int ret = -1;
1598 /* Don't mask creation mode, kernel already did that */
1599 umask(0);
1601 pthread_mutex_init(&lo.mutex, NULL);
1602 lo.root.next = lo.root.prev = &lo.root;
1603 lo.root.fd = -1;
1604 lo.root.fuse_ino = FUSE_ROOT_ID;
1605 lo.cache = CACHE_NORMAL;
1608 * Set up the ino map like this:
1609 * [0] Reserved (will not be used)
1610 * [1] Root inode
1612 lo_map_init(&lo.ino_map);
1613 lo_map_reserve(&lo.ino_map, 0)->in_use = false;
1614 root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino);
1615 root_elem->inode = &lo.root;
1617 if (fuse_parse_cmdline(&args, &opts) != 0) {
1618 return 1;
1620 if (opts.show_help) {
1621 printf("usage: %s [options]\n\n", argv[0]);
1622 fuse_cmdline_help();
1623 printf(" -o source=PATH shared directory tree\n");
1624 fuse_lowlevel_help();
1625 ret = 0;
1626 goto err_out1;
1627 } else if (opts.show_version) {
1628 fuse_lowlevel_version();
1629 ret = 0;
1630 goto err_out1;
1631 } else if (opts.print_capabilities) {
1632 print_capabilities();
1633 ret = 0;
1634 goto err_out1;
1637 if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) {
1638 return 1;
1641 lo.debug = opts.debug;
1642 lo.root.refcount = 2;
1643 if (lo.source) {
1644 struct stat stat;
1645 int res;
1647 res = lstat(lo.source, &stat);
1648 if (res == -1) {
1649 fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
1650 lo.source);
1651 exit(1);
1653 if (!S_ISDIR(stat.st_mode)) {
1654 fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
1655 exit(1);
1658 } else {
1659 lo.source = "/";
1661 lo.root.is_symlink = false;
1662 if (!lo.timeout_set) {
1663 switch (lo.cache) {
1664 case CACHE_NEVER:
1665 lo.timeout = 0.0;
1666 break;
1668 case CACHE_NORMAL:
1669 lo.timeout = 1.0;
1670 break;
1672 case CACHE_ALWAYS:
1673 lo.timeout = 86400.0;
1674 break;
1676 } else if (lo.timeout < 0) {
1677 fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout);
1678 exit(1);
1681 lo.root.fd = open(lo.source, O_PATH);
1682 if (lo.root.fd == -1) {
1683 fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source);
1684 exit(1);
1687 se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
1688 if (se == NULL) {
1689 goto err_out1;
1692 if (fuse_set_signal_handlers(se) != 0) {
1693 goto err_out2;
1696 if (fuse_session_mount(se) != 0) {
1697 goto err_out3;
1700 fuse_daemonize(opts.foreground);
1702 /* Block until ctrl+c or fusermount -u */
1703 ret = virtio_loop(se);
1705 fuse_session_unmount(se);
1706 err_out3:
1707 fuse_remove_signal_handlers(se);
1708 err_out2:
1709 fuse_session_destroy(se);
1710 err_out1:
1711 fuse_opt_free_args(&args);
1713 lo_map_destroy(&lo.ino_map);
1715 if (lo.root.fd >= 0) {
1716 close(lo.root.fd);
1719 return ret ? 1 : 0;