cleanup
diff --git a/ChangeLog b/ChangeLog
index 4bc79dc..e41d1a9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2005-08-15  Miklos Szeredi <miklos@szeredi.hu>
+
+	* lib: cleaned up (or messed up, depending on your POV) the low
+	level library API.  Hopefully this is close to the final form.
+
 2005-08-05  Miklos Szeredi <miklos@szeredi.hu>
 
 	* fusermount: don't allow empty mountpoint argument, which defeats
diff --git a/example/hello_ll.c b/example/hello_ll.c
index 3a6ea09..f5a2baf 100644
--- a/example/hello_ll.c
+++ b/example/hello_ll.c
@@ -134,7 +134,7 @@
     reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
 }
 
-static struct fuse_ll_operations hello_ll_oper = {
+static struct fuse_lowlevel_ops hello_ll_oper = {
     .lookup     = hello_ll_lookup,
     .getattr	= hello_ll_getattr,
     .readdir    = hello_ll_readdir,
@@ -145,7 +145,6 @@
 int main(int argc, char *argv[])
 {
     const char *mountpoint;
-    struct fuse_ll *f;
     int err = -1;
     int fd;
 
@@ -156,10 +155,17 @@
     mountpoint = argv[1];
     fd = fuse_mount(mountpoint, NULL);
     if (fd != -1) {
-        f = fuse_ll_new(fd, "debug", &hello_ll_oper, sizeof(hello_ll_oper), NULL);
-        if (f != NULL) {
-            err = fuse_ll_loop(f);
-            fuse_ll_destroy(f);
+        struct fuse_session *se;
+
+        se = fuse_lowlevel_new("debug", &hello_ll_oper, sizeof(hello_ll_oper),
+                               NULL);
+        if (se != NULL) {
+            struct fuse_chan *ch = fuse_kern_chan_new(fd);
+            if (ch != NULL) {
+                fuse_session_add_chan(se, ch);
+                err = fuse_session_loop(se);
+            }
+            fuse_session_destroy(se);
         }
         close(fd);
     }
diff --git a/include/Makefile.am b/include/Makefile.am
index ee8a83c..8c33be5 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,6 +1,13 @@
 ## Process this file with automake to produce Makefile.in
 
 fuseincludedir=$(includedir)/fuse
-fuseinclude_HEADERS = fuse.h fuse_compat.h fuse_common.h fuse_lowlevel.h
+
+fuseinclude_HEADERS = \
+	fuse.h		\
+	fuse_compat.h	\
+	fuse_common.h	\
+	fuse_lowlevel.h
+
 include_HEADERS = old/fuse.h
+
 noinst_HEADERS = fuse_kernel.h
diff --git a/include/fuse.h b/include/fuse.h
index ca96084..ca3797f 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -34,6 +34,12 @@
 /** Handle for a FUSE filesystem */
 struct fuse;
 
+/** Structure containing a raw command */
+struct fuse_cmd;
+
+/** The lowlevel FUSE session */
+struct fuse_session;
+
 /** Function to add an entry in a readdir() operation
  *
  * @param buf the buffer passed to the readdir() operation
@@ -504,8 +510,8 @@
 /** Set function which can be used to get the current context */
 void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
 
-/** Returns the lowlevel FUSE object */
-struct fuse_ll *fuse_get_lowlevel(struct fuse *f);
+/** Returns the lowlevel FUSE session */
+struct fuse_session *fuse_get_session(struct fuse *f);
 
 /* ----------------------------------------------------------- *
  * Compatibility stuff                                         *
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 648d6e4..2af3ce9 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -53,7 +53,4 @@
     unsigned int keep_cache : 1;
 };
 
-/** Structure containing a raw command */
-struct fuse_cmd;
-
 #endif /* _FUSE_COMMON_H_ */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index 8b18959..4c1af83 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/statfs.h>
+#include <sys/uio.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -29,7 +30,9 @@
 
 typedef unsigned long fuse_ino_t;
 typedef struct fuse_req *fuse_req_t;
-struct fuse_ll;
+
+struct fuse_session;
+struct fuse_chan;
 
 struct fuse_entry_param {
     fuse_ino_t ino;
@@ -69,8 +72,8 @@
 
 /* ------------------------------------------ */
 
-struct fuse_ll_operations {
-    void* (*init)  (void *);
+struct fuse_lowlevel_ops {
+    void (*init)  (void *);
     void (*destroy)(void *);
 
     void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
@@ -174,29 +177,73 @@
 
 /* ------------------------------------------ */
 
-typedef void (*fuse_ll_processor_t)(struct fuse_ll *, struct fuse_cmd *, void *);
+int fuse_lowlevel_is_lib_option(const char *opt);
 
-struct fuse_ll *fuse_ll_new(int fd, const char *opts,
-                            const struct fuse_ll_operations *op,
-                            size_t op_size, void *userdata);
+struct fuse_session *fuse_lowlevel_new(const char *opts,
+                                       const struct fuse_lowlevel_ops *op,
+                                       size_t op_size, void *userdata);
 
-void fuse_ll_destroy(struct fuse_ll *f);
+struct fuse_chan *fuse_kern_chan_new(int fd);
 
-int fuse_ll_is_lib_option(const char *opt);
+/* ------------------------------------------ */
 
-int fuse_ll_loop(struct fuse_ll *f);
+struct fuse_session_ops {
+    void (*process) (void *data, const char *buf, size_t len,
+                     struct fuse_chan *ch);
+    
+    void (*destroy) (void *data);
+};
 
-void fuse_ll_exit(struct fuse_ll *f);
+struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data);
 
-int fuse_ll_exited(struct fuse_ll* f);
+void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch);
 
-struct fuse_cmd *fuse_ll_read_cmd(struct fuse_ll *f);
+struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
+                                         struct fuse_chan *ch);
 
-void fuse_ll_process_cmd(struct fuse_ll *f, struct fuse_cmd *cmd);
+void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
+                          struct fuse_chan *ch);
 
-int fuse_ll_loop_mt(struct fuse_ll *f);
+void fuse_session_destroy(struct fuse_session *se);
 
-int fuse_ll_loop_mt_proc(struct fuse_ll *f, fuse_ll_processor_t proc, void *data);
+void fuse_session_exit(struct fuse_session *se);
+
+void fuse_session_reset(struct fuse_session *se);
+
+int fuse_session_exited(struct fuse_session *se);
+
+int fuse_session_loop(struct fuse_session *se);
+
+int fuse_session_loop_mt(struct fuse_session *se);
+
+/* ------------------------------------------ */
+
+struct fuse_chan_ops {
+    int (*receive)(struct fuse_chan *ch, char *buf, size_t size);
+
+    int (*send)(struct fuse_chan *ch, const struct iovec iov[],
+                size_t count);
+
+    void (*destroy)(struct fuse_chan *ch);
+};
+
+struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
+                                size_t bufsize, void *data);
+
+int fuse_chan_fd(struct fuse_chan *ch);
+
+size_t fuse_chan_bufsize(struct fuse_chan *ch);
+
+void *fuse_chan_data(struct fuse_chan *ch);
+
+struct fuse_session *fuse_chan_session(struct fuse_chan *ch);
+
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
+
+int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[],
+                   size_t count);
+
+void fuse_chan_destroy(struct fuse_chan *ch);
 
 /* ------------------------------------------ */
 
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b99f9e2..b92a0ad 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,12 +4,14 @@
 
 libfuse_la_SOURCES = 		\
 	fuse.c			\
-	fuse_mt.c		\
+	fuse_kern_chan.c	\
+	fuse_loop.c		\
+	fuse_loop_mt.c		\
 	fuse_lowlevel.c		\
-	fuse_lowlevel_mt.c	\
+	fuse_mt.c		\
+	fuse_session.c		\
 	helper.c		\
-	mount.c			\
-	fuse_lowlevel_i.h
+	mount.c
 
 libfuse_la_LDFLAGS = -lpthread -version-number 2:4:0 \
 	-Wl,--version-script,fuse_versionscript
diff --git a/lib/fuse.c b/lib/fuse.c
index 68eeee0..7613d38 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -63,7 +63,7 @@
 #define FUSE_MAX_PATH 4096
 
 struct fuse {
-    struct fuse_ll *fll;
+    struct fuse_session *se;
     int flags;
     struct fuse_operations op;
     int compat;
@@ -110,6 +110,11 @@
     fuse_ino_t nodeid;
 };
 
+struct fuse_cmd {
+    char *buf;
+    size_t buflen;
+};
+
 static struct fuse_context *(*fuse_getcontext)(void) = NULL;
 
 #ifndef USE_UCLIBC
@@ -561,14 +566,12 @@
         reply_err(req, err);
 }
 
