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);
   390         res = mknod_wrapper(dir->fd, name, link, mode, rdev);
   396         saverr = lo_do_lookup(req, parent, name, &e);
   401                 fuse_log(FUSE_LOG_DEBUG, 
"  %lli/%s -> %lli\n",
   402                         (
unsigned long long) parent, name, (
unsigned long long) e.
ino);
   412                      const char *name, mode_t mode, dev_t rdev)
   414         lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
   420         lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
   423 static void lo_symlink(
fuse_req_t req, 
const char *link,
   426         lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
   429 static int linkat_empty_nofollow(
struct lo_inode *inode, 
int dfd,
   435         if (inode->is_symlink) {
   436                 res = linkat(inode->fd, 
"", dfd, name, AT_EMPTY_PATH);
   437                 if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
   444         sprintf(procname, 
"/proc/self/fd/%i", inode->fd);
   446         return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
   453         struct lo_data *lo = lo_data(req);
   454         struct lo_inode *inode = lo_inode(req, ino);
   462         res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
   466         res = fstatat(inode->fd, 
"", &e.
attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
   470         pthread_mutex_lock(&lo->mutex);
   472         pthread_mutex_unlock(&lo->mutex);
   473         e.
ino = (uintptr_t) inode;
   476                 fuse_log(FUSE_LOG_DEBUG, 
"  %lli/%s -> %lli\n",
   477                         (
unsigned long long) parent, name,
   478                         (
unsigned long long) e.
ino);
   492         res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
   508         res = renameat(lo_fd(req, parent), name,
   509                         lo_fd(req, newparent), newname);
   518         res = unlinkat(lo_fd(req, parent), name, 0);
   523 static void unref_inode(
struct lo_data *lo, 
struct lo_inode *inode, uint64_t n)
   528         pthread_mutex_lock(&lo->mutex);
   529         assert(inode->refcount >= n);
   530         inode->refcount -= n;
   531         if (!inode->refcount) {
   532                 struct lo_inode *prev, *next;
   539                 pthread_mutex_unlock(&lo->mutex);
   544                 pthread_mutex_unlock(&lo->mutex);
   550         struct lo_data *lo = lo_data(req);
   551         struct lo_inode *inode = lo_inode(req, ino);
   554                 fuse_log(FUSE_LOG_DEBUG, 
"  forget %lli %lli -%lli\n",
   555                         (
unsigned long long) ino,
   556                         (
unsigned long long) inode->refcount,
   557                         (
unsigned long long) nlookup);
   560         unref_inode(lo, inode, nlookup);
   565         lo_forget_one(req, ino, nlookup);
   569 static void lo_forget_multi(
fuse_req_t req, 
size_t count,
   570                                 struct fuse_forget_data *forgets)
   574         for (i = 0; i < count; i++)
   575                 lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
   581         char buf[PATH_MAX + 1];
   584         res = readlinkat(lo_fd(req, ino), 
"", buf, 
sizeof(buf));
   588         if (res == 
sizeof(buf))
   598         struct dirent *entry;
   604         return (
struct lo_dirp *) (uintptr_t) fi->
fh;
   610         struct lo_data *lo = lo_data(req);
   614         d = calloc(1, 
sizeof(
struct lo_dirp));
   618         fd = openat(lo_fd(req, ino), 
".", O_RDONLY);
   622         d->dp = fdopendir(fd);
   629         fi->
fh = (uintptr_t) d;
   630         if (lo->cache == CACHE_ALWAYS)
   646 static int is_dot_or_dotdot(
const char *name)
   648         return name[0] == 
'.' && (name[1] == 
'\0' ||
   649                                   (name[1] == 
'.' && name[2] == 
'\0'));
   655         struct lo_dirp *d = lo_dirp(fi);
   663         buf = calloc(1, size);
   670         if (offset != d->offset) {
   671                 seekdir(d->dp, offset);
   682                         d->entry = readdir(d->dp);
   692                 nextoff = d->entry->d_off;
   693                 name = d->entry->d_name;
   697                         if (is_dot_or_dotdot(name)) {
   699                                         .
attr.st_ino = d->entry->d_ino,
   700                                         .attr.st_mode = d->entry->d_type << 12,
   703                                 err = lo_do_lookup(req, ino, name, &e);
   713                                 .st_ino = d->entry->d_ino,
   714                                 .st_mode = d->entry->d_type << 12,
   721                                 lo_forget_one(req, entry_ino, 1);
   738     if (err && rem == size)
   748         lo_do_readdir(req, ino, size, offset, fi, 0);
   754         lo_do_readdir(req, ino, size, offset, fi, 1);
   759         struct lo_dirp *d = lo_dirp(fi);
   770         struct lo_data *lo = lo_data(req);
   775                 fuse_log(FUSE_LOG_DEBUG, 
"lo_create(parent=%" PRIu64 
", name=%s)\n",
   778         fd = openat(lo_fd(req, parent), name,
   779                     (fi->
flags | O_CREAT) & ~O_NOFOLLOW, mode);
   784         if (lo->cache == CACHE_NEVER)
   786         else if (lo->cache == CACHE_ALWAYS)
   789         err = lo_do_lookup(req, parent, name, &e);
   800         int fd = dirfd(lo_dirp(fi)->dp);
   813         struct lo_data *lo = lo_data(req);
   816                 fuse_log(FUSE_LOG_DEBUG, 
"lo_open(ino=%" PRIu64 
", flags=%d)\n",
   821         if (lo->writeback && (fi->
flags & O_ACCMODE) == O_WRONLY) {
   822                 fi->
flags &= ~O_ACCMODE;
   832         if (lo->writeback && (fi->
flags & O_APPEND))
   833                 fi->
flags &= ~O_APPEND;
   835         sprintf(buf, 
"/proc/self/fd/%i", lo_fd(req, ino));
   836         fd = open(buf, fi->
flags & ~O_NOFOLLOW);
   841         if (lo->cache == CACHE_NEVER)
   843         else if (lo->cache == CACHE_ALWAYS)
   860         res = close(dup(fi->
fh));
   870                 res = fdatasync(fi->
fh);
   882                 fuse_log(FUSE_LOG_DEBUG, 
"lo_read(ino=%" PRIu64 
", size=%zd, "   883                         "off=%lu)\n", ino, size, (
unsigned long) offset);
   905                 fuse_log(FUSE_LOG_DEBUG, 
"lo_write(ino=%" PRIu64 
", size=%zd, off=%lu)\n",
   906                         ino, out_buf.
buf[0].
size, (
unsigned long) off);
   918         struct statvfs stbuf;
   920         res = fstatvfs(lo_fd(req, ino), &stbuf);
   930         int err = EOPNOTSUPP;
   933 #ifdef HAVE_FALLOCATE   934         err = fallocate(fi->
fh, mode, offset, length);
   938 #elif defined(HAVE_POSIX_FALLOCATE)   944         err = posix_fallocate(fi->
fh, offset, length);
   956         res = flock(fi->
fh, op);
   966         struct lo_inode *inode = lo_inode(req, ino);
   971         if (!lo_data(req)->xattr)
   975                 fuse_log(FUSE_LOG_DEBUG, 
"lo_getxattr(ino=%" PRIu64 
", name=%s size=%zd)\n",
   979         if (inode->is_symlink) {
   985         sprintf(procname, 
"/proc/self/fd/%i", inode->fd);
   988                 value = malloc(size);
   992                 ret = getxattr(procname, name, value, size);
  1001                 ret = getxattr(procname, name, NULL, 0);
  1022         struct lo_inode *inode = lo_inode(req, ino);
  1027         if (!lo_data(req)->xattr)
  1030         if (lo_debug(req)) {
  1031                 fuse_log(FUSE_LOG_DEBUG, 
"lo_listxattr(ino=%" PRIu64 
", size=%zd)\n",
  1035         if (inode->is_symlink) {
  1041         sprintf(procname, 
"/proc/self/fd/%i", inode->fd);
  1044                 value = malloc(size);
  1048                 ret = listxattr(procname, value, size);
  1057                 ret = listxattr(procname, NULL, 0);
  1075                         const char *value, 
size_t size, 
int flags)
  1078         struct lo_inode *inode = lo_inode(req, ino);
  1083         if (!lo_data(req)->xattr)
  1086         if (lo_debug(req)) {
  1087                 fuse_log(FUSE_LOG_DEBUG, 
"lo_setxattr(ino=%" PRIu64 
", name=%s value=%s size=%zd)\n",
  1088                         ino, name, value, size);
  1091         if (inode->is_symlink) {
  1097         sprintf(procname, 
"/proc/self/fd/%i", inode->fd);
  1099         ret = setxattr(procname, name, value, size, flags);
  1100         saverr = ret == -1 ? errno : 0;
  1109         struct lo_inode *inode = lo_inode(req, ino);
  1114         if (!lo_data(req)->xattr)
  1117         if (lo_debug(req)) {
  1118                 fuse_log(FUSE_LOG_DEBUG, 
"lo_removexattr(ino=%" PRIu64 
", name=%s)\n",
  1122         if (inode->is_symlink) {
  1128         sprintf(procname, 
"/proc/self/fd/%i", inode->fd);
  1130         ret = removexattr(procname, name);
  1131         saverr = ret == -1 ? errno : 0;
  1137 #ifdef HAVE_COPY_FILE_RANGE  1147                 fuse_log(FUSE_LOG_DEBUG, 
"lo_copy_file_range(ino=%" PRIu64 
"/fd=%lu, "  1148                                 "off=%lu, ino=%" PRIu64 
"/fd=%lu, "  1149                                 "off=%lu, size=%zd, flags=0x%x)\n",
  1150                         ino_in, fi_in->
fh, off_in, ino_out, fi_out->
fh, off_out,
  1153         res = copy_file_range(fi_in->
fh, &off_in, fi_out->
fh, &off_out, len,
  1168         res = lseek(fi->
fh, off, whence);
  1177         .lookup         = lo_lookup,
  1180         .symlink        = lo_symlink,
  1182         .unlink         = lo_unlink,
  1184         .rename         = lo_rename,
  1185         .forget         = lo_forget,
  1186         .forget_multi   = lo_forget_multi,
  1187         .getattr        = lo_getattr,
  1188         .setattr        = lo_setattr,
  1189         .readlink       = lo_readlink,
  1190         .opendir        = lo_opendir,
  1191         .readdir        = lo_readdir,
  1192         .readdirplus    = lo_readdirplus,
  1193         .releasedir     = lo_releasedir,
  1194         .fsyncdir       = lo_fsyncdir,
  1195         .create         = lo_create,
  1197         .release        = lo_release,
  1201         .write_buf      = lo_write_buf,
  1202         .statfs         = lo_statfs,
  1203         .fallocate      = lo_fallocate,
  1205         .getxattr       = lo_getxattr,
  1206         .listxattr      = lo_listxattr,
  1207         .setxattr       = lo_setxattr,
  1208         .removexattr    = lo_removexattr,
  1209 #ifdef HAVE_COPY_FILE_RANGE  1210         .copy_file_range = lo_copy_file_range,
  1215 int main(
int argc, 
char *argv[])
  1218         struct fuse_session *se;
  1219         struct fuse_cmdline_opts opts;
  1220         struct lo_data lo = { .debug = 0,
  1227         pthread_mutex_init(&lo.mutex, NULL);
  1228         lo.root.next = lo.root.prev = &lo.root;
  1230         lo.cache = CACHE_NORMAL;
  1234         if (opts.show_help) {
  1235                 printf(
"usage: %s [options] <mountpoint>\n\n", argv[0]);
  1240         } 
else if (opts.show_version) {
  1247         if(opts.mountpoint == NULL) {
  1248                 printf(
"usage: %s [options] <mountpoint>\n", argv[0]);
  1249                 printf(
"       %s --help\n", argv[0]);
  1257         lo.debug = opts.debug;
  1258         lo.root.refcount = 2;
  1263                 res = lstat(lo.source, &stat);
  1265                         fuse_log(FUSE_LOG_ERR, 
"failed to stat source (\"%s\"): %m\n",
  1269                 if (!S_ISDIR(stat.st_mode)) {
  1270                         fuse_log(FUSE_LOG_ERR, 
"source is not a directory\n");
  1277         lo.root.is_symlink = 
false;
  1278         if (!lo.timeout_set) {
  1289                         lo.timeout = 86400.0;
  1292         } 
else if (lo.timeout < 0) {
  1293                 fuse_log(FUSE_LOG_ERR, 
"timeout is negative (%lf)\n",
  1298         lo.root.fd = open(lo.source, O_PATH);
  1299         if (lo.root.fd == -1) {
  1300                 fuse_log(FUSE_LOG_ERR, 
"open(\"%s\", O_PATH): %m\n",
  1318         if (opts.singlethread)
  1321                 ret = fuse_session_loop_mt(se, opts.clone_fd);
  1329         free(opts.mountpoint);
  1332         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)
unsigned int cache_readdir
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)