Refactor request opcode handlers.

This is mostly a structural change.  The handlers have been moved
into individual functions, which will help with upcoming changes.

Change-Id: I774739d859e177d6b5d4186d2771444166b734fa
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 4d72789..7177395 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -363,7 +363,9 @@
 
 struct node *lookup_by_inode(struct fuse *fuse, __u64 nid)
 {
-    if (nid == FUSE_ROOT_ID) {
+    if (!nid) {
+        return NULL;
+    } else if (nid == FUSE_ROOT_ID) {
         return &fuse->root;
     } else {
         return id_to_ptr(nid);
@@ -517,133 +519,537 @@
     }
 }
 
-void lookup_entry(struct fuse *fuse, struct node *node,
-                  const char *name, __u64 unique)
+static void lookup_entry(struct fuse* fuse, struct fuse_handler* handler,
+        __u64 unique, struct node* parent_node, const char* name)
 {
     struct fuse_entry_out out;
-    
-    memset(&out, 0, sizeof(out));
+    struct node* node;
 
-    node = node_lookup(fuse, node, name, &out.attr);
+    memset(&out, 0, sizeof(out));
+    node = node_lookup(fuse, parent_node, name, &out.attr);
     if (!node) {
         fuse_status(fuse, unique, -ENOENT);
         return;
     }
-    
+
     node->refcount++;
-//    fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
+    TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
     out.nodeid = node->nid;
     out.generation = node->gen;
     out.entry_valid = 10;
     out.attr_valid = 10;
-    
+
     fuse_reply(fuse, unique, &out, sizeof(out));
 }
 
-void handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
-        const struct fuse_in_header *hdr, const void *data, size_t data_len)
+static void handle_lookup(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header *hdr, const char* name)
 {
-    struct node *node;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
 
-    if (hdr->nodeid) {
-        node = lookup_by_inode(fuse, hdr->nodeid);
-        if (!node) {
-            fuse_status(fuse, hdr->unique, -ENOENT);
-            return;
-        }
-    } else {
-        node = 0;
+    TRACE("LOOKUP %s @ %llx (%s)\n", name, hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
     }
 
+    lookup_entry(fuse, handler, hdr->unique, node, name);
+}
+
+static void handle_forget(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
+{
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+    __u64 n = req->nlookup;
+
+    TRACE("FORGET #%lld @ %llx (%s)\n", n, hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    while (n--) {
+        node_release(node);
+    }
+    /* no reply */
+}
+
+static void handle_getaddr(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header *hdr, const struct fuse_getattr_in *req)
+{
+    struct fuse_attr_out out;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    TRACE("GETATTR flags=%x fh=%llx @ %llx (%s)\n", req->getattr_flags, req->fh,
+            hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    memset(&out, 0, sizeof(out));
+    node_get_attr(node, &out.attr);
+    out.attr_valid = 10;
+
+    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+}
+
+static void handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
+{
+    struct fuse_attr_out out;
+    char *path, buffer[PATH_MAX];
+    int res = 0;
+    struct timespec times[2];
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    TRACE("SETATTR fh=%llx valid=%x @ %llx (%s)\n", req->fh, req->valid,
+            hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    /* XXX: incomplete implementation on purpose.   chmod/chown
+     * should NEVER be implemented.*/
+
+    path = node_get_path(node, buffer, 0);
+    if (req->valid & FATTR_SIZE)
+        res = truncate(path, req->size);
+    if (res < 0) {
+        fuse_status(fuse, hdr->unique, -errno);
+        return;
+    }
+
+    /* Handle changing atime and mtime.  If FATTR_ATIME_and FATTR_ATIME_NOW
+     * are both set, then set it to the current time.  Else, set it to the
+     * time specified in the request.  Same goes for mtime.  Use utimensat(2)
+     * as it allows ATIME and MTIME to be changed independently, and has
+     * nanosecond resolution which fuse also has.
+     */
+    if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
+        times[0].tv_nsec = UTIME_OMIT;
+        times[1].tv_nsec = UTIME_OMIT;
+        if (req->valid & FATTR_ATIME) {
+            if (req->valid & FATTR_ATIME_NOW) {
+              times[0].tv_nsec = UTIME_NOW;
+            } else {
+              times[0].tv_sec = req->atime;
+              times[0].tv_nsec = req->atimensec;
+            }
+        }
+        if (req->valid & FATTR_MTIME) {
+            if (req->valid & FATTR_MTIME_NOW) {
+              times[1].tv_nsec = UTIME_NOW;
+            } else {
+              times[1].tv_sec = req->mtime;
+              times[1].tv_nsec = req->mtimensec;
+            }
+        }
+        TRACE("Calling utimensat on %s with atime %ld, mtime=%ld\n", path, times[0].tv_sec, times[1].tv_sec);
+        res = utimensat(-1, path, times, 0);
+        if (res < 0) {
+            fuse_status(fuse, hdr->unique, -errno);
+            return;
+        }
+    }
+
+    memset(&out, 0, sizeof(out));
+    node_get_attr(node, &out.attr);
+    out.attr_valid = 10;
+    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+}
+
+static void handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
+{
+    char *path, buffer[PATH_MAX];
+    int res;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    TRACE("MKNOD %s @ %llx (%s)\n", name, hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    path = node_get_path(node, buffer, name);
+
+    __u32 mode = (req->mode & (~0777)) | 0664;
+    res = mknod(path, mode, req->rdev); /* XXX perm?*/
+    if (res < 0) {
+        fuse_status(fuse, hdr->unique, -errno);
+        return;
+    }
+
+    lookup_entry(fuse, handler, hdr->unique, node, name);
+}
+
+static void handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
+{
+    struct fuse_entry_out out;
+    char *path, buffer[PATH_MAX];
+    int res;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    TRACE("MKDIR %s 0%o @ %llx (%s)\n", name, req->mode, hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    path = node_get_path(node, buffer, name);
+
+    __u32 mode = (req->mode & (~0777)) | 0775;
+    res = mkdir(path, mode);
+    if (res < 0) {
+        fuse_status(fuse, hdr->unique, -errno);
+        return;
+    }
+
+    lookup_entry(fuse, handler, hdr->unique, node, name);
+}
+
+static void handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const char* name)
+{
+    char *path, buffer[PATH_MAX];
+    int res;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    TRACE("UNLINK %s @ %llx (%s)\n", name, hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    path = node_get_path(node, buffer, name);
+    res = unlink(path);
+    fuse_status(fuse, hdr->unique, res ? -errno : 0);
+}
+
+static void handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const char* name)
+{
+    char *path, buffer[PATH_MAX];
+    int res;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    TRACE("RMDIR %s @ %llx (%s)\n", name, hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    path = node_get_path(node, buffer, name);
+    res = rmdir(path);
+    fuse_status(fuse, hdr->unique, res ? -errno : 0);
+}
+
+static void handle_rename(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
+        const char* oldname, const char* newname)
+{
+    char *oldpath, oldbuffer[PATH_MAX];
+    char *newpath, newbuffer[PATH_MAX];
+    struct node *target;
+    struct node *newparent;
+    int res;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    TRACE("RENAME %s->%s @ %llx (%s)\n", oldname, newname, hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    target = lookup_child_by_name(node, oldname);
+    if (!target) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+    oldpath = node_get_path(node, oldbuffer, oldname);
+
+    newparent = lookup_by_inode(fuse, req->newdir);
+    if (!newparent) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+    if (newparent == node) {
+        /* Special case for renaming a file where destination
+         * is same path differing only by case.
+         * In this case we don't want to look for a case insensitive match.
+         * This allows commands like "mv foo FOO" to work as expected.
+         */
+        newpath = do_node_get_path(newparent, newbuffer, newname, NO_CASE_SENSITIVE_MATCH);
+    } else {
+        newpath = node_get_path(newparent, newbuffer, newname);
+    }
+
+    if (!remove_child(node, target->nid)) {
+        ERROR("RENAME remove_child not found");
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+    if (!rename_node(target, newname)) {
+        fuse_status(fuse, hdr->unique, -ENOMEM);
+        return;
+    }
+    add_node_to_parent(target, newparent);
+
+    res = rename(oldpath, newpath);
+    TRACE("RENAME result %d\n", res);
+
+    fuse_status(fuse, hdr->unique, res ? -errno : 0);
+}
+
+static void handle_open(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_open_in* req)
+{
+    struct fuse_open_out out;
+    char *path, buffer[PATH_MAX];
+    struct handle *h;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    h = malloc(sizeof(*h));
+    if (!h) {
+        fuse_status(fuse, hdr->unique, -ENOMEM);
+        return;
+    }
+
+    TRACE("OPEN 0%o fh=%p @ %llx (%s)\n", req->flags, h, hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    path = node_get_path(node, buffer, 0);
+    h->fd = open(path, req->flags);
+    if (h->fd < 0) {
+        ERROR("ERROR\n");
+        fuse_status(fuse, hdr->unique, -errno);
+        free(h);
+        return;
+    }
+    out.fh = ptr_to_id(h);
+    out.open_flags = 0;
+    out.padding = 0;
+    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+}
+
+static void handle_read(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_read_in* req)
+{
+    struct handle *h = id_to_ptr(req->fh);
+    __u64 unique = hdr->unique;
+    __u32 size = req->size;
+    __u64 offset = req->offset;
+    /* Don't access any other fields of hdr or req beyond this point, the read buffer
+     * overlaps the request buffer and will clobber data in the request.  This
+     * saves us 128KB per request handler thread at the cost of this scary comment. */
+    int res;
+    TRACE("READ %p(%d) %u@%llu\n", h, h->fd, size, offset);
+    if (size > sizeof(handler->read_buffer)) {
+        fuse_status(fuse, unique, -EINVAL);
+        return;
+    }
+    res = pread64(h->fd, handler->read_buffer, size, offset);
+    if (res < 0) {
+        fuse_status(fuse, unique, -errno);
+        return;
+    }
+    fuse_reply(fuse, unique, handler->read_buffer, res);
+}
+
+static void handle_write(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_write_in* req,
+        const void* buffer)
+{
+    struct fuse_write_out out;
+    struct handle *h = id_to_ptr(req->fh);
+    int res;
+    TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
+    res = pwrite64(h->fd, buffer, req->size, req->offset);
+    if (res < 0) {
+        fuse_status(fuse, hdr->unique, -errno);
+        return;
+    }
+    out.size = res;
+    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+}
+
+static void handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr)
+{
+    struct statfs stat;
+    struct fuse_statfs_out out;
+    int res;
+
+    TRACE("STATFS\n");
+
+    if (statfs(fuse->root.name, &stat)) {
+        fuse_status(fuse, hdr->unique, -errno);
+        return;
+    }
+
+    memset(&out, 0, sizeof(out));
+    out.st.blocks = stat.f_blocks;
+    out.st.bfree = stat.f_bfree;
+    out.st.bavail = stat.f_bavail;
+    out.st.files = stat.f_files;
+    out.st.ffree = stat.f_ffree;
+    out.st.bsize = stat.f_bsize;
+    out.st.namelen = stat.f_namelen;
+    out.st.frsize = stat.f_frsize;
+    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+}
+
+static void handle_release(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_release_in* req)
+{
+    struct handle *h = id_to_ptr(req->fh);
+    TRACE("RELEASE %p(%d)\n", h, h->fd);
+    close(h->fd);
+    free(h);
+    fuse_status(fuse, hdr->unique, 0);
+}
+
+static void handle_fsync(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_fsync_in* req)
+{
+    int is_data_sync = req->fsync_flags & 1;
+    struct handle *h = id_to_ptr(req->fh);
+    int res;
+    TRACE("FSYNC %p(%d) is_data_sync=%d\n", h, h->fd, is_data_sync);
+    res = is_data_sync ? fdatasync(h->fd) : fsync(h->fd);
+    if (res < 0) {
+        fuse_status(fuse, hdr->unique, -errno);
+        return;
+    }
+    fuse_status(fuse, hdr->unique, 0);
+}
+
+static void handle_flush(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr)
+{
+    fuse_status(fuse, hdr->unique, 0);
+}
+
+static void handle_opendir(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_open_in* req)
+{
+    struct fuse_open_out out;
+    char *path, buffer[PATH_MAX];
+    struct dirhandle *h;
+    struct node* node = lookup_by_inode(fuse, hdr->nodeid);
+
+    TRACE("OPENDIR @ %llx (%s)\n", hdr->nodeid, node ? node->name : "?");
+    if (!node) {
+        fuse_status(fuse, hdr->unique, -ENOENT);
+        return;
+    }
+
+    h = malloc(sizeof(*h));
+    if (!h) {
+        fuse_status(fuse, hdr->unique, -ENOMEM);
+        return;
+    }
+
+    path = node_get_path(node, buffer, 0);
+    h->d = opendir(path);
+    if (h->d == 0) {
+        ERROR("ERROR\n");
+        fuse_status(fuse, hdr->unique, -errno);
+        free(h);
+        return;
+    }
+    out.fh = ptr_to_id(h);
+    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+}
+
+static void handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_read_in* req)
+{
+    char buffer[8192];
+    struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
+    struct dirent *de;
+    struct dirhandle *h = id_to_ptr(req->fh);
+    TRACE("READDIR %p\n", h);
+    if (req->offset == 0) {
+        /* rewinddir() might have been called above us, so rewind here too */
+        TRACE("calling rewinddir()\n");
+        rewinddir(h->d);
+    }
+    de = readdir(h->d);
+    if (!de) {
+        fuse_status(fuse, hdr->unique, 0);
+        return;
+    }
+    fde->ino = FUSE_UNKNOWN_INO;
+    /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
+    fde->off = req->offset + 1;
+    fde->type = de->d_type;
+    fde->namelen = strlen(de->d_name);
+    memcpy(fde->name, de->d_name, fde->namelen + 1);
+    fuse_reply(fuse, hdr->unique, fde,
+               FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
+}
+
+static void handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_release_in* req)
+{
+    struct dirhandle *h = id_to_ptr(req->fh);
+    TRACE("RELEASEDIR %p\n",h);
+    closedir(h->d);
+    free(h);
+    fuse_status(fuse, hdr->unique, 0);
+}
+
+static void handle_init(struct fuse* fuse, struct fuse_handler* handler,
+        const struct fuse_in_header* hdr, const struct fuse_init_in* req)
+{
+    struct fuse_init_out out;
+
+    TRACE("INIT ver=%d.%d maxread=%d flags=%x\n",
+            req->major, req->minor, req->max_readahead, req->flags);
+
+    out.major = FUSE_KERNEL_VERSION;
+    out.minor = FUSE_KERNEL_MINOR_VERSION;
+    out.max_readahead = req->max_readahead;
+    out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+    out.max_background = 32;
+    out.congestion_threshold = 32;
+    out.max_write = MAX_WRITE;
+
+    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+}
+
+static void handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
+        const struct fuse_in_header *hdr, const void *data, size_t data_len)
+{
     switch (hdr->opcode) {
     case FUSE_LOOKUP: { /* bytez[] -> entry_out */
         const char* name = data;
-        TRACE("LOOKUP %llx %s\n", hdr->nodeid, name);
-        lookup_entry(fuse, node, name, hdr->unique);
+        handle_lookup(fuse, handler, hdr, name);
         return;
     }
 
     case FUSE_FORGET: {
         const struct fuse_forget_in *req = data;
-        __u64 n = req->nlookup;
-        TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, n);
-            /* no reply */
-        while (n--)
-            node_release(node);
+        handle_forget(fuse, handler, hdr, req);
         return;
     }
 
     case FUSE_GETATTR: { /* getattr_in -> attr_out */
         const struct fuse_getattr_in *req = data;
-        struct fuse_attr_out out;
-
-        TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh);
-
-        memset(&out, 0, sizeof(out));
-        node_get_attr(node, &out.attr);
-        out.attr_valid = 10;
-
-        fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+        handle_getaddr(fuse, handler, hdr, req);
         return;
     }
 
     case FUSE_SETATTR: { /* setattr_in -> attr_out */
         const struct fuse_setattr_in *req = data;
-        struct fuse_attr_out out;
-        char *path, buffer[PATH_MAX];
-        int res = 0;
-        struct timespec times[2];
-
-        TRACE("SETATTR fh=%llx id=%llx valid=%x\n",
-              req->fh, hdr->nodeid, req->valid);
-
-        /* XXX: incomplete implementation on purpose.   chmod/chown
-         * should NEVER be implemented.*/
-
-        path = node_get_path(node, buffer, 0);
-        if (req->valid & FATTR_SIZE)
-            res = truncate(path, req->size);
-        if (res)
-            goto getout;
-
-        /* Handle changing atime and mtime.  If FATTR_ATIME_and FATTR_ATIME_NOW
-         * are both set, then set it to the current time.  Else, set it to the
-         * time specified in the request.  Same goes for mtime.  Use utimensat(2)
-         * as it allows ATIME and MTIME to be changed independently, and has
-         * nanosecond resolution which fuse also has.
-         */
-        if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
-            times[0].tv_nsec = UTIME_OMIT;
-            times[1].tv_nsec = UTIME_OMIT;
-            if (req->valid & FATTR_ATIME) {
-                if (req->valid & FATTR_ATIME_NOW) {
-                  times[0].tv_nsec = UTIME_NOW;
-                } else {
-                  times[0].tv_sec = req->atime;
-                  times[0].tv_nsec = req->atimensec;
-                }
-            }
-            if (req->valid & FATTR_MTIME) {
-                if (req->valid & FATTR_MTIME_NOW) {
-                  times[1].tv_nsec = UTIME_NOW;
-                } else {
-                  times[1].tv_sec = req->mtime;
-                  times[1].tv_nsec = req->mtimensec;
-                }
-            }
-            TRACE("Calling utimensat on %s with atime %ld, mtime=%ld\n", path, times[0].tv_sec, times[1].tv_sec);
-            res = utimensat(-1, path, times, 0);
-        }
-
-        getout:
-        memset(&out, 0, sizeof(out));
-        node_get_attr(node, &out.attr);
-        out.attr_valid = 10;
-
-        if (res)
-            fuse_status(fuse, hdr->unique, -errno);
-        else
-            fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+        handle_setattr(fuse, handler, hdr, req);
         return;
     }
 
@@ -652,61 +1058,26 @@
     case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
         const struct fuse_mknod_in *req = data;
         const char *name = ((const char*) data) + sizeof(*req);
-        char *path, buffer[PATH_MAX];
-        int res;
-
-        TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid);
-        path = node_get_path(node, buffer, name);
-
-        __u32 mode = (req->mode & (~0777)) | 0664;
-        res = mknod(path, mode, req->rdev); /* XXX perm?*/
-        if (res < 0) {
-            fuse_status(fuse, hdr->unique, -errno);
-        } else {
-            lookup_entry(fuse, node, name, hdr->unique);
-        }
+        handle_mknod(fuse, handler, hdr, req, name);
         return;
     }
 
     case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
         const struct fuse_mkdir_in *req = data;
         const char *name = ((const char*) data) + sizeof(*req);
-        struct fuse_entry_out out;
-        char *path, buffer[PATH_MAX];
-        int res;
-
-        TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode);
-        path = node_get_path(node, buffer, name);
-
-        __u32 mode = (req->mode & (~0777)) | 0775;
-        res = mkdir(path, mode);
-        if (res < 0) {
-            fuse_status(fuse, hdr->unique, -errno);
-        } else {
-            lookup_entry(fuse, node, name, hdr->unique);
-        }
+        handle_mkdir(fuse, handler, hdr, req, name);
         return;
     }
 
     case FUSE_UNLINK: { /* bytez[] -> */
         const char* name = data;
-        char *path, buffer[PATH_MAX];
-        int res;
-        TRACE("UNLINK %s @ %llx\n", name, hdr->nodeid);
-        path = node_get_path(node, buffer, name);
-        res = unlink(path);
-        fuse_status(fuse, hdr->unique, res ? -errno : 0);
+        handle_unlink(fuse, handler, hdr, name);
         return;
     }
 
     case FUSE_RMDIR: { /* bytez[] -> */
         const char* name = data;
-        char *path, buffer[PATH_MAX];
-        int res;
-        TRACE("RMDIR %s @ %llx\n", name, hdr->nodeid);
-        path = node_get_path(node, buffer, name);
-        res = rmdir(path);
-        fuse_status(fuse, hdr->unique, res ? -errno : 0);
+        handle_rmdir(fuse, handler, hdr, name);
         return;
     }
 
@@ -714,172 +1085,44 @@
         const struct fuse_rename_in *req = data;
         const char *oldname = ((const char*) data) + sizeof(*req);
         const char *newname = oldname + strlen(oldname) + 1;
-        char *oldpath, oldbuffer[PATH_MAX];
-        char *newpath, newbuffer[PATH_MAX];
-        struct node *target;
-        struct node *newparent;
-        int res;
-
-        TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid);
-
-        target = lookup_child_by_name(node, oldname);
-        if (!target) {
-            fuse_status(fuse, hdr->unique, -ENOENT);
-            return;
-        }
-        oldpath = node_get_path(node, oldbuffer, oldname);
-
-        newparent = lookup_by_inode(fuse, req->newdir);
-        if (!newparent) {
-            fuse_status(fuse, hdr->unique, -ENOENT);
-            return;
-        }
-        if (newparent == node) {
-            /* Special case for renaming a file where destination
-             * is same path differing only by case.
-             * In this case we don't want to look for a case insensitive match.
-             * This allows commands like "mv foo FOO" to work as expected.
-             */
-            newpath = do_node_get_path(newparent, newbuffer, newname, NO_CASE_SENSITIVE_MATCH);
-        } else {
-            newpath = node_get_path(newparent, newbuffer, newname);
-        }
-
-        if (!remove_child(node, target->nid)) {
-            ERROR("RENAME remove_child not found");
-            fuse_status(fuse, hdr->unique, -ENOENT);
-            return;
-        }
-        if (!rename_node(target, newname)) {
-            fuse_status(fuse, hdr->unique, -ENOMEM);
-            return;
-        }
-        add_node_to_parent(target, newparent);
-
-        res = rename(oldpath, newpath);
-        TRACE("RENAME result %d\n", res);
-
-        fuse_status(fuse, hdr->unique, res ? -errno : 0);
+        handle_rename(fuse, handler, hdr, req, oldname, newname);
         return;
     }
 
