38 #define FUSE_USE_VERSION 31 55 #include <sys/xattr.h> 61 #if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus 62 _Static_assert(
sizeof(
fuse_ino_t) >=
sizeof(uintptr_t),
63 "fuse_ino_t too small to hold uintptr_t values!");
65 struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
66 {
unsigned _uintptr_to_must_hold_fuse_ino_t:
67 ((
sizeof(
fuse_ino_t) >=
sizeof(uintptr_t)) ? 1 : -1); };
71 struct lo_inode *next;
72 struct lo_inode *prev;
87 pthread_mutex_t mutex;
99 static const struct fuse_opt lo_opts[] = {
101 offsetof(
struct lo_data, writeback), 1 },
103 offsetof(
struct lo_data, writeback), 0 },
105 offsetof(
struct lo_data, source), 0 },
107 offsetof(
struct lo_data, flock), 1 },
109 offsetof(
struct lo_data, flock), 0 },
111 offsetof(
struct lo_data, xattr), 1 },
113 offsetof(
struct lo_data, xattr), 0 },
115 offsetof(
struct lo_data, timeout), 0 },
117 offsetof(
struct lo_data, timeout_set), 1 },
119 offsetof(
struct lo_data, cache), CACHE_NEVER },
121 offsetof(
struct lo_data, cache), CACHE_NORMAL },
123 offsetof(
struct lo_data, cache), CACHE_ALWAYS },
128 static struct lo_data *lo_data(
fuse_req_t req)
136 return &lo_data(req)->root;
138 return (
struct lo_inode *) (uintptr_t) ino;
143 return lo_inode(req, ino)->fd;
148 return lo_data(req)->debug != 0;
151 static void lo_init(
void *userdata,
154 struct lo_data *lo = (
struct lo_data*) userdata;
162 fprintf(stderr,
"lo_init: activating writeback\n");
167 fprintf(stderr,
"lo_init: activating flock locks\n");
177 struct lo_data *lo = lo_data(req);
181 res = fstatat(lo_fd(req, ino),
"", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
188 static int utimensat_empty_nofollow(
struct lo_inode *inode,
189 const struct timespec *tv)
194 if (inode->is_symlink) {
195 res = utimensat(inode->fd,
"", tv,
196 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
197 if (res == -1 && errno == EINVAL) {
203 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
205 return utimensat(AT_FDCWD, procname, tv, 0);
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);
227 if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
228 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
229 attr->st_uid : (uid_t) -1;
230 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
231 attr->st_gid : (gid_t) -1;
233 res = fchownat(ifd,
"", uid, gid,
234 AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
238 if (valid & FUSE_SET_ATTR_SIZE) {
240 res = ftruncate(fi->
fh, attr->st_size);
242 sprintf(procname,
"/proc/self/fd/%i", ifd);
243 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;
261 if (valid & FUSE_SET_ATTR_MTIME_NOW)
262 tv[1].tv_nsec = UTIME_NOW;
263 else if (valid & FUSE_SET_ATTR_MTIME)
264 tv[1] = attr->st_mtim;
267 res = futimens(fi->
fh, tv);
269 res = utimensat_empty_nofollow(inode, tv);
274 return lo_getattr(req, ino, fi);
281 static struct lo_inode *lo_find(
struct lo_data *lo,
struct stat *st)
284 struct lo_inode *ret = NULL;
286 pthread_mutex_lock(&lo->mutex);
287 for (p = lo->root.next; p != &lo->root; p = p->next) {
288 if (p->ino == st->st_ino && p->dev == st->st_dev) {
289 assert(p->refcount > 0);
295 pthread_mutex_unlock(&lo->mutex);
305 struct lo_data *lo = lo_data(req);
306 struct lo_inode *inode;
308 memset(e, 0,
sizeof(*e));
312 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
316 res = fstatat(newfd,
"", &e->
attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
320 inode = lo_find(lo_data(req), &e->
attr);
325 struct lo_inode *prev, *next;
328 inode = calloc(1,
sizeof(
struct lo_inode));
332 inode->is_symlink = S_ISLNK(e->
attr.st_mode);
335 inode->ino = e->
attr.st_ino;
336 inode->dev = e->
attr.st_dev;
338 pthread_mutex_lock(&lo->mutex);
345 pthread_mutex_unlock(&lo->mutex);
347 e->
ino = (uintptr_t) inode;
350 fprintf(stderr,
" %lli/%s -> %lli\n",
351 (
unsigned long long) parent, name, (
unsigned long long) e->
ino);
368 fprintf(stderr,
"lo_lookup(parent=%" PRIu64
", name=%s)\n",
371 err = lo_do_lookup(req, parent, name, &e);
379 const char *name, mode_t mode, dev_t rdev,
385 struct lo_inode *inode;
386 struct lo_inode *dir = lo_inode(req, parent);
390 inode = calloc(1,
sizeof(
struct lo_inode));
395 res = mkdirat(dir->fd, name, mode);
396 else if (S_ISLNK(mode))
397 res = symlinkat(link, dir->fd, name);
399 res = mknodat(dir->fd, name, mode, rdev);
404 saverr = lo_do_lookup(req, parent, name, &e);
409 fprintf(stderr,
" %lli/%s -> %lli\n",
410 (
unsigned long long) parent, name, (
unsigned long long) e.
ino);
423 const char *name, mode_t mode, dev_t rdev)
425 lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
431 lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
434 static void lo_symlink(
fuse_req_t req,
const char *link,
437 lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
440 static int linkat_empty_nofollow(
struct lo_inode *inode,
int dfd,
446 if (inode->is_symlink) {
447 res = linkat(inode->fd,
"", dfd, name, AT_EMPTY_PATH);
448 if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
455 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
457 return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
464 struct lo_data *lo = lo_data(req);
465 struct lo_inode *inode = lo_inode(req, ino);
473 res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
477 res = fstatat(inode->fd,
"", &e.
attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
481 pthread_mutex_lock(&lo->mutex);
483 pthread_mutex_unlock(&lo->mutex);
484 e.
ino = (uintptr_t) inode;
487 fprintf(stderr,
" %lli/%s -> %lli\n",
488 (
unsigned long long) parent, name,
489 (
unsigned long long) e.
ino);
503 res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
519 res = renameat(lo_fd(req, parent), name,
520 lo_fd(req, newparent), newname);
529 res = unlinkat(lo_fd(req, parent), name, 0);
534 static void unref_inode(
struct lo_data *lo,
struct lo_inode *inode, uint64_t n)
539 pthread_mutex_lock(&lo->mutex);
540 assert(inode->refcount >= n);
541 inode->refcount -= n;
542 if (!inode->refcount) {
543 struct lo_inode *prev, *next;
550 pthread_mutex_unlock(&lo->mutex);
555 pthread_mutex_unlock(&lo->mutex);
561 struct lo_data *lo = lo_data(req);
562 struct lo_inode *inode = lo_inode(req, ino);
565 fprintf(stderr,
" forget %lli %lli -%lli\n",
566 (
unsigned long long) ino,
567 (
unsigned long long) inode->refcount,
568 (
unsigned long long) nlookup);
571 unref_inode(lo, inode, nlookup);
576 lo_forget_one(req, ino, nlookup);
580 static void lo_forget_multi(
fuse_req_t req,
size_t count,
581 struct fuse_forget_data *forgets)
585 for (i = 0; i < count; i++)
586 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
592 char buf[PATH_MAX + 1];
595 res = readlinkat(lo_fd(req, ino),
"", buf,
sizeof(buf));
599 if (res ==
sizeof(buf))
610 struct dirent *entry;
616 return (
struct lo_dirp *) (uintptr_t) fi->
fh;
622 struct lo_data *lo = lo_data(req);
623 struct lo_dirp *d = calloc(1,
sizeof(
struct lo_dirp));
627 d->fd = openat(lo_fd(req, ino),
".", O_RDONLY);
631 d->dp = fdopendir(d->fd);
638 fi->
fh = (uintptr_t) d;
639 if (lo->cache == CACHE_ALWAYS)
655 static int is_dot_or_dotdot(
const char *name)
657 return name[0] ==
'.' && (name[1] ==
'\0' ||
658 (name[1] ==
'.' && name[2] ==
'\0'));
664 struct lo_dirp *d = lo_dirp(fi);
672 buf = calloc(1, size);
676 if (offset != d->offset) {
677 seekdir(d->dp, offset);
690 d->entry = readdir(d->dp);
692 if (errno && rem == size) {
699 nextoff = telldir(d->dp);
700 name = d->entry->d_name;
704 if (is_dot_or_dotdot(name)) {
706 .
attr.st_ino = d->entry->d_ino,
707 .attr.st_mode = d->entry->d_type << 12,
710 err = lo_do_lookup(req, ino, name, &e);
719 .st_ino = d->entry->d_ino,
720 .st_mode = d->entry->d_type << 12,
747 lo_do_readdir(req, ino, size, offset, fi, 0);
753 lo_do_readdir(req, ino, size, offset, fi, 1);
758 struct lo_dirp *d = lo_dirp(fi);
773 fprintf(stderr,
"lo_create(parent=%" PRIu64
", name=%s)\n",
776 fd = openat(lo_fd(req, parent), name,
777 (fi->
flags | O_CREAT) & ~O_NOFOLLOW, mode);
783 err = lo_do_lookup(req, parent, name, &e);
794 int fd = dirfd(lo_dirp(fi)->dp);
807 struct lo_data *lo = lo_data(req);
810 fprintf(stderr,
"lo_open(ino=%" PRIu64
", flags=%d)\n",
815 if (lo->writeback && (fi->
flags & O_ACCMODE) == O_WRONLY) {
816 fi->
flags &= ~O_ACCMODE;
826 if (lo->writeback && (fi->
flags & O_APPEND))
827 fi->
flags &= ~O_APPEND;
829 sprintf(buf,
"/proc/self/fd/%i", lo_fd(req, ino));
830 fd = open(buf, fi->
flags & ~O_NOFOLLOW);
835 if (lo->cache == CACHE_NEVER)
837 else if (lo->cache == CACHE_ALWAYS)
854 res = close(dup(fi->
fh));
864 res = fdatasync(fi->
fh);
876 fprintf(stderr,
"lo_read(ino=%" PRIu64
", size=%zd, " 877 "off=%lu)\n", ino, size, (
unsigned long) offset);
899 fprintf(stderr,
"lo_write(ino=%" PRIu64
", size=%zd, off=%lu)\n",
900 ino, out_buf.
buf[0].
size, (
unsigned long) off);
912 struct statvfs stbuf;
914 res = fstatvfs(lo_fd(req, ino), &stbuf);
932 err = posix_fallocate(fi->
fh, offset, length);
943 res = flock(fi->
fh, op);
953 struct lo_inode *inode = lo_inode(req, ino);
958 if (!lo_data(req)->xattr)
962 fprintf(stderr,
"lo_getxattr(ino=%" PRIu64
", name=%s size=%zd)\n",
966 if (inode->is_symlink) {
972 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
975 value = malloc(size);
979 ret = getxattr(procname, name, value, size);
988 ret = getxattr(procname, name, NULL, 0);
1009 struct lo_inode *inode = lo_inode(req, ino);
1014 if (!lo_data(req)->xattr)
1017 if (lo_debug(req)) {
1018 fprintf(stderr,
"lo_listxattr(ino=%" PRIu64
", size=%zd)\n",
1022 if (inode->is_symlink) {
1028 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
1031 value = malloc(size);
1035 ret = listxattr(procname, value, size);
1044 ret = listxattr(procname, NULL, 0);
1062 const char *value,
size_t size,
int flags)
1065 struct lo_inode *inode = lo_inode(req, ino);
1070 if (!lo_data(req)->xattr)
1073 if (lo_debug(req)) {
1074 fprintf(stderr,
"lo_setxattr(ino=%" PRIu64
", name=%s value=%s size=%zd)\n",
1075 ino, name, value, size);
1078 if (inode->is_symlink) {
1084 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
1086 ret = setxattr(procname, name, value, size, flags);
1087 saverr = ret == -1 ? errno : 0;
1096 struct lo_inode *inode = lo_inode(req, ino);
1101 if (!lo_data(req)->xattr)
1104 if (lo_debug(req)) {
1105 fprintf(stderr,
"lo_removexattr(ino=%" PRIu64
", name=%s)\n",
1109 if (inode->is_symlink) {
1115 sprintf(procname,
"/proc/self/fd/%i", inode->fd);
1117 ret = removexattr(procname, name);
1118 saverr = ret == -1 ? errno : 0;
1126 .lookup = lo_lookup,
1129 .symlink = lo_symlink,
1131 .unlink = lo_unlink,
1133 .rename = lo_rename,
1134 .forget = lo_forget,
1135 .forget_multi = lo_forget_multi,
1136 .getattr = lo_getattr,
1137 .setattr = lo_setattr,
1138 .readlink = lo_readlink,
1139 .opendir = lo_opendir,
1140 .readdir = lo_readdir,
1141 .readdirplus = lo_readdirplus,
1142 .releasedir = lo_releasedir,
1143 .fsyncdir = lo_fsyncdir,
1144 .create = lo_create,
1146 .release = lo_release,
1150 .write_buf = lo_write_buf,
1151 .statfs = lo_statfs,
1152 .fallocate = lo_fallocate,
1154 .getxattr = lo_getxattr,
1155 .listxattr = lo_listxattr,
1156 .setxattr = lo_setxattr,
1157 .removexattr = lo_removexattr,
1160 int main(
int argc,
char *argv[])
1163 struct fuse_session *se;
1164 struct fuse_cmdline_opts opts;
1165 struct lo_data lo = { .debug = 0,
1172 pthread_mutex_init(&lo.mutex, NULL);
1173 lo.root.next = lo.root.prev = &lo.root;
1175 lo.cache = CACHE_NORMAL;
1179 if (opts.show_help) {
1180 printf(
"usage: %s [options] <mountpoint>\n\n", argv[0]);
1185 }
else if (opts.show_version) {
1195 lo.debug = opts.debug;
1196 lo.root.refcount = 2;
1201 res = lstat(lo.source, &stat);
1203 err(1,
"failed to stat source (\"%s\")", lo.source);
1204 if (!S_ISDIR(stat.st_mode))
1205 errx(1,
"source is not a directory");
1210 lo.root.is_symlink =
false;
1211 if (!lo.timeout_set) {
1222 lo.timeout = 86400.0;
1225 }
else if (lo.timeout < 0) {
1226 errx(1,
"timeout is negative (%lf)", lo.timeout);
1229 lo.root.fd = open(lo.source, O_PATH);
1230 if (lo.root.fd == -1)
1231 err(1,
"open(\"%s\", O_PATH)", lo.source);
1246 if (opts.singlethread)
1249 ret = fuse_session_loop_mt(se, opts.clone_fd);
1257 free(opts.mountpoint);
1260 if (lo.root.fd >= 0)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_err(fuse_req_t req, int err)
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
int fuse_session_loop(struct fuse_session *se)
void fuse_lowlevel_help(void)
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_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_daemonize(int foreground)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
struct fuse_req * fuse_req_t
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)
#define FUSE_CAP_EXPORT_SUPPORT
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_cmdline_help(void)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
int fuse_set_signal_handlers(struct fuse_session *se)
void fuse_remove_signal_handlers(struct fuse_session *se)
void fuse_lowlevel_version(void)
void fuse_reply_none(fuse_req_t req)
void fuse_opt_free_args(struct fuse_args *args)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_session_unmount(struct fuse_session *se)
enum fuse_buf_flags flags
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
#define FUSE_CAP_FLOCK_LOCKS
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_write(fuse_req_t req, size_t count)
#define FUSE_CAP_WRITEBACK_CACHE
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
const char * fuse_pkgversion(void)
#define FUSE_ARGS_INIT(argc, argv)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
void(* init)(void *userdata, struct fuse_conn_info *conn)