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