-static void *fuse_data_init(void *data)
+static void fuse_data_init(void *data)
 {
     struct fuse *f = (struct fuse *) data;
 
     if (f->op.init)
         f->user_data = f->op.init();
-
-    return f;
 }
 
 static void fuse_data_destroy(void *data)
@@ -1599,7 +1602,7 @@
     reply_err(req, err);
 }
 
-static struct fuse_ll_operations fuse_path_ops = {
+static struct fuse_lowlevel_ops fuse_path_ops = {
     .init = fuse_data_init,
     .destroy = fuse_data_destroy,
     .lookup = fuse_lookup,
@@ -1632,25 +1635,64 @@
     .removexattr = fuse_removexattr,
 };
 
+static void free_cmd(struct fuse_cmd *cmd)
+{
+    free(cmd->buf);
+    free(cmd);
+}
+
 void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
 {
-    fuse_ll_process_cmd(f->fll, cmd);
+    struct fuse_chan *ch = fuse_session_next_chan(f->se, NULL);
+    fuse_session_process(f->se, cmd->buf, cmd->buflen, ch);
 }
 
 int fuse_exited(struct fuse *f)
 {
-    return fuse_ll_exited(f->fll);
+    return fuse_session_exited(f->se);
+}
+
+struct fuse_session *fuse_get_session(struct fuse *f)
+{
+    return f->se;
+}
+
+static struct fuse_cmd *fuse_alloc_cmd(size_t bufsize)
+{
+    struct fuse_cmd *cmd = (struct fuse_cmd *) malloc(sizeof(*cmd));
+    if (cmd == NULL) {
+        fprintf(stderr, "fuse: failed to allocate cmd\n");
+        return NULL;
+    }
+    cmd->buf = (char *) malloc(bufsize);
+    if (cmd->buf == NULL) {
+        fprintf(stderr, "fuse: failed to allocate read buffer\n");
+        free(cmd);
+        return NULL;
+    }
+    return cmd;
 }
 
 struct fuse_cmd *fuse_read_cmd(struct fuse *f)
 {
-    return fuse_ll_read_cmd(f->fll);
+    struct fuse_chan *ch = fuse_session_next_chan(f->se, NULL);
+    size_t bufsize = fuse_chan_bufsize(ch);
+    struct fuse_cmd *cmd = fuse_alloc_cmd(bufsize);
+    if (cmd != NULL) {
+        int res = fuse_chan_receive(ch, cmd->buf, bufsize);
+        if (res <= 0) {
+            free_cmd(cmd);
+            return NULL;
+        }
+        cmd->buflen = res;
+    }
+    return cmd;
 }
 
 int fuse_loop(struct fuse *f)
 {
     if (f)
-        return fuse_ll_loop(f->fll);
+        return fuse_session_loop(f->se);
     else
         return -1;
 }
@@ -1664,7 +1706,7 @@
 
 void fuse_exit(struct fuse *f)
 {
-    fuse_ll_exit(f->fll);
+    fuse_session_exit(f->se);
 }
 
 struct fuse_context *fuse_get_context()
@@ -1681,11 +1723,6 @@
     fuse_getcontext = func;
 }
 
