added lowlevel API and (non yet working) implementation
authorMiklos Szeredi <miklos@szeredi.hu>
Mon, 11 Jul 2005 12:32:31 +0000 (12:32 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Mon, 11 Jul 2005 12:32:31 +0000 (12:32 +0000)
include/fuse_common.h [new file with mode: 0644]
include/fuse_lowlevel.h [new file with mode: 0644]
lib/fuse_lowlevel.c [new file with mode: 0644]

diff --git a/include/fuse_common.h b/include/fuse_common.h
new file mode 100644 (file)
index 0000000..cd19435
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001-2005  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU LGPL.
+    See the file COPYING.LIB.
+*/
+
+#if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_)
+#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h instead."
+#endif
+
+/** Information about open files */
+struct fuse_file_info {
+    /** Open flags.  Available in open() and release() */
+    int flags;
+
+    /** File handle.  May be filled in by filesystem in open().
+        Available in all other file operations */
+    unsigned long fh;
+
+    /** In case of a write operation indicates if this was caused by a
+        writepage */
+    int writepage;
+};
+
+/** Extra context that may be needed by some filesystems
+ *
+ * The uid, gid and pid fields are not filled in case of a writepage
+ * operation.
+ */
+struct fuse_context {
+    /** Pointer to the fuse object */
+    struct fuse *fuse;
+
+    /** User ID of the calling process */
+    uid_t uid;
+
+    /** Group ID of the calling process */
+    gid_t gid;
+
+    /** Thread ID of the calling process */
+    pid_t pid;
+
+    /** Private filesystem data */
+    void *private_data;
+};
+
+
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
new file mode 100644 (file)
index 0000000..e90cb84
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001-2005  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU LGPL.
+    See the file COPYING.LIB.
+*/
+
+/* ----------------------------------------------------------- *
+ * Low level API                                               *
+ * ----------------------------------------------------------- */
+
+typedef unsigned long fuse_ino_t;
+typedef struct fuse_req *fuse_req_t;
+
+/* 'to_set' flags in setattr */
+#define FUSE_SET_ATTR_MODE     (1 << 0)
+#define FUSE_SET_ATTR_UID      (1 << 1)
+#define FUSE_SET_ATTR_GID      (1 << 2)
+#define FUSE_SET_ATTR_SIZE     (1 << 3)
+#define FUSE_SET_ATTR_ATIME    (1 << 4)
+#define FUSE_SET_ATTR_MTIME    (1 << 5)
+#define FUSE_SET_ATTR_CTIME    (1 << 6)
+
+struct fuse_lowlevel_operations {
+    void (*lookup)  (fuse_req_t req, fuse_ino_t parent, const char *name);
+    void (*forget)  (fuse_req_t req, fuse_ino_t ino);
+    void (*getattr) (fuse_req_t req, fuse_ino_t ino);
+    void (*readlink)(fuse_req_t req, fuse_ino_t ino);
+    void (*mknod)   (fuse_req_t req, fuse_ino_t parent, const char *name,
+                     mode_t mode, dev_t rdev);
+    void (*mkdir)   (fuse_req_t req, fuse_ino_t parent, const char *name,
+                     mode_t mode);
+    void (*unlink)  (fuse_req_t req, fuse_ino_t parent, const char *name);
+    void (*rmdir)   (fuse_req_t req, fuse_ino_t parent, const char *name);
+    void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+                     const char *name);
+    void (*rename)  (fuse_req_t req, fuse_ino_t parent, const char *name,
+                     fuse_ino_t newparent, const char *newname);
+    void (*link)    (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+                     const char *newname);
+    void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+                     int to_set);
+    void (*open)    (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f);
+    void (*read)    (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+                     struct fuse_file_info *f);
+    void (*write)   (fuse_req_t req, fuse_ino_t ino, const char *buf,
+                     size_t size, off_t off, struct fuse_file_info *f);
+    void (*flush)   (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f);
+    void (*release) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f);
+    void (*fsync)   (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f);
+    void (*opendir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f);
+    void (*readdir)  (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+                     struct fuse_file_info *f);
+    void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+                        struct fuse_file_info *f);
+    void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *f);
+    void (*statfs)  (fuse_req_t req, fuse_ino_t ino);
+    void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name,
+                     const char *value, size_t size, int flags);
+    void (*getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name,
+                     size_t size);
+    void (*listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size);
+    void (*removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name);
+};
+
+/* all except release and forget */
+int fuse_reply_err(fuse_req_t req, int err);
+
+/* forget, unlink, rmdir, rename, setxattr, removexattr, release, releasedir */
+int fuse_reply_ok(fuse_req_t req);
+
+/* lookup, mknod, mkdir, symlink, link */
+int fuse_reply_entry(fuse_req_t req, fuse_ino_t ino, unsigned long generation,
+                     const struct stat *attr, double attr_timeout,
+                     double entry_timeout);
+
+/* getattr, setattr */
+int fuse_reply_attr(fuse_req_t req, int struct stat *attr, double attr_timeout);
+/* readlink */
+int fuse_reply_readlink(fuse_req_t req, const char *link);
+
+/* open, write, flush, fsync, opendir, fsyncdir */
+int fuse_reply_file_info(fuse_req_t req, const struct fuse_file_info *f);
+
+/* read, readdir */
+int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size,
+                    const struct fuse_file_info *f);
+
+/* statfs */
+int fuse_reply_statfs(fuse_req_t req, const struct statfs *statfs);
+
+/* getxattr, listxattr */
+int fuse_reply_xattr(fuse_req_t req, const char *buf, size_t size);
+
+/* return the size of a directory entry */
+size_t fuse_dirent_size(size_t namelen);
+
+/* add a directory entry to the buffer */
+int fuse_add_dirent(char *buf, const char *name, const struct stat *stat,
+                    off_t off);
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
new file mode 100644 (file)
index 0000000..c5e1bf7
--- /dev/null
@@ -0,0 +1,1726 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001-2005  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU LGPL.
+    See the file COPYING.LIB
+*/
+
+#include "fuse_i.h"
+#include "fuse_compat.h"
+#include "fuse_kernel.h"
+#include "fuse_kernel_compat5.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdint.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+
+/* FUSE flags: */
+
+/** Enable debuging output */
+#define FUSE_DEBUG       (1 << 1)
+
+/** If a file is removed but it's still open, don't hide the file but
+    remove it immediately */
+#define FUSE_HARD_REMOVE (1 << 2)
+
+/** Use st_ino field in getattr instead of generating inode numbers  */
+#define FUSE_USE_INO     (1 << 3)
+
+/** Only allow root or the owner to access the filesystem */
+#define FUSE_ALLOW_ROOT  (1 << 4)
+
+/** Make a best effort to fill in inode number in a readdir **/
+#define FUSE_READDIR_INO (1 << 5)
+
+/** Ignore file mode supplied by the filesystem, and create one based
+    on the 'umask' option */
+#define FUSE_SET_MODE (1 << 6)
+
+/** Ignore st_uid supplied by the filesystem and set it based on the
+    'uid' option*/
+#define FUSE_SET_UID (1 << 7)
+
+/** Ignore st_gid supplied by the filesystem and set it based on the
+    'gid' option*/
+#define FUSE_SET_GID (1 << 8)
+
+
+struct fuse_ll {
+    int flags;
+    int fd;
+    struct fuse_lowlevel_operations op;
+    volatile int exited;
+    int got_init;
+    void *user_data;
+    int major;
+    int minor;
+    uid_t owner;
+    uid_t uid;
+    gid_t gid;
+    mode_t umask;
+};
+
+struct fuse_cmd {
+    char *buf;
+    size_t buflen;
+};
+
+static const char *opname(enum fuse_opcode opcode)
+{
+    switch (opcode) {
+    case FUSE_LOOKUP:          return "LOOKUP";
+    case FUSE_FORGET:          return "FORGET";
+    case FUSE_GETATTR:         return "GETATTR";
+    case FUSE_SETATTR:         return "SETATTR";
+    case FUSE_READLINK:                return "READLINK";
+    case FUSE_SYMLINK:         return "SYMLINK";
+    case FUSE_MKNOD:           return "MKNOD";
+    case FUSE_MKDIR:           return "MKDIR";
+    case FUSE_UNLINK:          return "UNLINK";
+    case FUSE_RMDIR:           return "RMDIR";
+    case FUSE_RENAME:          return "RENAME";
+    case FUSE_LINK:            return "LINK";
+    case FUSE_OPEN:            return "OPEN";
+    case FUSE_READ:            return "READ";
+    case FUSE_WRITE:           return "WRITE";
+    case FUSE_STATFS:          return "STATFS";
+    case FUSE_FLUSH:           return "FLUSH";
+    case FUSE_RELEASE:         return "RELEASE";
+    case FUSE_FSYNC:           return "FSYNC";
+    case FUSE_SETXATTR:                return "SETXATTR";
+    case FUSE_GETXATTR:                return "GETXATTR";
+    case FUSE_LISTXATTR:       return "LISTXATTR";
+    case FUSE_REMOVEXATTR:     return "REMOVEXATTR";
+    case FUSE_INIT:            return "INIT";
+    case FUSE_OPENDIR:         return "OPENDIR";
+    case FUSE_READDIR:         return "READDIR";
+    case FUSE_RELEASEDIR:      return "RELEASEDIR";
+    case FUSE_FSYNCDIR:                return "FSYNCDIR";
+    default:                   return "???";
+    }
+}
+
+
+static void convert_stat(struct fuse_ll *f, nodeid_t nodeid, struct stat *stbuf,
+                         struct fuse_attr *attr)
+{
+    attr->ino       = (f->flags & FUSE_USE_INO) ? stbuf->st_ino : nodeid;
+    attr->mode      = stbuf->st_mode;
+    if (f->flags & FUSE_SET_MODE)
+        attr->mode = (attr->mode & S_IFMT) | (0777 & ~f->umask);
+    attr->nlink     = stbuf->st_nlink;
+    attr->uid       = (f->flags & FUSE_SET_UID) ? f->uid : stbuf->st_uid;
+    attr->gid       = (f->flags & FUSE_SET_GID) ? f->gid : stbuf->st_gid;
+    attr->rdev      = stbuf->st_rdev;
+    attr->size      = stbuf->st_size;
+    attr->blocks    = stbuf->st_blocks;
+    attr->atime     = stbuf->st_atime;
+    attr->mtime     = stbuf->st_mtime;
+    attr->ctime     = stbuf->st_ctime;
+#ifdef HAVE_STRUCT_STAT_ST_ATIM
+    attr->atimensec = stbuf->st_atim.tv_nsec;
+    attr->mtimensec = stbuf->st_mtim.tv_nsec;
+    attr->ctimensec = stbuf->st_ctim.tv_nsec;
+#endif
+}
+
+static  size_t iov_length(const struct iovec *iov, size_t count)
+{
+    size_t seg;
+    size_t ret = 0;
+
+    for (seg = 0; seg < count; seg++)
+        ret += iov[seg].iov_len;
+    return ret;
+}
+
+static int send_reply_raw(struct fuse_ll *f, const struct iovec iov[],
+                          size_t count)
+{
+    int res;
+    unsigned outsize = iov_length(iov, count);
+    struct fuse_out_header *out = (struct fuse_out_header *) iov[0].iov_base;
+    out->len = outsize;
+
+    if ((f->flags & FUSE_DEBUG)) {
+        printf("   unique: %llu, error: %i (%s), outsize: %i\n",
+               out->unique, out->error, strerror(-out->error), outsize);
+        fflush(stdout);
+    }
+
+    /* This needs to be done before the reply, otherwise the scheduler
+       could play tricks with us, and only let the counter be
+       increased long after the operation is done */
+    fuse_inc_avail(f);
+
+    res = writev(f->fd, iov, count);
+    if (res == -1) {
+        /* ENOENT means the operation was interrupted */
+        if (!f->exited && errno != ENOENT)
+            perror("fuse: writing device");
+        return -errno;
+    }
+    return 0;
+}
+
+static int send_reply(struct fuse_ll *f, struct fuse_in_header *in, int error,
+                      void *arg, size_t argsize)
+{
+    struct fuse_out_header out;
+    struct iovec iov[2];
+    size_t count;
+
+    if (error <= -1000 || error > 0) {
+        fprintf(stderr, "fuse: bad error value: %i\n",  error);
+        error = -ERANGE;
+    }
+
+    out.unique = in->unique;
+    out.error = error;
+    count = 1;
+    iov[0].iov_base = &out;
+    iov[0].iov_len = sizeof(struct fuse_out_header);
+    if (argsize && !error) {
+        count++;
+        iov[1].iov_base = arg;
+        iov[1].iov_len = argsize;
+    }
+    return send_reply_raw(f, iov, count);
+}
+
+static void do_lookup(struct fuse_ll *f, struct fuse_in_header *in, char *name)
+{
+    int res;
+    int res2;
+    char *path;
+    struct fuse_entry_out arg;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, in->nodeid, name);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("LOOKUP %s\n", path);
+            fflush(stdout);
+        }
+        res = -ENOSYS;
+        if (f->op.getattr)
+            res = lookup_path(f, in->nodeid, in->unique, name, path, &arg);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    res2 = send_reply(f, in, res, &arg, sizeof(arg));
+    if (res == 0 && res2 == -ENOENT)
+        cancel_lookup(f, arg.nodeid, in->unique);
+}
+
+static void do_forget(struct fuse_ll *f, struct fuse_in_header *in,
+                      struct fuse_forget_in *arg)
+{
+    if (f->flags & FUSE_DEBUG) {
+        printf("FORGET %lu/%llu\n", (unsigned long) in->nodeid,
+               arg->nlookup);
+        fflush(stdout);
+    }
+    if (f->major <= 6)
+        forget_node_old(f, in->nodeid, arg->nlookup);
+    else
+        forget_node(f, in->nodeid, arg->nlookup);
+}
+
+static void do_getattr(struct fuse_ll *f, struct fuse_in_header *in)
+{
+    int res;
+    char *path;
+    struct stat buf;
+    struct fuse_attr_out arg;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.getattr)
+            res = f->op.getattr(path, &buf);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+
+    if (res == 0) {
+        memset(&arg, 0, sizeof(struct fuse_attr_out));
+        arg.attr_valid = ATTR_REVALIDATE_TIME;
+        arg.attr_valid_nsec = 0;
+        convert_stat(f, in->nodeid, &buf, &arg.attr);
+    }
+
+    send_reply(f, in, res, &arg, sizeof(arg));
+}
+
+static int do_chmod(struct fuse_ll *f, const char *path, struct fuse_attr *attr)
+{
+    int res;
+
+    res = -ENOSYS;
+    if (f->op.chmod)
+        res = f->op.chmod(path, attr->mode);
+
+    return res;
+}
+
+static int do_chown(struct fuse_ll *f, const char *path, struct fuse_attr *attr,
+                    int valid)
+{
+    int res;
+    uid_t uid = (valid & FATTR_UID) ? attr->uid : (uid_t) -1;
+    gid_t gid = (valid & FATTR_GID) ? attr->gid : (gid_t) -1;
+
+    res = -ENOSYS;
+    if (f->op.chown)
+        res = f->op.chown(path, uid, gid);
+
+    return res;
+}
+
+static int do_truncate(struct fuse_ll *f, const char *path,
+                       struct fuse_attr *attr)
+{
+    int res;
+
+    res = -ENOSYS;
+    if (f->op.truncate)
+        res = f->op.truncate(path, attr->size);
+
+    return res;
+}
+
+static int do_utime(struct fuse_ll *f, const char *path, struct fuse_attr *attr)
+{
+    int res;
+    struct utimbuf buf;
+    buf.actime = attr->atime;
+    buf.modtime = attr->mtime;
+    res = -ENOSYS;
+    if (f->op.utime)
+        res = f->op.utime(path, &buf);
+
+    return res;
+}
+
+static void do_setattr(struct fuse_ll *f, struct fuse_in_header *in,
+                       struct fuse_setattr_in *arg)
+{
+    int res;
+    char *path;
+    int valid = arg->valid;
+    struct fuse_attr *attr = MEMBER_COMPAT(f, arg, attr, fuse_setattr_in);
+    struct fuse_attr_out outarg;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.getattr) {
+            res = 0;
+            if (!res && (valid & FATTR_MODE))
+                res = do_chmod(f, path, attr);
+            if (!res && (valid & (FATTR_UID | FATTR_GID)))
+                res = do_chown(f, path, attr, valid);
+            if (!res && (valid & FATTR_SIZE))
+                res = do_truncate(f, path, attr);
+            if (!res && (valid & (FATTR_ATIME | FATTR_MTIME)) ==
+               (FATTR_ATIME | FATTR_MTIME))
+                res = do_utime(f, path, attr);
+            if (!res) {
+                struct stat buf;
+                res = f->op.getattr(path, &buf);
+                if (!res) {
+                    memset(&outarg, 0, sizeof(struct fuse_attr_out));
+                    outarg.attr_valid = ATTR_REVALIDATE_TIME;
+                    outarg.attr_valid_nsec = 0;
+                    convert_stat(f, in->nodeid, &buf, &outarg.attr);
+                }
+            }
+        }
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, &outarg, sizeof(outarg));
+}
+
+static void do_readlink(struct fuse_ll *f, struct fuse_in_header *in)
+{
+    int res;
+    char link[PATH_MAX + 1];
+    char *path;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.readlink)
+            res = f->op.readlink(path, link, sizeof(link));
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    link[PATH_MAX] = '\0';
+    send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
+}
+
+static void do_mknod(struct fuse_ll *f, struct fuse_in_header *in,
+                     struct fuse_mknod_in *inarg)
+{
+    int res;
+    int res2;
+    char *path;
+    char *name = PARAM(inarg);
+    struct fuse_entry_out outarg;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, in->nodeid, name);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("MKNOD %s\n", path);
+            fflush(stdout);
+        }
+        res = -ENOSYS;
+        if (f->op.mknod && f->op.getattr) {
+            res = f->op.mknod(path, inarg->mode, inarg->rdev);
+            if (res == 0)
+                res = lookup_path(f, in->nodeid, in->unique, name, path, &outarg);
+        }
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
+    if (res == 0 && res2 == -ENOENT)
+        cancel_lookup(f, outarg.nodeid, in->unique);
+}
+
+static void do_mkdir(struct fuse_ll *f, struct fuse_in_header *in,
+                     struct fuse_mkdir_in *inarg)
+{
+    int res;
+    int res2;
+    char *path;
+    char *name = PARAM_COMPAT(f, inarg, fuse_mkdir_in);
+    struct fuse_entry_out outarg;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, in->nodeid, name);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("MKDIR %s\n", path);
+            fflush(stdout);
+        }
+        res = -ENOSYS;
+        if (f->op.mkdir && f->op.getattr) {
+            res = f->op.mkdir(path, inarg->mode);
+            if (res == 0)
+                res = lookup_path(f, in->nodeid, in->unique, name, path, &outarg);
+        }
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
+    if (res == 0 && res2 == -ENOENT)
+        cancel_lookup(f, outarg.nodeid, in->unique);
+}
+
+static void do_unlink(struct fuse_ll *f, struct fuse_in_header *in, char *name)
+{
+    int res;
+    char *path;
+
+    res = -ENOENT;
+    pthread_rwlock_wrlock(&f->tree_lock);
+    path = get_path_name(f, in->nodeid, name);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("UNLINK %s\n", path);
+            fflush(stdout);
+        }
+        res = -ENOSYS;
+        if (f->op.unlink) {
+            if (!(f->flags & FUSE_HARD_REMOVE) && is_open(f, in->nodeid, name))
+                res = hide_node(f, path, in->nodeid, name);
+            else {
+                res = f->op.unlink(path);
+                if (res == 0)
+                    remove_node(f, in->nodeid, name);
+
+            }
+        }
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_rmdir(struct fuse_ll *f, struct fuse_in_header *in, char *name)
+{
+    int res;
+    char *path;
+
+    res = -ENOENT;
+    pthread_rwlock_wrlock(&f->tree_lock);
+    path = get_path_name(f, in->nodeid, name);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("RMDIR %s\n", path);
+            fflush(stdout);
+        }
+        res = -ENOSYS;
+        if (f->op.rmdir) {
+            res = f->op.rmdir(path);
+            if (res == 0)
+                remove_node(f, in->nodeid, name);
+        }
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_symlink(struct fuse_ll *f, struct fuse_in_header *in, char *name,
+                       char *link)
+{
+    int res;
+    int res2;
+    char *path;
+    struct fuse_entry_out outarg;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, in->nodeid, name);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("SYMLINK %s\n", path);
+            fflush(stdout);
+        }
+        res = -ENOSYS;
+        if (f->op.symlink && f->op.getattr) {
+            res = f->op.symlink(link, path);
+            if (res == 0)
+                res = lookup_path(f, in->nodeid, in->unique, name, path, &outarg);
+        }
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
+    if (res == 0 && res2 == -ENOENT)
+        cancel_lookup(f, outarg.nodeid, in->unique);
+
+}
+
+static void do_rename(struct fuse_ll *f, struct fuse_in_header *in,
+                      struct fuse_rename_in *inarg)
+{
+    int res;
+    nodeid_t olddir = in->nodeid;
+    nodeid_t newdir = inarg->newdir;
+    char *oldname = PARAM(inarg);
+    char *newname = oldname + strlen(oldname) + 1;
+    char *oldpath;
+    char *newpath;
+
+    res = -ENOENT;
+    pthread_rwlock_wrlock(&f->tree_lock);
+    oldpath = get_path_name(f, olddir, oldname);
+    if (oldpath != NULL) {
+        newpath = get_path_name(f, newdir, newname);
+        if (newpath != NULL) {
+            if (f->flags & FUSE_DEBUG) {
+                printf("RENAME %s -> %s\n", oldpath, newpath);
+                fflush(stdout);
+            }
+            res = -ENOSYS;
+            if (f->op.rename) {
+                res = 0;
+                if (!(f->flags & FUSE_HARD_REMOVE) &&
+                    is_open(f, newdir, newname))
+                    res = hide_node(f, newpath, newdir, newname);
+                if (res == 0) {
+                    res = f->op.rename(oldpath, newpath);
+                    if (res == 0)
+                        res = rename_node(f, olddir, oldname, newdir, newname, 0);
+                }
+            }
+            free(newpath);
+        }
+        free(oldpath);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_link(struct fuse_ll *f, struct fuse_in_header *in,
+                    struct fuse_link_in *arg)
+{
+    int res;
+    int res2;
+    char *oldpath;
+    char *newpath;
+    char *name = PARAM(arg);
+    struct fuse_entry_out outarg;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    oldpath = get_path(f, arg->oldnodeid);
+    if (oldpath != NULL) {
+        newpath =  get_path_name(f, in->nodeid, name);
+        if (newpath != NULL) {
+            if (f->flags & FUSE_DEBUG) {
+                printf("LINK %s\n", newpath);
+                fflush(stdout);
+            }
+            res = -ENOSYS;
+            if (f->op.link && f->op.getattr) {
+                res = f->op.link(oldpath, newpath);
+                if (res == 0)
+                    res = lookup_path(f, in->nodeid, in->unique, name,
+                                      newpath, &outarg);
+            }
+            free(newpath);
+        }
+        free(oldpath);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
+    if (res == 0 && res2 == -ENOENT)
+        cancel_lookup(f, outarg.nodeid, in->unique);
+}
+
+static void do_open(struct fuse_ll *f, struct fuse_in_header *in,
+                    struct fuse_open_in *arg)
+{
+    int res;
+    char *path;
+    struct fuse_open_out outarg;
+    struct fuse_file_info fi;
+
+    memset(&outarg, 0, sizeof(outarg));
+    memset(&fi, 0, sizeof(fi));
+    fi.flags = arg->flags;
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.open) {
+            if (!f->compat)
+                res = f->op.open(path, &fi);
+            else
+                res = ((struct fuse_operations_compat2 *) &f->op)->open(path, fi.flags);
+        }
+    }
+    if (res == 0) {
+        int res2;
+        outarg.fh = fi.fh;
+        if (f->flags & FUSE_DEBUG) {
+            printf("OPEN[%lu] flags: 0x%x\n", fi.fh, arg->flags);
+            fflush(stdout);
+        }
+
+        pthread_mutex_lock(&f->lock);
+        res2 = send_reply(f, in, res, &outarg, SIZEOF_COMPAT(f, fuse_open_out));
+        if(res2 == -ENOENT) {
+            /* The open syscall was interrupted, so it must be cancelled */
+            if(f->op.release) {
+                if (!f->compat)
+                    f->op.release(path, &fi);
+                else
+                    ((struct fuse_operations_compat2 *) &f->op)->release(path, fi.flags);
+            }
+        } else {
+            struct node *node = get_node(f, in->nodeid);
+            node->open_count ++;
+        }
+        pthread_mutex_unlock(&f->lock);
+    } else
+        send_reply(f, in, res, NULL, 0);
+
+    if (path)
+        free(path);
+    pthread_rwlock_unlock(&f->tree_lock);
+}
+
+static void do_flush(struct fuse_ll *f, struct fuse_in_header *in,
+                     struct fuse_flush_in *arg)
+{
+    char *path;
+    int res;
+    struct fuse_file_info fi;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = arg->fh;
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("FLUSH[%lu]\n", (unsigned long) arg->fh);
+            fflush(stdout);
+        }
+        res = -ENOSYS;
+        if (f->op.flush)
+            res = f->op.flush(path, &fi);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_release(struct fuse_ll *f, struct fuse_in_header *in,
+                       struct fuse_release_in *arg)
+{
+    struct node *node;
+    char *path;
+    struct fuse_file_info fi;
+    int unlink_hidden;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.flags = arg->flags;
+    fi.fh = arg->fh;
+
+    pthread_mutex_lock(&f->lock);
+    node = get_node(f, in->nodeid);
+    assert(node->open_count > 0);
+    --node->open_count;
+    unlink_hidden = (node->is_hidden && !node->open_count);
+    pthread_mutex_unlock(&f->lock);
+
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (f->flags & FUSE_DEBUG) {
+        printf("RELEASE[%lu] flags: 0x%x\n", fi.fh, fi.flags);
+        fflush(stdout);
+    }
+    if (f->op.release) {
+        if (!f->compat)
+            f->op.release(path ? path : "-", &fi);
+        else if (path)
+            ((struct fuse_operations_compat2 *) &f->op)->release(path, fi.flags);
+    }
+
+    if(unlink_hidden && path)
+        f->op.unlink(path);
+
+    if (path)
+        free(path);
+    pthread_rwlock_unlock(&f->tree_lock);
+
+    send_reply(f, in, 0, NULL, 0);
+}
+
+static void do_read(struct fuse_ll *f, struct fuse_in_header *in,
+                    struct fuse_read_in *arg)
+{
+    int res;
+    char *path;
+    size_t size;
+    char *buf;
+    struct fuse_file_info fi;
+
+    buf = (char *) malloc(arg->size);
+    if (buf == NULL) {
+        send_reply(f, in, -ENOMEM, NULL, 0);
+        return;
+    }
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = arg->fh;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("READ[%lu] %u bytes from %llu\n",
+                   (unsigned long) arg->fh, arg->size, arg->offset);
+            fflush(stdout);
+        }
+
+        res = -ENOSYS;
+        if (f->op.read)
+            res = f->op.read(path, buf, arg->size, arg->offset, &fi);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+
+    size = 0;
+    if (res >= 0) {
+        size = res;
+        res = 0;
+        if (f->flags & FUSE_DEBUG) {
+            printf("   READ[%lu] %u bytes\n", (unsigned long) arg->fh,
+                   size);
+            fflush(stdout);
+        }
+    }
+
+    send_reply(f, in, res, buf, size);
+    free(buf);
+}
+
+static void do_write(struct fuse_ll *f, struct fuse_in_header *in,
+                     struct fuse_write_in *arg)
+{
+    int res;
+    char *path;
+    struct fuse_write_out outarg;
+    struct fuse_file_info fi;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = arg->fh;
+    fi.writepage = arg->write_flags & 1;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("WRITE%s[%lu] %u bytes to %llu\n",
+                   (arg->write_flags & 1) ? "PAGE" : "",
+                   (unsigned long) arg->fh, arg->size, arg->offset);
+            fflush(stdout);
+        }
+
+        res = -ENOSYS;
+        if (f->op.write)
+            res = f->op.write(path, PARAM(arg), arg->size, arg->offset, &fi);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+
+    memset(&outarg, 0, sizeof(outarg));
+    if (res >= 0) {
+        outarg.size = res;
+        res = 0;
+    }
+
+    send_reply(f, in, res, &outarg, SIZEOF_COMPAT(f, fuse_write_out));
+}
+
+static int default_statfs(struct statfs *buf)
+{
+    buf->f_namelen = 255;
+    buf->f_bsize = 512;
+    return 0;
+}
+
+static void convert_statfs_compat(struct fuse_statfs_compat1 *compatbuf,
+                                  struct statfs *statfs)
+{
+    statfs->f_bsize   = compatbuf->block_size;
+    statfs->f_blocks  = compatbuf->blocks;
+    statfs->f_bfree   = compatbuf->blocks_free;
+    statfs->f_bavail  = compatbuf->blocks_free;
+    statfs->f_files   = compatbuf->files;
+    statfs->f_ffree   = compatbuf->files_free;
+    statfs->f_namelen = compatbuf->namelen;
+}
+
+static void convert_statfs(struct statfs *statfs, struct fuse_kstatfs *kstatfs)
+{
+    kstatfs->bsize     = statfs->f_bsize;
+    kstatfs->blocks    = statfs->f_blocks;
+    kstatfs->bfree     = statfs->f_bfree;
+    kstatfs->bavail    = statfs->f_bavail;
+    kstatfs->files     = statfs->f_files;
+    kstatfs->ffree     = statfs->f_ffree;
+    kstatfs->namelen   = statfs->f_namelen;
+}
+
+static void do_statfs(struct fuse_ll *f, struct fuse_in_header *in)
+{
+    int res;
+    struct fuse_statfs_out arg;
+    struct statfs buf;
+
+    memset(&buf, 0, sizeof(struct statfs));
+    if (f->op.statfs) {
+        if (!f->compat || f->compat > 11)
+            res = f->op.statfs("/", &buf);
+        else {
+            struct fuse_statfs_compat1 compatbuf;
+            memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1));
+            res = ((struct fuse_operations_compat1 *) &f->op)->statfs(&compatbuf);
+            if (res == 0)
+                convert_statfs_compat(&compatbuf, &buf);
+        }
+    }
+    else
+        res = default_statfs(&buf);
+
+    if (res == 0)
+        convert_statfs(&buf, &arg.st);
+
+    send_reply(f, in, res, &arg, sizeof(arg));
+}
+
+static void do_fsync(struct fuse_ll *f, struct fuse_in_header *in,
+                     struct fuse_fsync_in *inarg)
+{
+    int res;
+    char *path;
+    struct fuse_file_info fi;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = inarg->fh;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        if (f->flags & FUSE_DEBUG) {
+            printf("FSYNC[%lu]\n", (unsigned long) inarg->fh);
+            fflush(stdout);
+        }
+        res = -ENOSYS;
+        if (f->op.fsync)
+            res = f->op.fsync(path, inarg->fsync_flags & 1, &fi);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_setxattr(struct fuse_ll *f, struct fuse_in_header *in,
+                        struct fuse_setxattr_in *arg)
+{
+    int res;
+    char *path;
+    char *name = PARAM(arg);
+    unsigned char *value = name + strlen(name) + 1;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.setxattr)
+            res = f->op.setxattr(path, name, value, arg->size, arg->flags);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static int common_getxattr(struct fuse_ll *f, struct fuse_in_header *in,
+                           const char *name, char *value, size_t size)
+{
+    int res;
+    char *path;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.getxattr)
+            res = f->op.getxattr(path, name, value, size);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    return res;
+}
+
+static void do_getxattr_read(struct fuse_ll *f, struct fuse_in_header *in,
+                             const char *name, size_t size)
+{
+    int res;
+    char *value = (char *) malloc(size);
+    if (value == NULL) {
+        send_reply(f, in, -ENOMEM, NULL, 0);
+        return;
+    }
+    res = common_getxattr(f, in, name, value, size);
+    size = 0;
+    if (res > 0) {
+        size = res;
+        res = 0;
+    }
+    send_reply(f, in, res, value, size);
+    free(value);
+}
+
+static void do_getxattr_size(struct fuse_ll *f, struct fuse_in_header *in,
+                             const char *name)
+{
+    int res;
+    struct fuse_getxattr_out arg;
+
+    memset(&arg, 0, sizeof(arg));
+    res = common_getxattr(f, in, name, NULL, 0);
+    if (res >= 0) {
+        arg.size = res;
+        res = 0;
+    }
+    send_reply(f, in, res, &arg, SIZEOF_COMPAT(f, fuse_getxattr_out));
+}
+
+static void do_getxattr(struct fuse_ll *f, struct fuse_in_header *in,
+                        struct fuse_getxattr_in *arg)
+{
+    char *name = PARAM(arg);
+
+    if (arg->size)
+        do_getxattr_read(f, in, name, arg->size);
+    else
+        do_getxattr_size(f, in, name);
+}
+
+static int common_listxattr(struct fuse_ll *f, struct fuse_in_header *in,
+                            char *list, size_t size)
+{
+    int res;
+    char *path;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.listxattr)
+            res = f->op.listxattr(path, list, size);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    return res;
+}
+
+static void do_listxattr_read(struct fuse_ll *f, struct fuse_in_header *in,
+                              size_t size)
+{
+    int res;
+    char *list = (char *) malloc(size);
+    if (list == NULL) {
+        send_reply(f, in, -ENOMEM, NULL, 0);
+        return;
+    }
+    res = common_listxattr(f, in, list, size);
+    size = 0;
+    if (res > 0) {
+        size = res;
+        res = 0;
+    }
+    send_reply(f, in, res, list, size);
+    free(list);
+}
+
+static void do_listxattr_size(struct fuse_ll *f, struct fuse_in_header *in)
+{
+    int res;
+    struct fuse_getxattr_out arg;
+
+    memset(&arg, 0, sizeof(arg));
+    res = common_listxattr(f, in, NULL, 0);
+    if (res >= 0) {
+        arg.size = res;
+        res = 0;
+    }
+    send_reply(f, in, res, &arg, SIZEOF_COMPAT(f, fuse_getxattr_out));
+}
+
+static void do_listxattr(struct fuse_ll *f, struct fuse_in_header *in,
+                         struct fuse_getxattr_in *arg)
+{
+    if (arg->size)
+        do_listxattr_read(f, in, arg->size);
+    else
+        do_listxattr_size(f, in);
+}
+
+static void do_removexattr(struct fuse_ll *f, struct fuse_in_header *in,
+                           char *name)
+{
+    int res;
+    char *path;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.removexattr)
+            res = f->op.removexattr(path, name);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_init(struct fuse_ll *f, struct fuse_in_header *in,
+                    struct fuse_init_in_out *arg)
+{
+    struct fuse_init_in_out outarg;
+
+    if (in->padding == 5) {
+        arg->minor = arg->major;
+        arg->major = in->padding;
+    }
+
+    if (f->flags & FUSE_DEBUG) {
+        printf("INIT: %u.%u\n", arg->major, arg->minor);
+        fflush(stdout);
+    }
+    f->got_init = 1;
+    if (f->op.init)
+        f->user_data = f->op.init();
+
+    if (arg->major == 5) {
+        f->major = 5;
+        f->minor = 1;
+    } else if (arg->major == 6) {
+        f->major = 6;
+        f->minor = 1;
+    } else {
+        f->major = FUSE_KERNEL_VERSION;
+        f->minor = FUSE_KERNEL_MINOR_VERSION;
+    }
+    memset(&outarg, 0, sizeof(outarg));
+    outarg.major = f->major;
+    outarg.minor = f->minor;
+
+    if (f->flags & FUSE_DEBUG) {
+        printf("   INIT: %u.%u\n", outarg.major, outarg.minor);
+        fflush(stdout);
+    }
+
+    send_reply(f, in, 0, &outarg, sizeof(outarg));
+}
+
+static struct fuse_dirhandle *get_dirhandle(unsigned long fh)
+{
+    return (struct fuse_dirhandle *) fh;
+}
+
+static void do_opendir(struct fuse_ll *f, struct fuse_in_header *in,
+                       struct fuse_open_in *arg)
+{
+    int res;
+    struct fuse_open_out outarg;
+    struct fuse_dirhandle *dh;
+
+    dh = (struct fuse_dirhandle *) malloc(sizeof(struct fuse_dirhandle));
+    if (dh == NULL) {
+        send_reply(f, in, -ENOMEM, NULL, 0);
+        return;
+    }
+    memset(dh, 0, sizeof(struct fuse_dirhandle));
+    dh->fuse = f;
+    dh->contents = NULL;
+    dh->len = 0;
+    dh->filled = 0;
+    dh->nodeid = in->nodeid;
+    mutex_init(&dh->lock);
+
+    memset(&outarg, 0, sizeof(outarg));
+    outarg.fh = (unsigned long) dh;
+
+    if (f->op.opendir) {
+        struct fuse_file_info fi;
+        char *path;
+
+        memset(&fi, 0, sizeof(fi));
+        fi.flags = arg->flags;
+
+        res = -ENOENT;
+        pthread_rwlock_rdlock(&f->tree_lock);
+        path = get_path(f, in->nodeid);
+        if (path != NULL) {
+            res = f->op.opendir(path, &fi);
+            dh->fh = fi.fh;
+        }
+        if (res == 0) {
+            int res2;
+            pthread_mutex_lock(&f->lock);
+            res2 = send_reply(f, in, res, &outarg, SIZEOF_COMPAT(f, fuse_open_out));
+            if(res2 == -ENOENT) {
+                /* The opendir syscall was interrupted, so it must be
+                   cancelled */
+                if(f->op.releasedir)
+                    f->op.releasedir(path, &fi);
+                pthread_mutex_destroy(&dh->lock);
+                free(dh);
+            }
+            pthread_mutex_unlock(&f->lock);
+        } else {
+            send_reply(f, in, res, NULL, 0);
+            free(dh);
+        }
+        free(path);
+        pthread_rwlock_unlock(&f->tree_lock);
+    } else
+        send_reply(f, in, 0, &outarg, SIZEOF_COMPAT(f, fuse_open_out));
+}
+
+static int fill_dir_common(struct fuse_dirhandle *dh, const char *name,
+                           int type, ino_t ino, off_t off)
+{
+    unsigned namelen = strlen(name);
+    unsigned entlen;
+    unsigned entsize;
+    unsigned padlen;
+    unsigned newlen;
+    unsigned char *newptr;
+
+    if (!(dh->fuse->flags & FUSE_USE_INO)) {
+        ino = (ino_t) -1;
+        if (dh->fuse->flags & FUSE_READDIR_INO) {
+            struct node *node;
+            pthread_mutex_lock(&dh->fuse->lock);
+            node = lookup_node(dh->fuse, dh->nodeid, name);
+            if (node)
+                ino  = (ino_t) node->nodeid;
+            pthread_mutex_unlock(&dh->fuse->lock);
+        }
+    }
+
+    if (namelen > FUSE_NAME_MAX)
+        namelen = FUSE_NAME_MAX;
+    else if (!namelen) {
+        dh->error = -EIO;
+        return 1;
+    }
+
+    entlen = (dh->fuse->major == 5 ?
+              FUSE_NAME_OFFSET_COMPAT5 : FUSE_NAME_OFFSET) + namelen;
+    entsize = FUSE_DIRENT_ALIGN(entlen);
+    padlen = entsize - entlen;
+    newlen = dh->len + entsize;
+    if (off && dh->fuse->major != 5) {
+        dh->filled = 0;
+        if (newlen > dh->needlen)
+            return 1;
+    }
+
+    newptr = realloc(dh->contents, newlen);
+    if (!newptr) {
+        dh->error = -ENOMEM;
+        return 1;
+    }
+    dh->contents = newptr;
+    if (dh->fuse->major == 5) {
+        struct fuse_dirent_compat5 *dirent;
+        dirent = (struct fuse_dirent_compat5 *) (dh->contents + dh->len);
+        dirent->ino = ino;
+        dirent->namelen = namelen;
+        dirent->type = type;
+        strncpy(dirent->name, name, namelen);
+    } else {
+        struct fuse_dirent *dirent;
+        dirent = (struct fuse_dirent *) (dh->contents + dh->len);
+        dirent->ino = ino;
+        dirent->off = off ? off : newlen;
+        dirent->namelen = namelen;
+        dirent->type = type;
+        strncpy(dirent->name, name, namelen);
+    }
+    if (padlen)
+        memset(dh->contents + dh->len + entlen, 0, padlen);
+    dh->len = newlen;
+    return 0;
+}
+
+static int fill_dir(void *buf, const char *name, const struct stat *stat,
+                    off_t off)
+{
+    int type = stat ? (stat->st_mode & 0170000) >> 12 : 0;
+    ino_t ino = stat ? stat->st_ino : (ino_t) -1;
+    return fill_dir_common(buf, name, type, ino, off);
+}
+
+static int fill_dir_old(struct fuse_dirhandle *dh, const char *name, int type,
+                        ino_t ino)
+{
+    fill_dir_common(dh, name, type, ino, 0);
+    return dh->error;
+}
+
+static int readdir_fill(struct fuse_ll *f, struct fuse_in_header *in,
+                        struct fuse_read_in *arg, struct fuse_dirhandle *dh)
+{
+    int err = -ENOENT;
+    char *path;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        struct fuse_file_info fi;
+
+        memset(&fi, 0, sizeof(fi));
+        fi.fh = dh->fh;
+
+        dh->len = 0;
+        dh->error = 0;
+        dh->needlen = arg->size;
+        dh->filled = 1;
+        err = -ENOSYS;
+        if (f->op.readdir) {
+            off_t offset = f->major == 5 ? 0 : arg->offset;
+            err = f->op.readdir(path, dh, fill_dir, offset, &fi);
+        } else if (f->op.getdir)
+            err = f->op.getdir(path, dh, fill_dir_old);
+        if (!err)
+            err = dh->error;
+        if (err)
+            dh->filled = 0;
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    return err;
+}
+
+static void do_readdir(struct fuse_ll *f, struct fuse_in_header *in,
+                       struct fuse_read_in *arg)
+{
+    int err = 0;
+    struct fuse_dirhandle *dh = get_dirhandle(arg->fh);
+    size_t size = 0;
+    unsigned char *buf = NULL;
+
+    pthread_mutex_lock(&dh->lock);
+    if (!dh->filled) {
+        err = readdir_fill(f, in, arg, dh);
+        if (err)
+            goto out;
+    }
+    if (dh->filled) {
+        if (arg->offset < dh->len) {
+            size = arg->size;
+            if (arg->offset + size > dh->len)
+                size = dh->len - arg->offset;
+            buf = dh->contents + arg->offset;
+        }
+    } else {
+        size = dh->len;
+        buf = dh->contents;
+    }
+
+ out:
+    send_reply(f, in, err, buf, size);
+    pthread_mutex_unlock(&dh->lock);
+}
+
+static void do_releasedir(struct fuse_ll *f, struct fuse_in_header *in,
+                          struct fuse_release_in *arg)
+{
+    struct fuse_dirhandle *dh = get_dirhandle(arg->fh);
+    if (f->op.releasedir) {
+        char *path;
+        struct fuse_file_info fi;
+
+        memset(&fi, 0, sizeof(fi));
+        fi.fh = dh->fh;
+
+        pthread_rwlock_rdlock(&f->tree_lock);
+        path = get_path(f, in->nodeid);
+        f->op.releasedir(path ? path : "-", &fi);
+        free(path);
+        pthread_rwlock_unlock(&f->tree_lock);
+    }
+    pthread_mutex_lock(&dh->lock);
+    pthread_mutex_unlock(&dh->lock);
+    pthread_mutex_destroy(&dh->lock);
+    free(dh->contents);
+    free(dh);
+    send_reply(f, in, 0, NULL, 0);
+}
+
+static void do_fsyncdir(struct fuse_ll *f, struct fuse_in_header *in,
+                        struct fuse_fsync_in *inarg)
+{
+    int res;
+    char *path;
+    struct fuse_dirhandle *dh = get_dirhandle(inarg->fh);
+    struct fuse_file_info fi;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = dh->fh;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, in->nodeid);
+    if (path != NULL) {
+        res = -ENOSYS;
+        if (f->op.fsyncdir)
+            res = f->op.fsyncdir(path, inarg->fsync_flags & 1, &fi);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void free_cmd(struct fuse_cmd *cmd)
+{
+    free(cmd->buf);
+    free(cmd);
+}
+
+void fuse_process_cmd(struct fuse_ll *f, struct fuse_cmd *cmd)
+{
+    struct fuse_in_header *in = (struct fuse_in_header *) cmd->buf;
+    void *inarg = cmd->buf + SIZEOF_COMPAT(f, fuse_in_header);
+    struct fuse_context *ctx = fuse_get_context();
+
+    fuse_dec_avail(f);
+
+    if ((f->flags & FUSE_DEBUG)) {
+        printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n",
+               in->unique, opname(in->opcode), in->opcode,
+               (unsigned long) in->nodeid, cmd->buflen);
+        fflush(stdout);
+    }
+
+    if (!f->got_init && in->opcode != FUSE_INIT) {
+        /* Old kernel version probably */
+        send_reply(f, in, -EPROTO, NULL, 0);
+        goto out;
+    }
+
+    if ((f->flags & FUSE_ALLOW_ROOT) && in->uid != f->owner && in->uid != 0 &&
+        in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
+        in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
+        in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
+        in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) {
+        send_reply(f, in, -EACCES, NULL, 0);
+        goto out;
+    }
+
+    ctx->fuse = f;
+    ctx->uid = in->uid;
+    ctx->gid = in->gid;
+    ctx->pid = in->pid;
+    ctx->private_data = f->user_data;
+
+    switch (in->opcode) {
+    case FUSE_LOOKUP:
+        do_lookup(f, in, (char *) inarg);
+        break;
+
+    case FUSE_GETATTR:
+        do_getattr(f, in);
+        break;
+
+    case FUSE_SETATTR:
+        do_setattr(f, in, (struct fuse_setattr_in *) inarg);
+        break;
+
+    case FUSE_READLINK:
+        do_readlink(f, in);
+        break;
+
+    case FUSE_MKNOD:
+        do_mknod(f, in, (struct fuse_mknod_in *) inarg);
+        break;
+
+    case FUSE_MKDIR:
+        do_mkdir(f, in, (struct fuse_mkdir_in *) inarg);
+        break;
+
+    case FUSE_UNLINK:
+        do_unlink(f, in, (char *) inarg);
+        break;
+
+    case FUSE_RMDIR:
+        do_rmdir(f, in, (char *) inarg);
+        break;
+
+    case FUSE_SYMLINK:
+        do_symlink(f, in, (char *) inarg,
+                   ((char *) inarg) + strlen((char *) inarg) + 1);
+        break;
+
+    case FUSE_RENAME:
+        do_rename(f, in, (struct fuse_rename_in *) inarg);
+        break;
+
+    case FUSE_LINK:
+        do_link(f, in, (struct fuse_link_in *) inarg);
+        break;
+
+    case FUSE_OPEN:
+        do_open(f, in, (struct fuse_open_in *) inarg);
+        break;
+
+    case FUSE_FLUSH:
+        do_flush(f, in, (struct fuse_flush_in *) inarg);
+        break;
+
+    case FUSE_RELEASE:
+        do_release(f, in, (struct fuse_release_in *) inarg);
+        break;
+
+    case FUSE_READ:
+        do_read(f, in, (struct fuse_read_in *) inarg);
+        break;
+
+    case FUSE_WRITE:
+        do_write(f, in, (struct fuse_write_in *) inarg);
+        break;
+
+    case FUSE_STATFS:
+        do_statfs(f, in);
+        break;
+
+    case FUSE_FSYNC:
+        do_fsync(f, in, (struct fuse_fsync_in *) inarg);
+        break;
+
+    case FUSE_SETXATTR:
+        do_setxattr(f, in, (struct fuse_setxattr_in *) inarg);
+        break;
+
+    case FUSE_GETXATTR:
+        do_getxattr(f, in, (struct fuse_getxattr_in *) inarg);
+        break;
+
+    case FUSE_LISTXATTR:
+        do_listxattr(f, in, (struct fuse_getxattr_in *) inarg);
+        break;
+
+    case FUSE_REMOVEXATTR:
+        do_removexattr(f, in, (char *) inarg);
+        break;
+
+    case FUSE_INIT:
+        do_init(f, in, (struct fuse_init_in_out *) inarg);
+        break;
+
+    case FUSE_OPENDIR:
+        do_opendir(f, in, (struct fuse_open_in *) inarg);
+        break;
+
+    case FUSE_READDIR:
+        do_readdir(f, in, (struct fuse_read_in *) inarg);
+        break;
+
+    case FUSE_RELEASEDIR:
+        do_releasedir(f, in, (struct fuse_release_in *) inarg);
+        break;
+
+    case FUSE_FSYNCDIR:
+        do_fsyncdir(f, in, (struct fuse_fsync_in *) inarg);
+        break;
+
+    default:
+        send_reply(f, in, -ENOSYS, NULL, 0);
+    }
+
+ out:
+    free_cmd(cmd);
+}
+
+int fuse_exited(struct fuse_ll* f)
+{
+    return f->exited;
+}
+
+struct fuse_cmd *fuse_read_cmd(struct fuse_ll *f)
+{
+    ssize_t res;
+    struct fuse_cmd *cmd;
+    struct fuse_in_header *in;
+    void *inarg;
+
+    cmd = (struct fuse_cmd *) malloc(sizeof(struct fuse_cmd));
+    if (cmd == NULL) {
+        fprintf(stderr, "fuse: failed to allocate cmd in read\n");
+        return NULL;
+    }
+    cmd->buf = (char *) malloc(FUSE_MAX_IN);
+    if (cmd->buf == NULL) {
+        fprintf(stderr, "fuse: failed to allocate read buffer\n");
+        free(cmd);
+        return NULL;
+    }
+    in = (struct fuse_in_header *) cmd->buf;
+    inarg = cmd->buf + SIZEOF_COMPAT(f, fuse_in_header);
+
+    res = read(f->fd, cmd->buf, FUSE_MAX_IN);
+    if (res == -1) {
+        free_cmd(cmd);
+        if (fuse_exited(f) || errno == EINTR || errno == ENOENT)
+            return NULL;
+
+        /* ENODEV means we got unmounted, so we silenty return failure */
+        if (errno != ENODEV) {
+            /* BAD... This will happen again */
+            perror("fuse: reading device");
+        }
+
+        fuse_exit(f);
+        return NULL;
+    }
+    if ((size_t) res < SIZEOF_COMPAT(f, fuse_in_header)) {
+        free_cmd(cmd);
+        /* Cannot happen */
+        fprintf(stderr, "short read on fuse device\n");
+        fuse_exit(f);
+        return NULL;
+    }
+    cmd->buflen = res;
+
+    /* Forget is special, it can be done without messing with threads. */
+    if (in->opcode == FUSE_FORGET) {
+        do_forget(f, in, (struct fuse_forget_in *) inarg);
+        free_cmd(cmd);
+        return NULL;
+    }
+
+    return cmd;
+}
+
+int fuse_loop(struct fuse_ll *f)
+{
+    if (f == NULL)
+        return -1;
+
+    while (1) {
+        struct fuse_cmd *cmd;
+
+        if (fuse_exited(f))
+            break;
+
+        cmd = fuse_read_cmd(f);
+        if (cmd == NULL)
+            continue;
+
+        fuse_process_cmd(f, cmd);
+    }
+    f->exited = 0;
+    return 0;
+}
+
+int fuse_invalidate(struct fuse_ll *f, const char *path)
+{
+    (void) f;
+    (void) path;
+    return -EINVAL;
+}
+
+void fuse_exit(struct fuse_ll *f)
+{
+    f->exited = 1;
+}
+
+struct fuse_context *fuse_get_context()
+{
+    static struct fuse_context context;
+    if (fuse_getcontext)
+        return fuse_getcontext();
+    else
+        return &context;
+}
+
+void fuse_set_getcontext_func(struct fuse_context *(*func)(void))
+{
+    fuse_getcontext = func;
+}
+
+static int begins_with(const char *s, const char *beg)
+{
+    if (strncmp(s, beg, strlen(beg)) == 0)
+        return 1;
+    else
+        return 0;
+}
+
+int fuse_is_lib_option(const char *opt)
+{
+    if (strcmp(opt, "debug") == 0 ||
+        strcmp(opt, "hard_remove") == 0 ||
+        strcmp(opt, "use_ino") == 0 ||
+        strcmp(opt, "allow_root") == 0 ||
+        strcmp(opt, "readdir_ino") == 0 ||
+        begins_with(opt, "umask=") ||
+        begins_with(opt, "uid=") ||
+        begins_with(opt, "gid="))
+        return 1;
+    else
+        return 0;
+}
+
+static int parse_lib_opts(struct fuse_ll *f, const char *opts)
+{
+    if (opts) {
+        char *xopts = strdup(opts);
+        char *s = xopts;
+        char *opt;
+
+        if (xopts == NULL) {
+            fprintf(stderr, "fuse: memory allocation failed\n");
+            return -1;
+        }
+
+        while((opt = strsep(&s, ","))) {
+            if (strcmp(opt, "debug") == 0)
+                f->flags |= FUSE_DEBUG;
+            else if (strcmp(opt, "hard_remove") == 0)
+                f->flags |= FUSE_HARD_REMOVE;
+            else if (strcmp(opt, "use_ino") == 0)
+                f->flags |= FUSE_USE_INO;
+            else if (strcmp(opt, "allow_root") == 0)
+                f->flags |= FUSE_ALLOW_ROOT;
+            else if (strcmp(opt, "readdir_ino") == 0)
+                f->flags |= FUSE_READDIR_INO;
+            else if (sscanf(opt, "umask=%o", &f->umask) == 1)
+                f->flags |= FUSE_SET_MODE;
+            else if (sscanf(opt, "uid=%u", &f->uid) == 1)
+                f->flags |= FUSE_SET_UID;
+            else if(sscanf(opt, "gid=%u", &f->gid) == 1)
+                f->flags |= FUSE_SET_GID;
+            else
+                fprintf(stderr, "fuse: warning: unknown option `%s'\n", opt);
+        }
+        free(xopts);
+    }
+    return 0;
+}
+
+struct fuse_ll *fuse_lowlevel_new(int fd, const char *opts,
+                                  const struct fuse_lowlevel_operations *op,
+                                  size_t op_size)
+{
+    struct fuse_ll *f;
+
+    if (sizeof(struct fuse_lowlevel_operations) < op_size) {
+        fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
+        op_size = sizeof(struct fuse_lowlevel_operations);
+    }
+
+    f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
+    if (f == NULL) {
+        fprintf(stderr, "fuse: failed to allocate fuse object\n");
+        goto out;
+    }
+
+    if (parse_lib_opts(f, opts) == -1)
+        goto out_free;
+
+    f->fd = fd;
+    memcpy(&f->op, op, op_size);
+    f->exited = 0;
+    f->owner = getuid();
+
+    return f;
+
+ out_free:
+    free(f);
+ out:
+    return NULL;
+}
+
+