2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 * This program can be distributed under the terms of the GNU GPLv2.
6 * See the file COPYING.
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
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).
31 * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o
35 * \include passthrough_ll.c
39 #define FUSE_USE_VERSION 31
46 #include <fuse_lowlevel.h>
56 #include <sys/xattr.h>
59 #include "passthrough_helpers.h"
62 * We are re-using pointers to our `struct lo_inode` and `struct
63 * lo_dirp` elements as inodes. This means that we must be able to
64 * store uintptr_t values in a fuse_ino_t variable. The following
65 * incantation checks this condition at compile time.
67 #if defined(__GNUC__) && \
68 (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \
70 _Static_assert(sizeof(fuse_ino_t
) >= sizeof(uintptr_t),
71 "fuse_ino_t too small to hold uintptr_t values!");
73 struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct
{
74 unsigned _uintptr_to_must_hold_fuse_ino_t
75 : ((sizeof(fuse_ino_t
) >= sizeof(uintptr_t)) ? 1 : -1);
80 struct lo_inode
*next
; /* protected by lo->mutex */
81 struct lo_inode
*prev
; /* protected by lo->mutex */
86 uint64_t refcount
; /* protected by lo->mutex */
96 pthread_mutex_t mutex
;
105 struct lo_inode root
; /* protected by lo->mutex */
108 static const struct fuse_opt lo_opts
[] = {
109 { "writeback", offsetof(struct lo_data
, writeback
), 1 },
110 { "no_writeback", offsetof(struct lo_data
, writeback
), 0 },
111 { "source=%s", offsetof(struct lo_data
, source
), 0 },
112 { "flock", offsetof(struct lo_data
, flock
), 1 },
113 { "no_flock", offsetof(struct lo_data
, flock
), 0 },
114 { "xattr", offsetof(struct lo_data
, xattr
), 1 },
115 { "no_xattr", offsetof(struct lo_data
, xattr
), 0 },
116 { "timeout=%lf", offsetof(struct lo_data
, timeout
), 0 },
117 { "timeout=", offsetof(struct lo_data
, timeout_set
), 1 },
118 { "cache=never", offsetof(struct lo_data
, cache
), CACHE_NEVER
},
119 { "cache=auto", offsetof(struct lo_data
, cache
), CACHE_NORMAL
},
120 { "cache=always", offsetof(struct lo_data
, cache
), CACHE_ALWAYS
},
125 static struct lo_data
*lo_data(fuse_req_t req
)
127 return (struct lo_data
*)fuse_req_userdata(req
);
130 static struct lo_inode
*lo_inode(fuse_req_t req
, fuse_ino_t ino
)
132 if (ino
== FUSE_ROOT_ID
) {
133 return &lo_data(req
)->root
;
135 return (struct lo_inode
*)(uintptr_t)ino
;
139 static int lo_fd(fuse_req_t req
, fuse_ino_t ino
)
141 return lo_inode(req
, ino
)->fd
;
144 static bool lo_debug(fuse_req_t req
)
146 return lo_data(req
)->debug
!= 0;
149 static void lo_init(void *userdata
, struct fuse_conn_info
*conn
)
151 struct lo_data
*lo
= (struct lo_data
*)userdata
;
153 if (conn
->capable
& FUSE_CAP_EXPORT_SUPPORT
) {
154 conn
->want
|= FUSE_CAP_EXPORT_SUPPORT
;
157 if (lo
->writeback
&& conn
->capable
& FUSE_CAP_WRITEBACK_CACHE
) {
159 fuse_log(FUSE_LOG_DEBUG
, "lo_init: activating writeback\n");
161 conn
->want
|= FUSE_CAP_WRITEBACK_CACHE
;
163 if (lo
->flock
&& conn
->capable
& FUSE_CAP_FLOCK_LOCKS
) {
165 fuse_log(FUSE_LOG_DEBUG
, "lo_init: activating flock locks\n");
167 conn
->want
|= FUSE_CAP_FLOCK_LOCKS
;
171 static void lo_getattr(fuse_req_t req
, fuse_ino_t ino
,
172 struct fuse_file_info
*fi
)
176 struct lo_data
*lo
= lo_data(req
);
181 fstatat(lo_fd(req
, ino
), "", &buf
, AT_EMPTY_PATH
| AT_SYMLINK_NOFOLLOW
);
183 return (void)fuse_reply_err(req
, errno
);
186 fuse_reply_attr(req
, &buf
, lo
->timeout
);
189 static int utimensat_empty_nofollow(struct lo_inode
*inode
,
190 const struct timespec
*tv
)
195 if (inode
->is_symlink
) {
196 res
= utimensat(inode
->fd
, "", tv
, AT_EMPTY_PATH
| AT_SYMLINK_NOFOLLOW
);
197 if (res
== -1 && errno
== EINVAL
) {
198 /* Sorry, no race free way to set times on symlink. */
203 sprintf(procname
, "/proc/self/fd/%i", inode
->fd
);
205 return utimensat(AT_FDCWD
, procname
, tv
, 0);
208 static void lo_setattr(fuse_req_t req
, fuse_ino_t ino
, struct stat
*attr
,
209 int valid
, struct fuse_file_info
*fi
)
213 struct lo_inode
*inode
= lo_inode(req
, ino
);
217 if (valid
& FUSE_SET_ATTR_MODE
) {
219 res
= fchmod(fi
->fh
, attr
->st_mode
);
221 sprintf(procname
, "/proc/self/fd/%i", ifd
);
222 res
= chmod(procname
, attr
->st_mode
);
228 if (valid
& (FUSE_SET_ATTR_UID
| FUSE_SET_ATTR_GID
)) {
229 uid_t uid
= (valid
& FUSE_SET_ATTR_UID
) ? attr
->st_uid
: (uid_t
)-1;
230 gid_t gid
= (valid
& FUSE_SET_ATTR_GID
) ? attr
->st_gid
: (gid_t
)-1;
232 res
= fchownat(ifd
, "", uid
, gid
, AT_EMPTY_PATH
| AT_SYMLINK_NOFOLLOW
);
237 if (valid
& FUSE_SET_ATTR_SIZE
) {
239 res
= ftruncate(fi
->fh
, attr
->st_size
);
241 sprintf(procname
, "/proc/self/fd/%i", ifd
);
242 res
= truncate(procname
, attr
->st_size
);
248 if (valid
& (FUSE_SET_ATTR_ATIME
| FUSE_SET_ATTR_MTIME
)) {
249 struct timespec tv
[2];
253 tv
[0].tv_nsec
= UTIME_OMIT
;
254 tv
[1].tv_nsec
= UTIME_OMIT
;
256 if (valid
& FUSE_SET_ATTR_ATIME_NOW
) {
257 tv
[0].tv_nsec
= UTIME_NOW
;
258 } else if (valid
& FUSE_SET_ATTR_ATIME
) {
259 tv
[0] = attr
->st_atim
;
262 if (valid
& FUSE_SET_ATTR_MTIME_NOW
) {
263 tv
[1].tv_nsec
= UTIME_NOW
;
264 } else if (valid
& FUSE_SET_ATTR_MTIME
) {
265 tv
[1] = attr
->st_mtim
;
269 res
= futimens(fi
->fh
, tv
);
271 res
= utimensat_empty_nofollow(inode
, tv
);
278 return lo_getattr(req
, ino
, fi
);
282 fuse_reply_err(req
, saverr
);
285 static struct lo_inode
*lo_find(struct lo_data
*lo
, struct stat
*st
)
288 struct lo_inode
*ret
= NULL
;
290 pthread_mutex_lock(&lo
->mutex
);
291 for (p
= lo
->root
.next
; p
!= &lo
->root
; p
= p
->next
) {
292 if (p
->ino
== st
->st_ino
&& p
->dev
== st
->st_dev
) {
293 assert(p
->refcount
> 0);
299 pthread_mutex_unlock(&lo
->mutex
);
303 static int lo_do_lookup(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
304 struct fuse_entry_param
*e
)
309 struct lo_data
*lo
= lo_data(req
);
310 struct lo_inode
*inode
;
312 memset(e
, 0, sizeof(*e
));
313 e
->attr_timeout
= lo
->timeout
;
314 e
->entry_timeout
= lo
->timeout
;
316 newfd
= openat(lo_fd(req
, parent
), name
, O_PATH
| O_NOFOLLOW
);
321 res
= fstatat(newfd
, "", &e
->attr
, AT_EMPTY_PATH
| AT_SYMLINK_NOFOLLOW
);
326 inode
= lo_find(lo_data(req
), &e
->attr
);
331 struct lo_inode
*prev
, *next
;
334 inode
= calloc(1, sizeof(struct lo_inode
));
339 inode
->is_symlink
= S_ISLNK(e
->attr
.st_mode
);
342 inode
->ino
= e
->attr
.st_ino
;
343 inode
->dev
= e
->attr
.st_dev
;
345 pthread_mutex_lock(&lo
->mutex
);
352 pthread_mutex_unlock(&lo
->mutex
);
354 e
->ino
= (uintptr_t)inode
;
357 fuse_log(FUSE_LOG_DEBUG
, " %lli/%s -> %lli\n",
358 (unsigned long long)parent
, name
, (unsigned long long)e
->ino
);
371 static void lo_lookup(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
373 struct fuse_entry_param e
;
377 fuse_log(FUSE_LOG_DEBUG
, "lo_lookup(parent=%" PRIu64
", name=%s)\n",
381 err
= lo_do_lookup(req
, parent
, name
, &e
);
383 fuse_reply_err(req
, err
);
385 fuse_reply_entry(req
, &e
);
389 static void lo_mknod_symlink(fuse_req_t req
, fuse_ino_t parent
,
390 const char *name
, mode_t mode
, dev_t rdev
,
395 struct lo_inode
*dir
= lo_inode(req
, parent
);
396 struct fuse_entry_param e
;
400 res
= mknod_wrapper(dir
->fd
, name
, link
, mode
, rdev
);
407 saverr
= lo_do_lookup(req
, parent
, name
, &e
);
413 fuse_log(FUSE_LOG_DEBUG
, " %lli/%s -> %lli\n",
414 (unsigned long long)parent
, name
, (unsigned long long)e
.ino
);
417 fuse_reply_entry(req
, &e
);
421 fuse_reply_err(req
, saverr
);
424 static void lo_mknod(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
425 mode_t mode
, dev_t rdev
)
427 lo_mknod_symlink(req
, parent
, name
, mode
, rdev
, NULL
);
430 static void lo_mkdir(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
433 lo_mknod_symlink(req
, parent
, name
, S_IFDIR
| mode
, 0, NULL
);
436 static void lo_symlink(fuse_req_t req
, const char *link
, fuse_ino_t parent
,
439 lo_mknod_symlink(req
, parent
, name
, S_IFLNK
, 0, link
);
442 static int linkat_empty_nofollow(struct lo_inode
*inode
, int dfd
,
448 if (inode
->is_symlink
) {
449 res
= linkat(inode
->fd
, "", dfd
, name
, AT_EMPTY_PATH
);
450 if (res
== -1 && (errno
== ENOENT
|| errno
== EINVAL
)) {
451 /* Sorry, no race free way to hard-link a symlink. */
457 sprintf(procname
, "/proc/self/fd/%i", inode
->fd
);
459 return linkat(AT_FDCWD
, procname
, dfd
, name
, AT_SYMLINK_FOLLOW
);
462 static void lo_link(fuse_req_t req
, fuse_ino_t ino
, fuse_ino_t parent
,
466 struct lo_data
*lo
= lo_data(req
);
467 struct lo_inode
*inode
= lo_inode(req
, ino
);
468 struct fuse_entry_param e
;
471 memset(&e
, 0, sizeof(struct fuse_entry_param
));
472 e
.attr_timeout
= lo
->timeout
;
473 e
.entry_timeout
= lo
->timeout
;
475 res
= linkat_empty_nofollow(inode
, lo_fd(req
, parent
), name
);
480 res
= fstatat(inode
->fd
, "", &e
.attr
, AT_EMPTY_PATH
| AT_SYMLINK_NOFOLLOW
);
485 pthread_mutex_lock(&lo
->mutex
);
487 pthread_mutex_unlock(&lo
->mutex
);
488 e
.ino
= (uintptr_t)inode
;
491 fuse_log(FUSE_LOG_DEBUG
, " %lli/%s -> %lli\n",
492 (unsigned long long)parent
, name
, (unsigned long long)e
.ino
);
495 fuse_reply_entry(req
, &e
);
500 fuse_reply_err(req
, saverr
);
503 static void lo_rmdir(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
507 res
= unlinkat(lo_fd(req
, parent
), name
, AT_REMOVEDIR
);
509 fuse_reply_err(req
, res
== -1 ? errno
: 0);
512 static void lo_rename(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
513 fuse_ino_t newparent
, const char *newname
,
519 fuse_reply_err(req
, EINVAL
);
523 res
= renameat(lo_fd(req
, parent
), name
, lo_fd(req
, newparent
), newname
);
525 fuse_reply_err(req
, res
== -1 ? errno
: 0);
528 static void lo_unlink(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
532 res
= unlinkat(lo_fd(req
, parent
), name
, 0);
534 fuse_reply_err(req
, res
== -1 ? errno
: 0);
537 static void unref_inode(struct lo_data
*lo
, struct lo_inode
*inode
, uint64_t n
)
543 pthread_mutex_lock(&lo
->mutex
);
544 assert(inode
->refcount
>= n
);
545 inode
->refcount
-= n
;
546 if (!inode
->refcount
) {
547 struct lo_inode
*prev
, *next
;
554 pthread_mutex_unlock(&lo
->mutex
);
559 pthread_mutex_unlock(&lo
->mutex
);
563 static void lo_forget_one(fuse_req_t req
, fuse_ino_t ino
, uint64_t nlookup
)
565 struct lo_data
*lo
= lo_data(req
);
566 struct lo_inode
*inode
= lo_inode(req
, ino
);
569 fuse_log(FUSE_LOG_DEBUG
, " forget %lli %lli -%lli\n",
570 (unsigned long long)ino
, (unsigned long long)inode
->refcount
,
571 (unsigned long long)nlookup
);
574 unref_inode(lo
, inode
, nlookup
);
577 static void lo_forget(fuse_req_t req
, fuse_ino_t ino
, uint64_t nlookup
)
579 lo_forget_one(req
, ino
, nlookup
);
580 fuse_reply_none(req
);
583 static void lo_forget_multi(fuse_req_t req
, size_t count
,
584 struct fuse_forget_data
*forgets
)
588 for (i
= 0; i
< count
; i
++) {
589 lo_forget_one(req
, forgets
[i
].ino
, forgets
[i
].nlookup
);
591 fuse_reply_none(req
);
594 static void lo_readlink(fuse_req_t req
, fuse_ino_t ino
)
596 char buf
[PATH_MAX
+ 1];
599 res
= readlinkat(lo_fd(req
, ino
), "", buf
, sizeof(buf
));
601 return (void)fuse_reply_err(req
, errno
);
604 if (res
== sizeof(buf
)) {
605 return (void)fuse_reply_err(req
, ENAMETOOLONG
);
610 fuse_reply_readlink(req
, buf
);
615 struct dirent
*entry
;
619 static struct lo_dirp
*lo_dirp(struct fuse_file_info
*fi
)
621 return (struct lo_dirp
*)(uintptr_t)fi
->fh
;
624 static void lo_opendir(fuse_req_t req
, fuse_ino_t ino
,
625 struct fuse_file_info
*fi
)
628 struct lo_data
*lo
= lo_data(req
);
632 d
= calloc(1, sizeof(struct lo_dirp
));
637 fd
= openat(lo_fd(req
, ino
), ".", O_RDONLY
);
642 d
->dp
= fdopendir(fd
);
650 fi
->fh
= (uintptr_t)d
;
651 if (lo
->cache
== CACHE_ALWAYS
) {
654 fuse_reply_open(req
, fi
);
666 fuse_reply_err(req
, error
);
669 static int is_dot_or_dotdot(const char *name
)
671 return name
[0] == '.' &&
672 (name
[1] == '\0' || (name
[1] == '.' && name
[2] == '\0'));
675 static void lo_do_readdir(fuse_req_t req
, fuse_ino_t ino
, size_t size
,
676 off_t offset
, struct fuse_file_info
*fi
, int plus
)
678 struct lo_dirp
*d
= lo_dirp(fi
);
686 buf
= calloc(1, size
);
693 if (offset
!= d
->offset
) {
694 seekdir(d
->dp
, offset
);
705 d
->entry
= readdir(d
->dp
);
707 if (errno
) { /* Error */
710 } else { /* End of stream */
715 nextoff
= d
->entry
->d_off
;
716 name
= d
->entry
->d_name
;
717 fuse_ino_t entry_ino
= 0;
719 struct fuse_entry_param e
;
720 if (is_dot_or_dotdot(name
)) {
721 e
= (struct fuse_entry_param
){
722 .attr
.st_ino
= d
->entry
->d_ino
,
723 .attr
.st_mode
= d
->entry
->d_type
<< 12,
726 err
= lo_do_lookup(req
, ino
, name
, &e
);
733 entsize
= fuse_add_direntry_plus(req
, p
, rem
, name
, &e
, nextoff
);
736 .st_ino
= d
->entry
->d_ino
,
737 .st_mode
= d
->entry
->d_type
<< 12,
739 entsize
= fuse_add_direntry(req
, p
, rem
, name
, &st
, nextoff
);
742 if (entry_ino
!= 0) {
743 lo_forget_one(req
, entry_ino
, 1);
758 * If there's an error, we can only signal it if we haven't stored
759 * any entries yet - otherwise we'd end up with wrong lookup
760 * counts for the entries that are already in the buffer. So we
761 * return what we've collected until that point.
763 if (err
&& rem
== size
) {
764 fuse_reply_err(req
, err
);
766 fuse_reply_buf(req
, buf
, size
- rem
);
771 static void lo_readdir(fuse_req_t req
, fuse_ino_t ino
, size_t size
,
772 off_t offset
, struct fuse_file_info
*fi
)
774 lo_do_readdir(req
, ino
, size
, offset
, fi
, 0);
777 static void lo_readdirplus(fuse_req_t req
, fuse_ino_t ino
, size_t size
,
778 off_t offset
, struct fuse_file_info
*fi
)
780 lo_do_readdir(req
, ino
, size
, offset
, fi
, 1);
783 static void lo_releasedir(fuse_req_t req
, fuse_ino_t ino
,
784 struct fuse_file_info
*fi
)
786 struct lo_dirp
*d
= lo_dirp(fi
);
790 fuse_reply_err(req
, 0);
793 static void lo_create(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
794 mode_t mode
, struct fuse_file_info
*fi
)
797 struct lo_data
*lo
= lo_data(req
);
798 struct fuse_entry_param e
;
802 fuse_log(FUSE_LOG_DEBUG
, "lo_create(parent=%" PRIu64
", name=%s)\n",
806 fd
= openat(lo_fd(req
, parent
), name
, (fi
->flags
| O_CREAT
) & ~O_NOFOLLOW
,
809 return (void)fuse_reply_err(req
, errno
);
813 if (lo
->cache
== CACHE_NEVER
) {
815 } else if (lo
->cache
== CACHE_ALWAYS
) {
819 err
= lo_do_lookup(req
, parent
, name
, &e
);
821 fuse_reply_err(req
, err
);
823 fuse_reply_create(req
, &e
, fi
);
827 static void lo_fsyncdir(fuse_req_t req
, fuse_ino_t ino
, int datasync
,
828 struct fuse_file_info
*fi
)
831 int fd
= dirfd(lo_dirp(fi
)->dp
);
838 fuse_reply_err(req
, res
== -1 ? errno
: 0);
841 static void lo_open(fuse_req_t req
, fuse_ino_t ino
, struct fuse_file_info
*fi
)
845 struct lo_data
*lo
= lo_data(req
);
848 fuse_log(FUSE_LOG_DEBUG
, "lo_open(ino=%" PRIu64
", flags=%d)\n", ino
,
853 * With writeback cache, kernel may send read requests even
854 * when userspace opened write-only
856 if (lo
->writeback
&& (fi
->flags
& O_ACCMODE
) == O_WRONLY
) {
857 fi
->flags
&= ~O_ACCMODE
;
862 * With writeback cache, O_APPEND is handled by the kernel.
863 * This breaks atomicity (since the file may change in the
864 * underlying filesystem, so that the kernel's idea of the
865 * end of the file isn't accurate anymore). In this example,
866 * we just accept that. A more rigorous filesystem may want
867 * to return an error here
869 if (lo
->writeback
&& (fi
->flags
& O_APPEND
)) {
870 fi
->flags
&= ~O_APPEND
;
873 sprintf(buf
, "/proc/self/fd/%i", lo_fd(req
, ino
));
874 fd
= open(buf
, fi
->flags
& ~O_NOFOLLOW
);
876 return (void)fuse_reply_err(req
, errno
);
880 if (lo
->cache
== CACHE_NEVER
) {
882 } else if (lo
->cache
== CACHE_ALWAYS
) {
885 fuse_reply_open(req
, fi
);
888 static void lo_release(fuse_req_t req
, fuse_ino_t ino
,
889 struct fuse_file_info
*fi
)
894 fuse_reply_err(req
, 0);
897 static void lo_flush(fuse_req_t req
, fuse_ino_t ino
, struct fuse_file_info
*fi
)
901 res
= close(dup(fi
->fh
));
902 fuse_reply_err(req
, res
== -1 ? errno
: 0);
905 static void lo_fsync(fuse_req_t req
, fuse_ino_t ino
, int datasync
,
906 struct fuse_file_info
*fi
)
911 res
= fdatasync(fi
->fh
);
915 fuse_reply_err(req
, res
== -1 ? errno
: 0);
918 static void lo_read(fuse_req_t req
, fuse_ino_t ino
, size_t size
, off_t offset
,
919 struct fuse_file_info
*fi
)
921 struct fuse_bufvec buf
= FUSE_BUFVEC_INIT(size
);
924 fuse_log(FUSE_LOG_DEBUG
,
925 "lo_read(ino=%" PRIu64
", size=%zd, "
927 ino
, size
, (unsigned long)offset
);
930 buf
.buf
[0].flags
= FUSE_BUF_IS_FD
| FUSE_BUF_FD_SEEK
;
931 buf
.buf
[0].fd
= fi
->fh
;
932 buf
.buf
[0].pos
= offset
;
934 fuse_reply_data(req
, &buf
);
937 static void lo_write_buf(fuse_req_t req
, fuse_ino_t ino
,
938 struct fuse_bufvec
*in_buf
, off_t off
,
939 struct fuse_file_info
*fi
)
943 struct fuse_bufvec out_buf
= FUSE_BUFVEC_INIT(fuse_buf_size(in_buf
));
945 out_buf
.buf
[0].flags
= FUSE_BUF_IS_FD
| FUSE_BUF_FD_SEEK
;
946 out_buf
.buf
[0].fd
= fi
->fh
;
947 out_buf
.buf
[0].pos
= off
;
950 fuse_log(FUSE_LOG_DEBUG
,
951 "lo_write(ino=%" PRIu64
", size=%zd, off=%lu)\n", ino
,
952 out_buf
.buf
[0].size
, (unsigned long)off
);
955 res
= fuse_buf_copy(&out_buf
, in_buf
);
957 fuse_reply_err(req
, -res
);
959 fuse_reply_write(req
, (size_t)res
);
963 static void lo_statfs(fuse_req_t req
, fuse_ino_t ino
)
966 struct statvfs stbuf
;
968 res
= fstatvfs(lo_fd(req
, ino
), &stbuf
);
970 fuse_reply_err(req
, errno
);
972 fuse_reply_statfs(req
, &stbuf
);
976 static void lo_fallocate(fuse_req_t req
, fuse_ino_t ino
, int mode
, off_t offset
,
977 off_t length
, struct fuse_file_info
*fi
)
979 int err
= EOPNOTSUPP
;
982 #ifdef HAVE_FALLOCATE
983 err
= fallocate(fi
->fh
, mode
, offset
, length
);
988 #elif defined(HAVE_POSIX_FALLOCATE)
990 fuse_reply_err(req
, EOPNOTSUPP
);
994 err
= posix_fallocate(fi
->fh
, offset
, length
);
997 fuse_reply_err(req
, err
);
1000 static void lo_flock(fuse_req_t req
, fuse_ino_t ino
, struct fuse_file_info
*fi
,
1006 res
= flock(fi
->fh
, op
);
1008 fuse_reply_err(req
, res
== -1 ? errno
: 0);
1011 static void lo_getxattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
,
1016 struct lo_inode
*inode
= lo_inode(req
, ino
);
1021 if (!lo_data(req
)->xattr
) {
1025 if (lo_debug(req
)) {
1026 fuse_log(FUSE_LOG_DEBUG
,
1027 "lo_getxattr(ino=%" PRIu64
", name=%s size=%zd)\n", ino
, name
,
1031 if (inode
->is_symlink
) {
1032 /* Sorry, no race free way to getxattr on symlink. */
1037 sprintf(procname
, "/proc/self/fd/%i", inode
->fd
);
1040 value
= malloc(size
);
1045 ret
= getxattr(procname
, name
, value
, size
);
1054 fuse_reply_buf(req
, value
, ret
);
1056 ret
= getxattr(procname
, name
, NULL
, 0);
1061 fuse_reply_xattr(req
, ret
);
1070 fuse_reply_err(req
, saverr
);
1074 static void lo_listxattr(fuse_req_t req
, fuse_ino_t ino
, size_t size
)
1078 struct lo_inode
*inode
= lo_inode(req
, ino
);
1083 if (!lo_data(req
)->xattr
) {
1087 if (lo_debug(req
)) {
1088 fuse_log(FUSE_LOG_DEBUG
, "lo_listxattr(ino=%" PRIu64
", size=%zd)\n",
1092 if (inode
->is_symlink
) {
1093 /* Sorry, no race free way to listxattr on symlink. */
1098 sprintf(procname
, "/proc/self/fd/%i", inode
->fd
);
1101 value
= malloc(size
);
1106 ret
= listxattr(procname
, value
, size
);
1115 fuse_reply_buf(req
, value
, ret
);
1117 ret
= listxattr(procname
, NULL
, 0);
1122 fuse_reply_xattr(req
, ret
);
1131 fuse_reply_err(req
, saverr
);
1135 static void lo_setxattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
,
1136 const char *value
, size_t size
, int flags
)
1139 struct lo_inode
*inode
= lo_inode(req
, ino
);
1144 if (!lo_data(req
)->xattr
) {
1148 if (lo_debug(req
)) {
1149 fuse_log(FUSE_LOG_DEBUG
,
1150 "lo_setxattr(ino=%" PRIu64
", name=%s value=%s size=%zd)\n",
1151 ino
, name
, value
, size
);
1154 if (inode
->is_symlink
) {
1155 /* Sorry, no race free way to setxattr on symlink. */
1160 sprintf(procname
, "/proc/self/fd/%i", inode
->fd
);
1162 ret
= setxattr(procname
, name
, value
, size
, flags
);
1163 saverr
= ret
== -1 ? errno
: 0;
1166 fuse_reply_err(req
, saverr
);
1169 static void lo_removexattr(fuse_req_t req
, fuse_ino_t ino
, const char *name
)
1172 struct lo_inode
*inode
= lo_inode(req
, ino
);
1177 if (!lo_data(req
)->xattr
) {
1181 if (lo_debug(req
)) {
1182 fuse_log(FUSE_LOG_DEBUG
, "lo_removexattr(ino=%" PRIu64
", name=%s)\n",
1186 if (inode
->is_symlink
) {
1187 /* Sorry, no race free way to setxattr on symlink. */
1192 sprintf(procname
, "/proc/self/fd/%i", inode
->fd
);
1194 ret
= removexattr(procname
, name
);
1195 saverr
= ret
== -1 ? errno
: 0;
1198 fuse_reply_err(req
, saverr
);
1201 #ifdef HAVE_COPY_FILE_RANGE
1202 static void lo_copy_file_range(fuse_req_t req
, fuse_ino_t ino_in
, off_t off_in
,
1203 struct fuse_file_info
*fi_in
, fuse_ino_t ino_out
,
1204 off_t off_out
, struct fuse_file_info
*fi_out
,
1205 size_t len
, int flags
)
1210 fuse_log(FUSE_LOG_DEBUG
,
1211 "lo_copy_file_range(ino=%" PRIu64
"/fd=%lu, "
1212 "off=%lu, ino=%" PRIu64
"/fd=%lu, "
1213 "off=%lu, size=%zd, flags=0x%x)\n",
1214 ino_in
, fi_in
->fh
, off_in
, ino_out
, fi_out
->fh
, off_out
, len
,
1217 res
= copy_file_range(fi_in
->fh
, &off_in
, fi_out
->fh
, &off_out
, len
, flags
);
1219 fuse_reply_err(req
, -errno
);
1221 fuse_reply_write(req
, res
);
1226 static void lo_lseek(fuse_req_t req
, fuse_ino_t ino
, off_t off
, int whence
,
1227 struct fuse_file_info
*fi
)
1232 res
= lseek(fi
->fh
, off
, whence
);
1234 fuse_reply_lseek(req
, res
);
1236 fuse_reply_err(req
, errno
);
1240 static struct fuse_lowlevel_ops lo_oper
= {
1242 .lookup
= lo_lookup
,
1245 .symlink
= lo_symlink
,
1247 .unlink
= lo_unlink
,
1249 .rename
= lo_rename
,
1250 .forget
= lo_forget
,
1251 .forget_multi
= lo_forget_multi
,
1252 .getattr
= lo_getattr
,
1253 .setattr
= lo_setattr
,
1254 .readlink
= lo_readlink
,
1255 .opendir
= lo_opendir
,
1256 .readdir
= lo_readdir
,
1257 .readdirplus
= lo_readdirplus
,
1258 .releasedir
= lo_releasedir
,
1259 .fsyncdir
= lo_fsyncdir
,
1260 .create
= lo_create
,
1262 .release
= lo_release
,
1266 .write_buf
= lo_write_buf
,
1267 .statfs
= lo_statfs
,
1268 .fallocate
= lo_fallocate
,
1270 .getxattr
= lo_getxattr
,
1271 .listxattr
= lo_listxattr
,
1272 .setxattr
= lo_setxattr
,
1273 .removexattr
= lo_removexattr
,
1274 #ifdef HAVE_COPY_FILE_RANGE
1275 .copy_file_range
= lo_copy_file_range
,
1280 int main(int argc
, char *argv
[])
1282 struct fuse_args args
= FUSE_ARGS_INIT(argc
, argv
);
1283 struct fuse_session
*se
;
1284 struct fuse_cmdline_opts opts
;
1285 struct lo_data lo
= { .debug
= 0, .writeback
= 0 };
1288 /* Don't mask creation mode, kernel already did that */
1291 pthread_mutex_init(&lo
.mutex
, NULL
);
1292 lo
.root
.next
= lo
.root
.prev
= &lo
.root
;
1294 lo
.cache
= CACHE_NORMAL
;
1296 if (fuse_parse_cmdline(&args
, &opts
) != 0) {
1299 if (opts
.show_help
) {
1300 printf("usage: %s [options]\n\n", argv
[0]);
1301 fuse_cmdline_help();
1302 fuse_lowlevel_help();
1305 } else if (opts
.show_version
) {
1306 fuse_lowlevel_version();
1311 if (fuse_opt_parse(&args
, &lo
, lo_opts
, NULL
) == -1) {
1315 lo
.debug
= opts
.debug
;
1316 lo
.root
.refcount
= 2;
1321 res
= lstat(lo
.source
, &stat
);
1323 fuse_log(FUSE_LOG_ERR
, "failed to stat source (\"%s\"): %m\n",
1327 if (!S_ISDIR(stat
.st_mode
)) {
1328 fuse_log(FUSE_LOG_ERR
, "source is not a directory\n");
1335 lo
.root
.is_symlink
= false;
1336 if (!lo
.timeout_set
) {
1347 lo
.timeout
= 86400.0;
1350 } else if (lo
.timeout
< 0) {
1351 fuse_log(FUSE_LOG_ERR
, "timeout is negative (%lf)\n", lo
.timeout
);
1355 lo
.root
.fd
= open(lo
.source
, O_PATH
);
1356 if (lo
.root
.fd
== -1) {
1357 fuse_log(FUSE_LOG_ERR
, "open(\"%s\", O_PATH): %m\n", lo
.source
);
1361 se
= fuse_session_new(&args
, &lo_oper
, sizeof(lo_oper
), &lo
);
1366 if (fuse_set_signal_handlers(se
) != 0) {
1370 if (fuse_session_mount(se
) != 0) {
1374 fuse_daemonize(opts
.foreground
);
1376 /* Block until ctrl+c or fusermount -u */
1377 if (opts
.singlethread
) {
1378 ret
= fuse_session_loop(se
);
1380 ret
= fuse_session_loop_mt(se
, opts
.clone_fd
);
1383 fuse_session_unmount(se
);
1385 fuse_remove_signal_handlers(se
);
1387 fuse_session_destroy(se
);
1389 fuse_opt_free_args(&args
);
1391 if (lo
.root
.fd
>= 0) {