-struct fuse_ll *fuse_get_lowlevel(struct fuse *f)
-{
-    return f->fll;
-}
-
 static int begins_with(const char *s, const char *beg)
 {
     if (strncmp(s, beg, strlen(beg)) == 0)
@@ -1696,7 +1733,7 @@
 
 int fuse_is_lib_option(const char *opt)
 {
-    if (fuse_ll_is_lib_option(opt) ||
+    if (fuse_lowlevel_is_lib_option(opt) ||
         strcmp(opt, "debug") == 0 ||
         strcmp(opt, "hard_remove") == 0 ||
         strcmp(opt, "use_ino") == 0 ||
@@ -1728,7 +1765,7 @@
         }
 
         while((opt = strsep(&s, ","))) {
-            if (fuse_ll_is_lib_option(opt)) {
+            if (fuse_lowlevel_is_lib_option(opt)) {
                 size_t optlen = strlen(opt);
                 if (strcmp(opt, "debug") == 0)
                     f->flags |= FUSE_DEBUG;
@@ -1772,6 +1809,7 @@
                              const struct fuse_operations *op,
                              size_t op_size, int compat)
 {
+    struct fuse_chan *ch;
     struct fuse *f;
     struct node *root;
     char *llopts = NULL;
@@ -1793,10 +1831,16 @@
     if (parse_lib_opts(f, opts, &llopts) == -1)
         goto out_free;
 
-    f->fll = fuse_ll_new(fd, llopts, &fuse_path_ops, sizeof(fuse_path_ops), f);
+    f->se = fuse_lowlevel_new(llopts, &fuse_path_ops, sizeof(fuse_path_ops), f);
     free(llopts);
-    if (f->fll == NULL)
+    if (f->se == NULL)
         goto out_free;
+    
+    ch = fuse_kern_chan_new(fd);
+    if (ch == NULL)
+        goto out_free_session;
+    
+    fuse_session_add_chan(f->se, ch);
 
     f->ctr = 0;
     f->generation = 0;
@@ -1806,7 +1850,7 @@
         calloc(1, sizeof(struct node *) * f->name_table_size);
     if (f->name_table == NULL) {
         fprintf(stderr, "fuse: memory allocation failed\n");
-        goto out_free;
+        goto out_free_session;
     }
 
     f->id_table_size = 14057;
@@ -1848,6 +1892,8 @@
     free(f->id_table);
  out_free_name_table:
     free(f->name_table);
+ out_free_session:
+    fuse_session_destroy(f->se);
  out_free:
     free(f);
  out:
@@ -1905,7 +1951,7 @@
     free(f->id_table);
     free(f->name_table);
     pthread_mutex_destroy(&f->lock);
-    fuse_ll_destroy(f->fll);
+    fuse_session_destroy(f->se);
     free(f);
 }
 
diff --git a/lib/fuse_kern_chan.c b/lib/fuse_kern_chan.c
new file mode 100644
index 0000000..577d2e5
--- /dev/null
+++ b/lib/fuse_kern_chan.c
@@ -0,0 +1,73 @@
+/*
+    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_lowlevel.h"
+#include "fuse_kernel.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+static int fuse_kern_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
+{
+    ssize_t res = read(fuse_chan_fd(ch), buf, size);
+    struct fuse_session *se = fuse_chan_session(ch);
+
+    assert(se != NULL);
+    if (fuse_session_exited(se))
+        return 0;
+    if (res == -1) {
+        /* EINTR means, the read() was interrupted, ENOENT means the
+           operation was interrupted */
+        if (errno == EINTR || errno == ENOENT)
+            return 0;
+        /* ENODEV means we got unmounted, so we silenty return failure */
+        if (errno != ENODEV)
+            perror("fuse: reading device");
+        return -1;
+    }
+    if ((size_t) res < sizeof(struct fuse_in_header)) {
+        fprintf(stderr, "short read on fuse device\n");
+        return -1;
+    }
+    return res;
+}
+
+static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
+                               size_t count)
+{
+    ssize_t res = writev(fuse_chan_fd(ch), iov, count);
+
+    if (res == -1) {
+        struct fuse_session *se = fuse_chan_session(ch);
+
+        assert(se != NULL);
+
+        /* ENOENT means the operation was interrupted */
+        if (!fuse_session_exited(se) && errno != ENOENT)
+            perror("fuse: writing device");
+        return -errno;
+    }
+    return 0;
+}
+
+static void fuse_kern_chan_destroy(struct fuse_chan *ch)
+{
+    close(fuse_chan_fd(ch));
+}
+
+struct fuse_chan *fuse_kern_chan_new(int fd)
+{
+    struct fuse_chan_ops op = {
+        .receive = fuse_kern_chan_receive,
+        .send = fuse_kern_chan_send,
+        .destroy = fuse_kern_chan_destroy,
+    };
+    return fuse_chan_new(&op, fd, FUSE_MAX_IN, NULL);
+}
diff --git a/lib/fuse_loop.c b/lib/fuse_loop.c
new file mode 100644
index 0000000..f9a05d8
--- /dev/null
+++ b/lib/fuse_loop.c
@@ -0,0 +1,38 @@
+/*
+    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_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int fuse_session_loop(struct fuse_session *se)
+{
+    int res = 0;
+    struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+    size_t bufsize = fuse_chan_bufsize(ch);
+    char *buf = (char *) malloc(bufsize);
+    if (!buf) {
+        fprintf(stderr, "fuse: failed to allocate read buffer\n");
+        return -1;
+    }
+
+    while (!fuse_session_exited(se)) {
+        res = fuse_chan_receive(ch, buf, bufsize);
+        if (!res)
+            continue;
+        if (res == -1)
+            break;
+        fuse_session_process(se, buf, res, ch);
+        res = 0;
+    }
+
+    free(buf);
+    fuse_session_reset(se);
+    return res;
+}
diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c
new file mode 100644
index 0000000..ad386a2
--- /dev/null
+++ b/lib/fuse_loop_mt.c
@@ -0,0 +1,161 @@
+/*
+    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_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#define FUSE_MAX_WORKERS 10
+
+struct fuse_worker {
+    pthread_mutex_t lock;
+    int numworker;
+    int numavail;
+    struct fuse_session *se;
+    struct fuse_chan *ch;
+    struct fuse_chan *prevch;
+    pthread_t threads[FUSE_MAX_WORKERS];
+    int error;
+};
+
+#ifndef USE_UCLIBC
+#define mutex_init(mut) pthread_mutex_init(mut, NULL)
+#else
+static void mutex_init(pthread_mutex_t mut)
+{
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+    pthread_mutex_init(mut, &attr);
+    pthread_mutexattr_destroy(&attr);
+}
+#endif
+
+static int fuse_loop_mt_send(struct fuse_chan *ch, const struct iovec iov[],
+                             size_t count)
+{
+    struct fuse_worker *w = (struct fuse_worker *) fuse_chan_data(ch);
+    pthread_mutex_lock(&w->lock);
+    w->numavail ++;
+    pthread_mutex_unlock(&w->lock);
+    return fuse_chan_send(w->prevch, iov, count);
+}
+
+
+static int start_thread(struct fuse_worker *w, pthread_t *thread_id);
+
+static void *do_work(void *data)
+{
+    struct fuse_worker *w = (struct fuse_worker *) data;
+    int is_mainthread = (w->numworker == 1);
+    size_t bufsize = fuse_chan_bufsize(w->prevch);
+    char *buf = (char *) malloc(bufsize);
+    if (!buf) {
+        fprintf(stderr, "fuse: failed to allocate read buffer\n");
+        fuse_session_exit(w->se);
+        w->error = -1;
+        return NULL;
+    }
+
+    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+    while (!fuse_session_exited(w->se)) {
+        int res = fuse_chan_receive(w->prevch, buf, bufsize);
+        if (!res)
+            continue;
+        if (res == -1) {
+            fuse_session_exit(w->se);
+            w->error = -1;
+        }
+
+        pthread_mutex_lock(&w->lock);
+        w->numavail--;
+        if (w->numavail == 0 && w->numworker < FUSE_MAX_WORKERS) {
+            if (w->numworker < FUSE_MAX_WORKERS) {
+                /* FIXME: threads should be stored in a list instead
+                   of an array */
+                int start_res;
+                pthread_t *thread_id = &w->threads[w->numworker];
+                w->numavail ++;
+                w->numworker ++;
+                start_res = start_thread(w, thread_id);
+                if (start_res == -1)
+                    w->numavail --;
+            }
+        }
+        pthread_mutex_unlock(&w->lock);
+        fuse_session_process(w->se, buf, res, w->ch);
+    }
+
+    /* Wait for cancellation */
+    if (!is_mainthread)
+        pause();
+
+    return NULL;
+}
+
+static int start_thread(struct fuse_worker *w, pthread_t *thread_id)
+{
+    int res = pthread_create(thread_id, NULL, do_work, w);
+    if (res != 0) {
+        fprintf(stderr, "fuse: error creating thread: %s\n", strerror(res));
+        return -1;
+    }
+
+    pthread_detach(*thread_id);
+    return 0;
+}
+
+int fuse_session_loop_mt(struct fuse_session *se)
+{
+    int i;
+    int err;
+    struct fuse_worker *w;
+    struct fuse_chan_ops cop = {
+        .send = fuse_loop_mt_send,
+    };
+
+    w = (struct fuse_worker *) malloc(sizeof(struct fuse_worker));
+    if (w == NULL) {
+        fprintf(stderr, "fuse: failed to allocate worker structure\n");
+        return -1;
+    }
+    memset(w, 0, sizeof(struct fuse_worker));
+    w->se = se;
+    w->prevch = fuse_session_next_chan(se, NULL);
+    w->ch = fuse_chan_new(&cop, -1, 0, w);
+    if (w->ch == NULL) {
+        free(w);
+        return -1;
+    }
+    w->error = 0;
+    w->numworker = 1;
+    w->numavail = 1;
+    mutex_init(&w->lock);
+
+    do_work(w);
+
+    pthread_mutex_lock(&w->lock);
+    for (i = 1; i < w->numworker; i++)
+        pthread_cancel(w->threads[i]);
+    pthread_mutex_unlock(&w->lock);
+    pthread_mutex_destroy(&w->lock);
+    err = w->error;
+    fuse_chan_destroy(w->ch);
+    free(w);
+    fuse_session_reset(se);
+    return err;
+}
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index fce1d6d..bcd48c5 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -7,7 +7,7 @@
 */
 
 #include <config.h>