-//    case FUSE_LINK:        
+//    case FUSE_LINK:
     case FUSE_OPEN: { /* open_in -> open_out */
         const struct fuse_open_in *req = data;
-        struct fuse_open_out out;
-        char *path, buffer[PATH_MAX];
-        struct handle *h;
-
-        h = malloc(sizeof(*h));
-        if (!h) {
-            fuse_status(fuse, hdr->unique, -ENOMEM);
-            return;
-        }
-
-        path = node_get_path(node, buffer, 0);
-        TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h);
-        h->fd = open(path, req->flags);
-        if (h->fd < 0) {
-            ERROR("ERROR\n");
-            fuse_status(fuse, hdr->unique, -errno);
-            free(h);
-            return;
-        }
-        out.fh = ptr_to_id(h);
-        out.open_flags = 0;
-        out.padding = 0;
-        fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+        handle_open(fuse, handler, hdr, req);
         return;
     }
 
     case FUSE_READ: { /* read_in -> byte[] */
         const struct fuse_read_in *req = data;
-        struct handle *h = id_to_ptr(req->fh);
-        __u64 unique = hdr->unique;
-        __u32 size = req->size;
-        __u64 offset = req->offset;
-        /* Don't access any other fields of hdr or req beyond this point, the read buffer
-         * overlaps the request buffer and will clobber data in the request.  This
-         * saves us 128KB per request handler thread at the cost of this scary comment. */
-        int res;
-        TRACE("READ %p(%d) %u@%llu\n", h, h->fd, size, offset);
-        if (size > sizeof(handler->read_buffer)) {
-            fuse_status(fuse, unique, -EINVAL);
-            return;
-        }
-        res = pread64(h->fd, handler->read_buffer, size, offset);
-        if (res < 0) {
-            fuse_status(fuse, unique, -errno);
-            return;
-        }
-        fuse_reply(fuse, unique, handler->read_buffer, res);
+        handle_read(fuse, handler, hdr, req);
         return;
     }
 
     case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
         const struct fuse_write_in *req = data;
         const void* buffer = (const __u8*)data + sizeof(*req);
