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