-#include "fuse_lowlevel_i.h"
+#include "fuse_lowlevel.h"
 #include "fuse_kernel.h"
 
 #include <stdio.h>
@@ -16,37 +16,28 @@
 #include <unistd.h>
 #include <limits.h>
 #include <errno.h>
-#include <assert.h>
 #include <stdint.h>
-#include <sys/param.h>
-#include <sys/uio.h>
 
 #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
 
-struct fuse_cmd {
-    char *buf;
-    size_t buflen;
+struct fuse_ll {
+    unsigned int debug : 1;
+    unsigned int allow_root : 1;
+    struct fuse_lowlevel_ops op;
+    int got_init;
+    void *userdata;
+    int major;
+    int minor;
+    uid_t owner;
 };
 
 struct fuse_req {
     struct fuse_ll *f;
     uint64_t unique;
     struct fuse_ctx ctx;
+    struct fuse_chan *ch;
 };
 
-#ifndef USE_UCLIBC
-#define mutex_init(mut) pthread_mutex_init(mut, NULL)
-#else
-static void mutex_init(pthread_mutex_t *mut)
-{
-    pthread_mutexattr_t attr;
-    pthread_mutexattr_init(&attr);
-    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
-    pthread_mutex_init(mut, &attr);
-    pthread_mutexattr_destroy(&attr);
-}
-#endif
-
 static const char *opname(enum fuse_opcode opcode)
 {
     switch (opcode) {
@@ -86,20 +77,6 @@
     }
 }
 
-static inline void fuse_dec_avail(struct fuse_ll *f)
-{
-    pthread_mutex_lock(&f->worker_lock);
-    f->numavail --;
-    pthread_mutex_unlock(&f->worker_lock);
-}
-
-static inline void fuse_inc_avail(struct fuse_ll *f)
-{
-    pthread_mutex_lock(&f->worker_lock);
-    f->numavail ++;
-    pthread_mutex_unlock(&f->worker_lock);
-}
-
 static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
 {
     attr->ino       = stbuf->st_ino;
@@ -166,48 +143,25 @@
     return ret;
 }
 
-static int send_reply_raw(struct fuse_ll *f, const struct iovec iov[],
-                          size_t count)
+static void free_req(fuse_req_t req)
 {
-    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->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 (!fuse_ll_exited(f) && errno != ENOENT)
-            perror("fuse: writing device");
-        return -errno;
-    }
-    return 0;
+    free(req);
 }
 