-        struct fuse_write_out out;
-        struct handle *h = id_to_ptr(req->fh);
-        int res;
-        TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
-        res = pwrite64(h->fd, buffer, req->size, req->offset);
-        if (res < 0) {
-            fuse_status(fuse, hdr->unique, -errno);
-            return;
-        }
-        out.size = res;
-        fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+        handle_write(fuse, handler, hdr, req, buffer);
         return;
     }
 
     case FUSE_STATFS: { /* getattr_in -> attr_out */
-        struct statfs stat;
-        struct fuse_statfs_out out;
-        int res;
-
-        TRACE("STATFS\n");
-
-        if (statfs(fuse->root.name, &stat)) {
-            fuse_status(fuse, hdr->unique, -errno);
-            return;
-        }
-
-        memset(&out, 0, sizeof(out));
-        out.st.blocks = stat.f_blocks;
-        out.st.bfree = stat.f_bfree;
-        out.st.bavail = stat.f_bavail;
-        out.st.files = stat.f_files;
-        out.st.ffree = stat.f_ffree;
-        out.st.bsize = stat.f_bsize;
-        out.st.namelen = stat.f_namelen;
-        out.st.frsize = stat.f_frsize;
-        fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+        handle_statfs(fuse, handler, hdr);
         return;
     }
 
     case FUSE_RELEASE: { /* release_in -> */
         const struct fuse_release_in *req = data;
-        struct handle *h = id_to_ptr(req->fh);
-        TRACE("RELEASE %p(%d)\n", h, h->fd);
-        close(h->fd);
-        free(h);
-        fuse_status(fuse, hdr->unique, 0);
+        handle_release(fuse, handler, hdr, req);
         return;
     }
 
     case FUSE_FSYNC: {
         const struct fuse_fsync_in *req = data;
-        int is_data_sync = req->fsync_flags & 1;
-        struct handle *h = id_to_ptr(req->fh);
-        int res;
-        TRACE("FSYNC %p(%d) is_data_sync=%d\n", h, h->fd, is_data_sync);
-        res = is_data_sync ? fdatasync(h->fd) : fsync(h->fd);
-        if (res < 0) {
-            fuse_status(fuse, hdr->unique, -errno);
-            return;
-        }
-        fuse_status(fuse, hdr->unique, 0);
+        handle_fsync(fuse, handler, hdr, req);
         return;
     }
 
