38 #define FUSE_USE_VERSION 31 42 #include <fuse_lowlevel.h> 56 #include <sys/xattr.h> 58 #include "passthrough_helpers.h" 64 #if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus 65 _Static_assert(
sizeof(
fuse_ino_t) >=
sizeof(uintptr_t),
66 "fuse_ino_t too small to hold uintptr_t values!");
68 struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
69 {
unsigned _uintptr_to_must_hold_fuse_ino_t:
70 ((
sizeof(
fuse_ino_t) >=
sizeof(uintptr_t)) ? 1 : -1); };
74 struct lo_inode *next;
75 struct lo_inode *prev;
90 pthread_mutex_t mutex;
102 static const struct fuse_opt lo_opts[] = {
104 offsetof(
struct lo_data, writeback), 1 },
106 offsetof(
struct lo_data, writeback), 0 },
108 offsetof(
struct lo_data, source), 0 },
110 offsetof(
struct lo_data, flock), 1 },
112 offsetof(
struct lo_data, flock), 0 },
114 offsetof(
struct lo_data, xattr), 1 },
116 offsetof(
struct lo_data, xattr), 0 },
118 offsetof(
struct lo_data, timeout), 0 },
120 offsetof(
struct lo_data, timeout_set), 1 },
122 offsetof(
struct lo_data, cache), CACHE_NEVER },
124 offsetof(
struct lo_data, cache), CACHE_NORMAL },
126 offsetof(
struct lo_data, cache), CACHE_ALWAYS },
131 static struct lo_data *lo_data(
fuse_req_t req)
139 return &lo_data(req)->root;
141 return (
struct lo_inode *) (uintptr_t) ino;
146 return lo_inode(req, ino)->fd;
151 return lo_data(req)->debug != 0;
154 static void lo_init(
void *userdata,
157 struct lo_data *lo = (
struct lo_data*) userdata;
165 fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating writeback\n");
170 fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating flock locks\n");
180 struct lo_data *lo = lo_data(req);
184 res = fstatat(lo_fd(req, ino),
"", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
191 static int utimensat_empty_nofollow(
struct lo_inode *inode,
192 const struct timespec *tv)
197 if (inode->is_symlink) {
198 res = utimensat(inode->fd,
"", tv,
199 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
200 if (res == -1 && errno == EINVAL) {
206 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
208 return utimensat(AT_FDCWD, procname, tv, 0);
216 struct lo_inode *inode = lo_inode(req, ino);
220 if (valid & FUSE_SET_ATTR_MODE) {
222 res = fchmod(fi->
fh, attr->st_mode);
224 sprintf(procname,
"/proc/self/fd/%i", ifd);
225 res = chmod(procname, attr->st_mode);
230 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
231 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
232 attr->st_uid : (uid_t) -1;
233 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
234 attr->st_gid : (gid_t) -1;
236 res = fchownat(ifd,
"", uid, gid,
237 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
241 if (valid & FUSE_SET_ATTR_SIZE) {
243 res = ftruncate(fi->
fh, attr->st_size);
245 sprintf(procname,
"/proc/self/fd/%i", ifd);
246 res = truncate(procname, attr->st_size);
251 if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
252 struct timespec tv[2];
256 tv[0].tv_nsec = UTIME_OMIT;
257 tv[1].tv_nsec = UTIME_OMIT;
259 if (valid & FUSE_SET_ATTR_ATIME_NOW)
260 tv[0].tv_nsec = UTIME_NOW;
261 else if (valid & FUSE_SET_ATTR_ATIME)
262 tv[0] = attr->st_atim;
264 if (valid & FUSE_SET_ATTR_MTIME_NOW)
265 tv[1].tv_nsec = UTIME_NOW;
266 else if (valid & FUSE_SET_ATTR_MTIME)
267 tv[1] = attr->st_mtim;
270 res = futimens(fi->
fh, tv);
272 res = utimensat_empty_nofollow(inode, tv);
277 return lo_getattr(req, ino, fi);
284 static struct lo_inode *lo_find(
struct lo_data *lo,
struct stat *st)
287 struct lo_inode *ret = NULL;
289 pthread_mutex_lock(&lo->mutex);
290 for (p = lo->root.next; p != &lo->root; p = p->next) {
291 if (p->ino == st->st_ino && p->dev == st->st_dev) {
292 assert(p->refcount > 0);
298 pthread_mutex_unlock(&lo->mutex);
308 struct lo_data *lo = lo_data(req);
309 struct lo_inode *inode;
311 memset(e, 0,
sizeof(*e));
315 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
319 res = fstatat(newfd,
"", &e->
attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
323 inode = lo_find(lo_data(req), &e->
attr);
328 struct lo_inode *prev, *next;
331 inode = calloc(1,
sizeof(
struct lo_inode));
335 inode->is_symlink = S_ISLNK(e->
attr.st_mode);
338 inode->ino = e->
attr.st_ino;
339 inode->dev = e->
attr.st_dev;
341 pthread_mutex_lock(&lo->mutex);
348 pthread_mutex_unlock(&lo->mutex);
350 e->
ino = (uintptr_t) inode;
353 fuse_log(FUSE_LOG_DEBUG,
" %lli/%s -> %lli\n",
354 (
unsigned long long) parent, name, (
unsigned long long) e->
ino);
371 fuse_log(FUSE_LOG_DEBUG,
"lo_lookup(parent=%" PRIu64
", name=%s)\n",
374 err = lo_do_lookup(req, parent, name, &e);
382 const char *name, mode_t mode, dev_t rdev,
387 struct lo_inode *dir = lo_inode(req, parent);
392 res = mknod_wrapper(dir->fd, name, link, mode, rdev);
398 saverr = lo_do_lookup(req, parent, name, &e);
403 fuse_log(FUSE_LOG_DEBUG,
" %lli/%s -> %lli\n",
404 (
unsigned long long) parent, name, (
unsigned long long) e.
ino);
414 const char *name, mode_t mode, dev_t rdev)
416 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
422 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
425 static void lo_symlink(
fuse_req_t req,
const char *link,
428 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
431 static int linkat_empty_nofollow(
struct lo_inode *inode,
int dfd,
437 if (inode->is_symlink) {
438 res = linkat(inode->fd,
"", dfd, name, AT_EMPTY_PATH);
439 if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
446 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
448 return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
455 struct lo_data *lo = lo_data(req);
456 struct lo_inode *inode = lo_inode(req, ino);
464 res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
468 res = fstatat(inode->fd,
"", &e.
attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
472 pthread_mutex_lock(&lo->mutex);
474 pthread_mutex_unlock(&lo->mutex);
475 e.
ino = (uintptr_t) inode;
478 fuse_log(FUSE_LOG_DEBUG,
" %lli/%s -> %lli\n",
479 (
unsigned long long) parent, name,
480 (
unsigned long long) e.
ino);
494 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
510 res = renameat(lo_fd(req, parent), name,
511 lo_fd(req, newparent), newname);
520 res = unlinkat(lo_fd(req, parent), name, 0);
525 static void unref_inode(
struct lo_data *lo,
struct lo_inode *inode, uint64_t n)
530 pthread_mutex_lock(&lo->mutex);
531 assert(inode->refcount >= n);
532 inode->refcount -= n;
533 if (!inode->refcount) {
534 struct lo_inode *prev, *next;
541 pthread_mutex_unlock(&lo->mutex);
546 pthread_mutex_unlock(&lo->mutex);
552 struct lo_data *lo = lo_data(req);
553 struct lo_inode *inode = lo_inode(req, ino);
556 fuse_log(FUSE_LOG_DEBUG,
" forget %lli %lli -%lli\n",
557 (
unsigned long long) ino,
558 (
unsigned long long) inode->refcount,
559 (
unsigned long long) nlookup);
562 unref_inode(lo, inode, nlookup);
567 lo_forget_one(req, ino, nlookup);
571 static void lo_forget_multi(
fuse_req_t req,
size_t count,
572 struct fuse_forget_data *forgets)
576 for (i = 0; i < count; i++)
577 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
583 char buf[PATH_MAX + 1];
586 res = readlinkat(lo_fd(req, ino),
"", buf,
sizeof(buf));
590 if (res ==
sizeof(buf))
600 struct dirent *entry;
606 return (
struct lo_dirp *) (uintptr_t) fi->
fh;
612 struct lo_data *lo = lo_data(req);
616 d = calloc(1,
sizeof(
struct lo_dirp));
620 fd = openat(lo_fd(req, ino),
".", O_RDONLY);
624 d->dp = fdopendir(fd);
631 fi->
fh = (uintptr_t) d;
632 if (lo->cache == CACHE_ALWAYS)
648 static int is_dot_or_dotdot(
const char *name)
650 return name[0] ==
'.' && (name[1] ==
'\0' ||
651 (name[1] ==
'.' && name[2] ==
'\0'));
657 struct lo_dirp *d = lo_dirp(fi);
665 buf = calloc(1, size);
672 if (offset != d->offset) {
673 seekdir(d->dp, offset);
684 d->entry = readdir(d->dp);
694 nextoff = d->entry->d_off;
695 name = d->entry->d_name;
699 if (is_dot_or_dotdot(name)) {
701 .
attr.st_ino = d->entry->d_ino,
702 .attr.st_mode = d->entry->d_type << 12,
705 err = lo_do_lookup(req, ino, name, &e);
715 .st_ino = d->entry->d_ino,
716 .st_mode = d->entry->d_type << 12,
723 lo_forget_one(req, entry_ino, 1);
740 if (err && rem == size)
750 lo_do_readdir(req, ino, size, offset, fi, 0);
756 lo_do_readdir(req, ino, size, offset, fi, 1);
761 struct lo_dirp *d = lo_dirp(fi);
772 struct lo_data *lo = lo_data(req);
777 fuse_log(FUSE_LOG_DEBUG,
"lo_create(parent=%" PRIu64
", name=%s)\n",
780 fd = openat(lo_fd(req, parent), name,
781 (fi->
flags | O_CREAT) & ~O_NOFOLLOW, mode);
786 if (lo->cache == CACHE_NEVER)
788 else if (lo->cache == CACHE_ALWAYS)
791 err = lo_do_lookup(req, parent, name, &e);
802 int fd = dirfd(lo_dirp(fi)->dp);
815 struct lo_data *lo = lo_data(req);
818 fuse_log(FUSE_LOG_DEBUG,
"lo_open(ino=%" PRIu64
", flags=%d)\n",
823 if (lo->writeback && (fi->
flags & O_ACCMODE) == O_WRONLY) {
824 fi->
flags &= ~O_ACCMODE;
834 if (lo->writeback && (fi->
flags & O_APPEND))
835 fi->
flags &= ~O_APPEND;
837 sprintf(buf,
"/proc/self/fd/%i", lo_fd(req, ino));
838 fd = open(buf, fi->
flags & ~O_NOFOLLOW);
843 if (lo->cache == CACHE_NEVER)
845 else if (lo->cache == CACHE_ALWAYS)
862 res = close(dup(fi->
fh));
872 res = fdatasync(fi->
fh);
884 fuse_log(FUSE_LOG_DEBUG,
"lo_read(ino=%" PRIu64
", size=%zd, " 885 "off=%lu)\n", ino, size, (
unsigned long) offset);
907 fuse_log(FUSE_LOG_DEBUG,
"lo_write(ino=%" PRIu64
", size=%zd, off=%lu)\n",
908 ino, out_buf.
buf[0].
size, (
unsigned long) off);
920 struct statvfs stbuf;
922 res = fstatvfs(lo_fd(req, ino), &stbuf);
932 int err = EOPNOTSUPP;
935 #ifdef HAVE_FALLOCATE 936 err = fallocate(fi->
fh, mode, offset, length);
940 #elif defined(HAVE_POSIX_FALLOCATE) 946 err = posix_fallocate(fi->
fh, offset, length);
958 res = flock(fi->
fh, op);
968 struct lo_inode *inode = lo_inode(req, ino);
973 if (!lo_data(req)->xattr)
977 fuse_log(FUSE_LOG_DEBUG,
"lo_getxattr(ino=%" PRIu64
", name=%s size=%zd)\n",
981 if (inode->is_symlink) {
987 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
990 value = malloc(size);
994 ret = getxattr(procname, name, value, size);
1003 ret = getxattr(procname, name, NULL, 0);
1024 struct lo_inode *inode = lo_inode(req, ino);
1029 if (!lo_data(req)->xattr)
1032 if (lo_debug(req)) {
1033 fuse_log(FUSE_LOG_DEBUG,
"lo_listxattr(ino=%" PRIu64
", size=%zd)\n",
1037 if (inode->is_symlink) {
1043 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
1046 value = malloc(size);
1050 ret = listxattr(procname, value, size);
1059 ret = listxattr(procname, NULL, 0);
1077 const char *value,
size_t size,
int flags)
1080 struct lo_inode *inode = lo_inode(req, ino);
1085 if (!lo_data(req)->xattr)
1088 if (lo_debug(req)) {
1089 fuse_log(FUSE_LOG_DEBUG,
"lo_setxattr(ino=%" PRIu64
", name=%s value=%s size=%zd)\n",
1090 ino, name, value, size);
1093 if (inode->is_symlink) {
1099 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
1101 ret = setxattr(procname, name, value, size, flags);
1102 saverr = ret == -1 ? errno : 0;
1111 struct lo_inode *inode = lo_inode(req, ino);
1116 if (!lo_data(req)->xattr)
1119 if (lo_debug(req)) {
1120 fuse_log(FUSE_LOG_DEBUG,
"lo_removexattr(ino=%" PRIu64
", name=%s)\n",
1124 if (inode->is_symlink) {
1130 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
1132 ret = removexattr(procname, name);
1133 saverr = ret == -1 ? errno : 0;
1139 #ifdef HAVE_COPY_FILE_RANGE 1149 fuse_log(FUSE_LOG_DEBUG,
"lo_copy_file_range(ino=%" PRIu64
"/fd=%lu, " 1150 "off=%lu, ino=%" PRIu64
"/fd=%lu, " 1151 "off=%lu, size=%zd, flags=0x%x)\n",
1152 ino_in, fi_in->
fh, off_in, ino_out, fi_out->
fh, off_out,
1155 res = copy_file_range(fi_in->
fh, &off_in, fi_out->
fh, &off_out, len,
1170 res = lseek(fi->
fh, off, whence);
1179 .lookup = lo_lookup,
1182 .symlink = lo_symlink,
1184 .unlink = lo_unlink,
1186 .rename = lo_rename,
1187 .forget = lo_forget,
1188 .forget_multi = lo_forget_multi,
1189 .getattr = lo_getattr,
1190 .setattr = lo_setattr,
1191 .readlink = lo_readlink,
1192 .opendir = lo_opendir,
1193 .readdir = lo_readdir,
1194 .readdirplus = lo_readdirplus,
1195 .releasedir = lo_releasedir,
1196 .fsyncdir = lo_fsyncdir,
1197 .create = lo_create,
1199 .release = lo_release,
1203 .write_buf = lo_write_buf,
1204 .statfs = lo_statfs,
1205 .fallocate = lo_fallocate,
1207 .getxattr = lo_getxattr,
1208 .listxattr = lo_listxattr,
1209 .setxattr = lo_setxattr,
1210 .removexattr = lo_removexattr,
1211 #ifdef HAVE_COPY_FILE_RANGE 1212 .copy_file_range = lo_copy_file_range,
1217 int main(
int argc,
char *argv[])
1220 struct fuse_session *se;
1221 struct fuse_cmdline_opts opts;
1222 struct lo_data lo = { .debug = 0,
1229 pthread_mutex_init(&lo.mutex, NULL);
1230 lo.root.next = lo.root.prev = &lo.root;
1232 lo.cache = CACHE_NORMAL;
1236 if (opts.show_help) {
1237 printf(
"usage: %s [options] <mountpoint>\n\n", argv[0]);
1242 }
else if (opts.show_version) {
1249 if(opts.mountpoint == NULL) {
1250 printf(
"usage: %s [options] <mountpoint>\n", argv[0]);
1251 printf(
" %s --help\n", argv[0]);
1259 lo.debug = opts.debug;
1260 lo.root.refcount = 2;
1265 res = lstat(lo.source, &stat);
1267 fuse_log(FUSE_LOG_ERR,
"failed to stat source (\"%s\"): %m\n",
1271 if (!S_ISDIR(stat.st_mode)) {
1272 fuse_log(FUSE_LOG_ERR,
"source is not a directory\n");
1279 lo.root.is_symlink =
false;
1280 if (!lo.timeout_set) {
1291 lo.timeout = 86400.0;
1294 }
else if (lo.timeout < 0) {
1295 fuse_log(FUSE_LOG_ERR,
"timeout is negative (%lf)\n",
1300 lo.root.fd = open(lo.source, O_PATH);
1301 if (lo.root.fd == -1) {
1302 fuse_log(FUSE_LOG_ERR,
"open(\"%s\", O_PATH): %m\n",
1320 if (opts.singlethread)
1323 ret = fuse_session_loop_mt(se, opts.clone_fd);
1331 free(opts.mountpoint);
1334 if (lo.root.fd >= 0)
void fuse_cmdline_help(void)
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
struct fuse_req * fuse_req_t
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_reply_none(fuse_req_t req)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
#define FUSE_CAP_EXPORT_SUPPORT
#define FUSE_ARGS_INIT(argc, argv)
#define FUSE_CAP_FLOCK_LOCKS
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_lowlevel_version(void)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_reply_lseek(fuse_req_t req, off_t off)
const char * fuse_pkgversion(void)
int fuse_session_loop(struct fuse_session *se)
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
void fuse_remove_signal_handlers(struct fuse_session *se)
enum fuse_buf_flags flags
void fuse_session_unmount(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
int fuse_reply_err(fuse_req_t req, int err)
#define FUSE_CAP_WRITEBACK_CACHE
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
void * fuse_req_userdata(fuse_req_t req)
void fuse_opt_free_args(struct fuse_args *args)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
int fuse_daemonize(int foreground)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
void fuse_lowlevel_help(void)
void(* init)(void *userdata, struct fuse_conn_info *conn)
void fuse_log(enum fuse_log_level level, const char *fmt,...)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)