-static int send_reply(struct fuse_ll *f, uint64_t unique, int error,
-                      const void *arg, size_t argsize)
+static int send_reply(fuse_req_t req, int error, const void *arg,
+                      size_t argsize)
 {
     struct fuse_out_header out;
     struct iovec iov[2];
     size_t count;
+    int res;
 
     if (error <= -1000 || error > 0) {
         fprintf(stderr, "fuse: bad error value: %i\n",  error);
         error = -ERANGE;
     }
 
-    out.unique = unique;
+    out.unique = req->unique;
     out.error = error;
     count = 1;
     iov[0].iov_base = &out;
@@ -217,7 +171,17 @@
         iov[1].iov_base = (void *) arg;
         iov[1].iov_len = argsize;
     }
-    return send_reply_raw(f, iov, count);
+    out.len = iov_length(iov, count);
+
+    if (req->f->debug) {
+        printf("   unique: %llu, error: %i (%s), outsize: %i\n",
+               out.unique, out.error, strerror(-out.error), out.len);
+        fflush(stdout);
+    }
+    res = fuse_chan_send(req->ch, iov, count);
+    free_req(req);
+
+    return res;
 }
 
 size_t fuse_dirent_size(size_t namelen)
@@ -257,23 +221,14 @@
     kstatfs->namelen	= stbuf->f_namelen;
 }
 
-static void free_req(fuse_req_t req)
+static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
 {
-    free(req);
-}
-
-static int send_reply_req(fuse_req_t req, const void *arg, size_t argsize)
-{
-    int res = send_reply(req->f, req->unique, 0, arg, argsize);
-    free_req(req);
-    return res;
+    return send_reply(req, 0, arg, argsize);
 }
 
 int fuse_reply_err(fuse_req_t req, int err)
 {
-    int res = send_reply(req->f, req->unique, -err, NULL, 0);
-    free_req(req);
-    return res;
+    return send_reply(req, -err, NULL, 0);
 }
 
 int fuse_reply_none(fuse_req_t req)
@@ -316,7 +271,7 @@
     arg.attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
     convert_stat(&e->attr, &arg.attr);
 
-    return send_reply_req(req, &arg, sizeof(arg));
+    return send_reply_ok(req, &arg, sizeof(arg));
 }
 
 int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
@@ -329,12 +284,12 @@
     arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
     convert_stat(attr, &arg.attr);
 
-    return send_reply_req(req, &arg, sizeof(arg));
+    return send_reply_ok(req, &arg, sizeof(arg));
 }
 
 int fuse_reply_readlink(fuse_req_t req, const char *linkname)
 {
-    return send_reply_req(req, linkname, strlen(linkname));
+    return send_reply_ok(req, linkname, strlen(linkname));
 }
 
 int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
@@ -348,7 +303,7 @@
     if (f->keep_cache)
         arg.open_flags |= FOPEN_KEEP_CACHE;
 
-    return send_reply_req(req, &arg, sizeof(arg));
+    return send_reply_ok(req, &arg, sizeof(arg));
 }
 
 int fuse_reply_write(fuse_req_t req, size_t count)
@@ -358,12 +313,12 @@
     memset(&arg, 0, sizeof(arg));
     arg.size = count;
 
-    return send_reply_req(req, &arg, sizeof(arg));
+    return send_reply_ok(req, &arg, sizeof(arg));
 }
 
 int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
 {
-    return send_reply_req(req, buf, size);
+    return send_reply_ok(req, buf, size);
 }
 
 int fuse_reply_statfs(fuse_req_t req, const struct statfs *stbuf)
@@ -373,7 +328,7 @@
     memset(&arg, 0, sizeof(arg));
     convert_statfs(stbuf, &arg.st);
 
-    return send_reply_req(req, &arg, sizeof(arg));
+    return send_reply_ok(req, &arg, sizeof(arg));
 }
 
 int fuse_reply_xattr(fuse_req_t req, size_t count)
@@ -383,7 +338,7 @@
     memset(&arg, 0, sizeof(arg));
     arg.size = count;
 
-    return send_reply_req(req, &arg, sizeof(arg));
+    return send_reply_ok(req, &arg, sizeof(arg));
 }
 
 int fuse_reply_getlk(fuse_req_t req, const struct fuse_lock_param *lk)
@@ -393,7 +348,7 @@
     memset(&arg, 0, sizeof(arg));
     convert_lock_param(lk, &arg.lk);
 
-    return send_reply_req(req, &arg, sizeof(arg));
+    return send_reply_ok(req, &arg, sizeof(arg));
 }
 
 static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, char *name)
@@ -727,10 +682,10 @@
         fuse_reply_err(req, ENOSYS);
 }
 
-static void do_init(struct fuse_ll *f, uint64_t unique,
-                    struct fuse_init_in_out *arg)
+static void do_init(fuse_req_t req, struct fuse_init_in_out *arg)
 {
     struct fuse_init_in_out outarg;
+    struct fuse_ll *f = req->f;
 
     if (f->debug) {
         printf("INIT: %u.%u\n", arg->major, arg->minor);
@@ -738,7 +693,7 @@
     }
     f->got_init = 1;
     if (f->op.init)
-        f->userdata = f->op.init(f->userdata);
+        f->op.init(f->userdata);
 
     f->major = FUSE_KERNEL_VERSION;
     f->minor = FUSE_KERNEL_MINOR_VERSION;
@@ -752,7 +707,7 @@
         fflush(stdout);
     }
 
-    send_reply(f, unique, 0, &outarg, sizeof(outarg));
+    send_reply_ok(req, &outarg, sizeof(outarg));
 }
 
 void *fuse_req_userdata(fuse_req_t req)