@@ -888,91 +1131,32 @@
 //    case FUSE_LISTXATTR:
 //    case FUSE_REMOVEXATTR:
     case FUSE_FLUSH: {
-        fuse_status(fuse, hdr->unique, 0);
+        handle_flush(fuse, handler, hdr);
         return;
     }
 
     case FUSE_OPENDIR: { /* open_in -> open_out */
         const struct fuse_open_in *req = data;
-        struct fuse_open_out out;
-        char *path, buffer[PATH_MAX];
-        struct dirhandle *h;
-
-        h = malloc(sizeof(*h));
-        if (!h) {
-            fuse_status(fuse, hdr->unique, -ENOMEM);
-            return;
-        }
-
-        path = node_get_path(node, buffer, 0);
-        TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path);
-        h->d = opendir(path);
-        if (h->d == 0) {
-            ERROR("ERROR\n");
-            fuse_status(fuse, hdr->unique, -errno);
-            free(h);
-            return;
-        }
-        out.fh = ptr_to_id(h);
-        fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+        handle_opendir(fuse, handler, hdr, req);
         return;
     }
 
     case FUSE_READDIR: {
         const struct fuse_read_in *req = data;
-        char buffer[8192];
-        struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
-        struct dirent *de;
-        struct dirhandle *h = id_to_ptr(req->fh);
-        TRACE("READDIR %p\n", h);
-        if (req->offset == 0) {
-            /* rewinddir() might have been called above us, so rewind here too */
-            TRACE("calling rewinddir()\n");
-            rewinddir(h->d);
-        }
-        de = readdir(h->d);
-        if (!de) {
-            fuse_status(fuse, hdr->unique, 0);
-            return;
-        }
-        fde->ino = FUSE_UNKNOWN_INO;
-        /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
-        fde->off = req->offset + 1;
-        fde->type = de->d_type;
-        fde->namelen = strlen(de->d_name);
-        memcpy(fde->name, de->d_name, fde->namelen + 1);
-        fuse_reply(fuse, hdr->unique, fde,
-                   FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
+        handle_readdir(fuse, handler, hdr, req);
         return;
     }
 
     case FUSE_RELEASEDIR: { /* release_in -> */
         const struct fuse_release_in *req = data;
-        struct dirhandle *h = id_to_ptr(req->fh);
-        TRACE("RELEASEDIR %p\n",h);
-        closedir(h->d);
-        free(h);
-        fuse_status(fuse, hdr->unique, 0);
+        handle_releasedir(fuse, handler, hdr, req);
         return;
     }
 
 //    case FUSE_FSYNCDIR:
     case FUSE_INIT: { /* init_in -> init_out */
         const struct fuse_init_in *req = data;
-        struct fuse_init_out out;
-        
-        TRACE("INIT ver=%d.%d maxread=%d flags=%x\n",
-                req->major, req->minor, req->max_readahead, req->flags);
-
-        out.major = FUSE_KERNEL_VERSION;
-        out.minor = FUSE_KERNEL_MINOR_VERSION;
-        out.max_readahead = req->max_readahead;
-        out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
-        out.max_background = 32;
-        out.congestion_threshold = 32;
-        out.max_write = MAX_WRITE;
-
-        fuse_reply(fuse, hdr->unique, &out, sizeof(out));
+        handle_init(fuse, handler, hdr, req);
         return;
     }
 
@@ -985,7 +1169,7 @@
     }
 }
 
-void handle_fuse_requests(struct fuse *fuse, struct fuse_handler* handler)
+static void handle_fuse_requests(struct fuse *fuse, struct fuse_handler* handler)
 {
     for (;;) {
         ssize_t len = read(fuse->fd, handler->request_buffer, sizeof(handler->request_buffer));
@@ -1016,7 +1200,7 @@
     }
 }
 
-int ignite_fuse(struct fuse* fuse)
+static int ignite_fuse(struct fuse* fuse)
 {
     /* use only one handler thread for now */
     struct fuse_handler handler;