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