@@ -765,48 +720,25 @@
     return &req->ctx;
 }
 
-static void free_cmd(struct fuse_cmd *cmd)
+static void fuse_ll_process(void *data, const char *buf, size_t len,
+                     struct fuse_chan *ch)
 {
-    free(cmd->buf);
-    free(cmd);
-}
-
-void fuse_ll_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(struct fuse_in_header);
+    struct fuse_ll *f = (struct fuse_ll *) data;
+    struct fuse_in_header *in = (struct fuse_in_header *) buf;
+    const void *inarg = buf + sizeof(struct fuse_in_header);
     struct fuse_req *req;
 
-    fuse_dec_avail(f);
-
     if (f->debug) {
         printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n",
                in->unique, opname((enum fuse_opcode) in->opcode), in->opcode,
-               (unsigned long) in->nodeid, cmd->buflen);
+               (unsigned long) in->nodeid, len);
         fflush(stdout);
     }
 
-    if (!f->got_init) {
-        if (in->opcode != FUSE_INIT)
-            send_reply(f, in->unique, -EPROTO, NULL, 0);
-        else
-            do_init(f, in->unique, (struct fuse_init_in_out *) inarg);
-        goto out;
-    }
-
-    if (f->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->unique, -EACCES, NULL, 0);
-        goto out;
-    }
-
     req = (struct fuse_req *) malloc(sizeof(struct fuse_req));
     if (req == NULL) {
         fprintf(stderr, "fuse: failed to allocate request\n");
-        goto out;
+        return;
     }
 
     req->f = f;
@@ -814,8 +746,21 @@
     req->ctx.uid = in->uid;
     req->ctx.gid = in->gid;
     req->ctx.pid = in->pid;
+    req->ch = ch;
 
-    switch (in->opcode) {
+    if (!f->got_init && in->opcode != FUSE_INIT)
+        fuse_reply_err(req, EPROTO);
+    else if (f->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) {
+        fuse_reply_err(req, EACCES);
+    } else switch (in->opcode) {
+    case FUSE_INIT:
+        do_init(req, (struct fuse_init_in_out *) inarg);
+        break;
+        
     case FUSE_LOOKUP:
         do_lookup(req, in->nodeid, (char *) inarg);
         break;
@@ -944,92 +889,9 @@
     default:
         fuse_reply_err(req, ENOSYS);
     }
-
- out:
-    free_cmd(cmd);
 }
 
-void fuse_ll_exit(struct fuse_ll *f)
-{
-    f->exited = 1;
-}
-
-int fuse_ll_exited(struct fuse_ll* f)
-{
-    return f->exited;
-}
-
-struct fuse_cmd *fuse_ll_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(struct fuse_in_header);
-
-    res = read(f->fd, cmd->buf, FUSE_MAX_IN);
-    if (res == -1) {
-        free_cmd(cmd);
-        if (fuse_ll_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_ll_exit(f);
-        return NULL;
-    }
-    if ((size_t) res < sizeof(struct fuse_in_header)) {
-        free_cmd(cmd);
-        /* Cannot happen */
-        fprintf(stderr, "short read on fuse device\n");
-        fuse_ll_exit(f);
-        return NULL;
-    }
-    cmd->buflen = res;
-
-
-    return cmd;
-}
-
-int fuse_ll_loop(struct fuse_ll *f)
-{
-    if (f == NULL)
-        return -1;
-
-    while (1) {
-        struct fuse_cmd *cmd;
-
-        if (fuse_ll_exited(f))
-            break;
-
-        cmd = fuse_ll_read_cmd(f);
-        if (cmd == NULL)
-            continue;
-
-        fuse_ll_process_cmd(f, cmd);
-    }
-    f->exited = 0;
-    return 0;
-}
-
-int fuse_ll_is_lib_option(const char *opt)
+int fuse_lowlevel_is_lib_option(const char *opt)
 {
     if (strcmp(opt, "debug") == 0 ||
         strcmp(opt, "allow_root") == 0)
@@ -1063,15 +925,31 @@
     return 0;
 }
 
-struct fuse_ll *fuse_ll_new(int fd, const char *opts,
-                            const struct fuse_ll_operations *op,
-                            size_t op_size, void *userdata)
+static void fuse_ll_destroy(void *data)
+{
+    struct fuse_ll *f = (struct fuse_ll *) data;
+
+    if (f->op.destroy)
+        f->op.destroy(f->userdata);
+
+    free(f);
+}
+
+
+struct fuse_session *fuse_lowlevel_new(const char *opts, 
+                                       const struct fuse_lowlevel_ops *op,
+                                       size_t op_size, void *userdata)
 {
     struct fuse_ll *f;
+    struct fuse_session *se;
+    struct fuse_session_ops sop = {
+        .process = fuse_ll_process,
+        .destroy = fuse_ll_destroy,
+    };
 
-    if (sizeof(struct fuse_ll_operations) < op_size) {
+    if (sizeof(struct fuse_lowlevel_ops) < op_size) {
         fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
-        op_size = sizeof(struct fuse_ll_operations);
+        op_size = sizeof(struct fuse_lowlevel_ops);
     }
 
     f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
@@ -1083,14 +961,15 @@
     if (parse_ll_opts(f, opts) == -1)
         goto out_free;
 
-    f->fd = fd;
     memcpy(&f->op, op, op_size);
-    f->exited = 0;
     f->owner = getuid();
     f->userdata = userdata;
-    mutex_init(&f->worker_lock);
 
-    return f;
+    se = fuse_session_new(&sop, f);
+    if (!se)
+        goto out_free;
+
+    return se;
 
  out_free:
     free(f);
@@ -1098,12 +977,3 @@
     return NULL;
 }
 
-void fuse_ll_destroy(struct fuse_ll *f)
-{
-    if (f->op.destroy)
-        f->op.destroy(f->userdata);
-
-    pthread_mutex_destroy(&f->worker_lock);
-    free(f);
-}
-
diff --git a/lib/fuse_lowlevel_i.h b/lib/fuse_lowlevel_i.h
deleted file mode 100644
index eb612fc..0000000
--- a/lib/fuse_lowlevel_i.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-    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_lowlevel.h"
-#include <pthread.h>
-
-struct fuse_ll {
-    unsigned int debug : 1;
-    unsigned int allow_root : 1;
-    int fd;
-    struct fuse_ll_operations op;
-    volatile int exited;
-    int got_init;
-    void *userdata;
-    int major;
-    int minor;
-    uid_t owner;
-    pthread_mutex_t worker_lock;
-    int numworker;
-    int numavail;
-};
diff --git a/lib/fuse_lowlevel_mt.c b/lib/fuse_lowlevel_mt.c
deleted file mode 100644
index ee3a0ac..0000000
--- a/lib/fuse_lowlevel_mt.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
-    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_lowlevel_i.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/time.h>
-
-#define FUSE_MAX_WORKERS 10
-
-struct fuse_worker {
-    struct fuse_ll *f;
-    pthread_t threads[FUSE_MAX_WORKERS];
-    void *data;
-    fuse_ll_processor_t proc;
-};
-
-static int start_thread(struct fuse_worker *w, pthread_t *thread_id);
-
-static void *do_work(void *data)
-{
-    struct fuse_worker *w = (struct fuse_worker *) data;
-    struct fuse_ll *f = w->f;
-    int is_mainthread = (f->numworker == 1);
-
-    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
-    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
-
-    while (1) {
-        struct fuse_cmd *cmd;
-
-        if (fuse_ll_exited(f))
-            break;
-
-        cmd = fuse_ll_read_cmd(w->f);
-        if (cmd == NULL)
-            continue;
-
-        if (f->numavail == 0 && f->numworker < FUSE_MAX_WORKERS) {
-            pthread_mutex_lock(&f->worker_lock);
-            if (f->numworker < FUSE_MAX_WORKERS) {
-                /* FIXME: threads should be stored in a list instead
-                   of an array */
-                int res;
-                pthread_t *thread_id = &w->threads[f->numworker];
-                f->numavail ++;
-                f->numworker ++;
-                pthread_mutex_unlock(&f->worker_lock);
-                res = start_thread(w, thread_id);
-                if (res == -1) {
-                    pthread_mutex_lock(&f->worker_lock);
-                    f->numavail --;
-                    pthread_mutex_unlock(&f->worker_lock);
-                }
-            } else
-                pthread_mutex_unlock(&f->worker_lock);
-        }
-
-        w->proc(w->f, cmd, w->data);
-    }
-
-    /* Wait for cancellation */
-    if (!is_mainthread)
-        pause();
-
-    return NULL;
-}
-
-static int start_thread(struct fuse_worker *w, pthread_t *thread_id)
-{
-    int res = pthread_create(thread_id, NULL, do_work, w);
-    if (res != 0) {
-        fprintf(stderr, "fuse: error creating thread: %s\n", strerror(res));
-        return -1;
-    }
-
-    pthread_detach(*thread_id);
-    return 0;
-}
-
-int fuse_ll_loop_mt_proc(struct fuse_ll *f, fuse_ll_processor_t proc, void *data)
-{
-    struct fuse_worker *w;
-    int i;
-
-    w = (struct fuse_worker *) malloc(sizeof(struct fuse_worker));
-    if (w == NULL) {
-        fprintf(stderr, "fuse: failed to allocate worker structure\n");
-        return -1;
-    }
-    memset(w, 0, sizeof(struct fuse_worker));
-    w->f = f;
-    w->data = data;
-    w->proc = proc;
-
-    f->numworker = 1;
-    do_work(w);
-
-    pthread_mutex_lock(&f->worker_lock);
-    for (i = 1; i < f->numworker; i++)
-        pthread_cancel(w->threads[i]);
-    pthread_mutex_unlock(&f->worker_lock);
-    free(w);
-    f->exited = 0;
-    return 0;
-}
-
-int fuse_ll_loop_mt(struct fuse_ll *f)
-{
-    if (f)
-        return fuse_ll_loop_mt_proc(f, (fuse_ll_processor_t) fuse_ll_process_cmd, NULL);
-    else
-        return -1;
-}
-
diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c
index 8359994..c6a6a03 100644
--- a/lib/fuse_mt.c
+++ b/lib/fuse_mt.c
@@ -13,7 +13,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <pthread.h>
-
+#include <assert.h>
 
 static pthread_key_t context_key;
 static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -75,28 +75,73 @@
     void *data;
 };
 
-static void mt_generic_proc(struct fuse_ll *f, struct fuse_cmd *cmd, void *data)
+static void mt_session_proc(void *data, const char *buf, size_t len,
+                            struct fuse_chan *ch)
 {
     struct procdata *pd = (struct procdata *) data;
-    (void) f;
+    struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
+
+    (void) len;
+    (void) ch;
     pd->proc(pd->f, cmd, pd->data);
 }
 
+static int mt_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
+{
+    struct fuse_cmd *cmd;
+    struct procdata *pd = (struct procdata *) fuse_chan_data(ch);
+
+    assert(size >= sizeof(cmd));
+    
+    cmd = fuse_read_cmd(pd->f);
+    if (cmd == NULL)
+        return -1;
+    
+    *(struct fuse_cmd **) buf = cmd;
+    
+    return 0;
+}
+
 int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
 {
     int res;
     struct procdata pd;
+    struct fuse_session *prevse = fuse_get_session(f);
+    struct fuse_session *se;
+    struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
+    struct fuse_chan *ch;
+    struct fuse_session_ops sop = {
+        .process = mt_session_proc,
+    };
+    struct fuse_chan_ops cop = {
+        .receive = mt_chan_receive,
+    };
 
     pd.f = f;
     pd.proc = proc;
     pd.data = data;
 
-    if (mt_create_context_key() != 0)
+    se = fuse_session_new(&sop, &pd);
+    if (se == NULL)
         return -1;
 
-    res = fuse_ll_loop_mt_proc(fuse_get_lowlevel(f), mt_generic_proc, &pd);
+    ch = fuse_chan_new(&cop, fuse_chan_fd(prevch), sizeof(struct fuse_cmd *),
+                       &pd);
+    if (ch == NULL) {
+        fuse_session_destroy(se);
+        return -1;
+    }
+    fuse_session_add_chan(se, ch);
+
+    if (mt_create_context_key() != 0) {
+        fuse_session_destroy(se);
+        return -1;
+    }
+
+    res = fuse_session_loop_mt(se);
 
     mt_delete_context_key();
+    fuse_session_destroy(se);
     return res;
 }
 
@@ -107,7 +152,7 @@
     if (mt_create_context_key() != 0)
         return -1;
 
-    res = fuse_ll_loop_mt(fuse_get_lowlevel(f));
+    res = fuse_session_loop_mt(fuse_get_session(f));
 
     mt_delete_context_key();
     return res;
diff --git a/lib/fuse_session.c b/lib/fuse_session.c
new file mode 100644
index 0000000..a467b2f
--- /dev/null
+++ b/lib/fuse_session.c
@@ -0,0 +1,154 @@
+/*
+    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_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+struct fuse_session {
+    struct fuse_session_ops op;
+
+    void *data;
+    
+    volatile int exited;
+
+    struct fuse_chan *ch;
+};
+
+struct fuse_chan {
+    struct fuse_chan_ops op;
+
+    struct fuse_session *se;
+
+    int fd;
+
+    size_t bufsize;
+    
+    void *data;
+};
+
+struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
+{
+    struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
+    if (se == NULL) {
+        fprintf(stderr, "fuse: failed to allocate session\n");
+        return NULL;
+    }
+
+    memset(se, 0, sizeof(*se));
+    se->op = *op;
+    se->data = data;
+
+    return se;
+}
+
+void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
+{
+    assert(se->ch == NULL);
+    assert(ch->se == NULL);
+    se->ch = ch;
+    ch->se = se;
+}
+
+struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
+                                         struct fuse_chan *ch)
+{
+    assert(ch == NULL || ch == se->ch);
+    if (ch == NULL)
+        return se->ch;
+    else
+        return NULL;
+}
+
+void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
+                          struct fuse_chan *ch)
+{
+    se->op.process(se->data, buf, len, ch);
+}
+
+void fuse_session_destroy(struct fuse_session *se)
+{
+    if (se->op.destroy)
+        se->op.destroy(se->data);
+    if (se->ch != NULL)
+        fuse_chan_destroy(se->ch);
+    free(se);
+}
+
+void fuse_session_exit(struct fuse_session *se)
+{
+    se->exited = 1;
+}
+
+void fuse_session_reset(struct fuse_session *se)
+{
+    se->exited = 0;
+}
+
+int fuse_session_exited(struct fuse_session *se)
+{
+    return se->exited;
+}
+
+struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd, 
+                                size_t bufsize, void *data)
+{
+    struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
+    if (ch == NULL) {
+        fprintf(stderr, "fuse: failed to allocate channel\n");
+        return NULL;
+    }
+
+    memset(ch, 0, sizeof(*ch));
+    ch->op = *op;
+    ch->fd = fd;
+    ch->bufsize = bufsize;
+    ch->data = data;
+
+    return ch;
+}
+
+int fuse_chan_fd(struct fuse_chan *ch)
+{
+    return ch->fd;
+}
+
+size_t fuse_chan_bufsize(struct fuse_chan *ch)
+{
+    return ch->bufsize;
+}
+
+void *fuse_chan_data(struct fuse_chan *ch)
+{
+    return ch->data;
+}
+
+struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
+{
+    return ch->se;
+}
+
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
+{
+    return ch->op.receive(ch, buf, size);
+}
+
+int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
+{
+    return ch->op.send(ch, iov, count);
+}
+
+void fuse_chan_destroy(struct fuse_chan *ch)
+{
+    if (ch->op.destroy)
+        ch->op.destroy(ch);
+    free(ch);
+}
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 283adae..d1b6983 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -1,24 +1,28 @@
 FUSE_2.2 {
 	global:
 		fuse_add_dirent;
+		fuse_chan_bufsize;
+		fuse_chan_data;
+		fuse_chan_destroy;
+		fuse_chan_fd;
+		fuse_chan_new;
+		fuse_chan_receive;
+		fuse_chan_send;
+		fuse_chan_session;
 		fuse_destroy;
 		fuse_dirent_size;
 		fuse_exit;
 		fuse_exited;
 		fuse_get_context;
+		fuse_get_session;
 		fuse_invalidate;
 		fuse_is_lib_option;
-		fuse_ll_new;
-		fuse_ll_destroy;
-		fuse_ll_is_lib_option;
-		fuse_ll_loop;
-		fuse_ll_loop_mt;
-		fuse_ll_exited;
-		fuse_ll_read_cmd;
-		fuse_ll_process_cmd;
+		fuse_kern_chan_new;
 		fuse_loop;
 		fuse_loop_mt;
 		fuse_loop_mt_proc;
+		fuse_lowlevel_is_lib_option;
+		fuse_lowlevel_new;
 		fuse_main;
 		fuse_main_compat1;
 		fuse_main_compat2;
@@ -30,17 +34,29 @@
 		fuse_new_compat2;
 		fuse_process_cmd;
 		fuse_read_cmd;
-		fuse_reply_err;
-		fuse_reply_none;
-		fuse_reply_entry;
 		fuse_reply_attr;
-		fuse_reply_readlink;
-		fuse_reply_open;
-		fuse_reply_write;
 		fuse_reply_buf;
+		fuse_reply_entry;
+		fuse_reply_err;
+		fuse_reply_getlk;
+		fuse_reply_none;
+		fuse_reply_open;
+		fuse_reply_readlink;
 		fuse_reply_statfs;
+		fuse_reply_write;
 		fuse_reply_xattr;
 		fuse_req_ctx;
+		fuse_req_userdata;
+		fuse_session_add_chan;
+		fuse_session_destroy;
+		fuse_session_exit;
+		fuse_session_exited;
+		fuse_session_loop;
+		fuse_session_loop_mt;
+		fuse_session_new;
+		fuse_session_next_chan;
+		fuse_session_process;
+		fuse_session_reset;
 		fuse_set_getcontext_func;
 		fuse_setup;
 		fuse_setup_compat2;