performance improvements
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..df5f428
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,4 @@
+- It is allowed to mount a directory on a non-directory.
+
+- When a non-directory is mounted the root inode is not filled in, only at
+  the first getattr
diff --git a/example/.cvsignore b/example/.cvsignore
index bcfd8b2..94a32e3 100644
--- a/example/.cvsignore
+++ b/example/.cvsignore
@@ -2,3 +2,4 @@
 Makefile
 .deps
 fusexmp
+null
diff --git a/example/Makefile.am b/example/Makefile.am
index 71748f5..19a3c32 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1,7 +1,8 @@
 ## Process this file with automake to produce Makefile.in
 
-noinst_PROGRAMS = fusexmp
+noinst_PROGRAMS = fusexmp null
 
 fusexmp_SOURCES = fusexmp.c
+null_SOURCES = null.c
 
-fusexmp_LDADD = ../lib/libfuse.a -lpthread
+LDADD = ../lib/libfuse.a -lpthread
diff --git a/example/null.c b/example/null.c
new file mode 100644
index 0000000..379ba7a
--- /dev/null
+++ b/example/null.c
@@ -0,0 +1,178 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001  Miklos Szeredi (mszeredi@inf.bme.hu)
+
+    This program can be distributed under the terms of the GNU GPL.
+    See the file COPYING.
+*/
+
+#include <fuse.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+
+#define UNUSED __attribute__((unused))
+
+static char *unmount_cmd;
+
+static int null_getattr(const char *path, struct stat *stbuf)
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+    
+    stbuf->st_mode = S_IFREG | 0644;
+    stbuf->st_nlink = 1;
+    stbuf->st_uid = getuid();
+    stbuf->st_gid = getgid();
+    stbuf->st_size = (1 << 30); /* 1G */
+    stbuf->st_blocks = 0;
+    stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
+
+    return 0;
+}
+
+static int null_truncate(const char *path, off_t UNUSED(size))
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    return 0;
+}
+
+static int null_open(const char *path, int UNUSED(flags))
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    return 0;
+}
+
+static int null_read(const char *path, char *UNUSED(buf), size_t size,
+                     off_t UNUSED(offset))
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    return size;
+}
+
+static int null_write(const char *path, const char *UNUSED(buf), size_t size,
+                     off_t UNUSED(offset))
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    return size;
+}
+
+
+static struct fuse_operations null_oper = {
+    getattr:	null_getattr,
+    readlink:	NULL,
+    getdir:     NULL,
+    mknod:	NULL,
+    mkdir:	NULL,
+    symlink:	NULL,
+    unlink:	NULL,
+    rmdir:	NULL,
+    rename:     NULL,
+    link:	NULL,
+    chmod:	NULL,
+    chown:	NULL,
+    truncate:	null_truncate,
+    utime:	NULL,
+    open:	null_open,
+    read:	null_read,
+    write:	null_write,
+};
+
+
+static void exit_handler()
+{
+    close(0);
+    system(unmount_cmd);    
+    exit(0);
+}
+
+static void set_signal_handlers()
+{
+    struct sigaction sa;
+
+    sa.sa_handler = exit_handler;
+    sigemptyset(&(sa.sa_mask));
+    sa.sa_flags = 0;
+
+    if (sigaction(SIGHUP, &sa, NULL) == -1 || 
+	sigaction(SIGINT, &sa, NULL) == -1 || 
+	sigaction(SIGTERM, &sa, NULL) == -1) {
+	
+	perror("Cannot set exit signal handlers");
+        exit(1);
+    }
+
+    sa.sa_handler = SIG_IGN;
+    
+    if(sigaction(SIGPIPE, &sa, NULL) == -1) {
+	perror("Cannot set ignored signals");
+        exit(1);
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    int argctr;
+    int flags;
+    int multithreaded;
+    struct fuse *fuse;
+
+    if(argc < 2) {
+        fprintf(stderr,
+                "usage: %s unmount_cmd [options] \n"
+                "Options:\n"
+                "    -d      enable debug output\n"
+                "    -s      disable multithreaded operation\n",
+                argv[0]);
+        exit(1);
+    }
+
+    argctr = 1;
+    unmount_cmd = argv[argctr++];
+
+    set_signal_handlers();
+
+    flags = 0;
+    multithreaded = 1;
+    for(; argctr < argc && argv[argctr][0] == '-'; argctr ++) {
+        switch(argv[argctr][1]) {
+        case 'd':
+            flags |= FUSE_DEBUG;
+            break;
+
+        case 's':
+            multithreaded = 0;
+            break;
+
+        default:
+            fprintf(stderr, "invalid option: %s\n", argv[argctr]);
+            exit(1);
+        }
+    }
+    if(argctr != argc) {
+        fprintf(stderr, "missing or surplus argument\n");
+        exit(1);
+    }
+
+    fuse = fuse_new(0, flags);
+    fuse_set_operations(fuse, &null_oper);
+
+    if(multithreaded)
+        fuse_loop_mt(fuse);
+    else
+        fuse_loop(fuse);
+
+    return 0;
+}
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 0cd9e5c..fd5ef9c 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -56,22 +56,22 @@
 #define FATTR_UTIME	(1 << 4)
 
 enum fuse_opcode {
-	FUSE_LOOKUP     = 1,
-	FUSE_FORGET,
-	FUSE_GETATTR,
-	FUSE_SETATTR,
-	FUSE_READLINK,
-	FUSE_SYMLINK,
-	FUSE_GETDIR,
-	FUSE_MKNOD,
-	FUSE_MKDIR,
-	FUSE_UNLINK,
-	FUSE_RMDIR,
-	FUSE_RENAME,
-	FUSE_LINK,
-	FUSE_OPEN,
-	FUSE_READ,
-	FUSE_WRITE,
+	FUSE_LOOKUP	= 1,
+	FUSE_FORGET	= 2,
+	FUSE_GETATTR	= 3,
+	FUSE_SETATTR	= 4,
+	FUSE_READLINK	= 5,
+	FUSE_SYMLINK	= 6,
+	FUSE_GETDIR	= 7,
+	FUSE_MKNOD	= 8,
+	FUSE_MKDIR	= 9,
+	FUSE_UNLINK	= 10,
+	FUSE_RMDIR	= 11,
+	FUSE_RENAME	= 12,
+	FUSE_LINK	= 13,
+	FUSE_OPEN	= 14,
+	FUSE_READ	= 15,
+	FUSE_WRITE	= 16,
 };
 
 /* Conservative buffer size for the client */
@@ -98,7 +98,7 @@
 struct fuse_mknod_in {
 	unsigned short mode;
 	unsigned short rdev;
-	char name[1];
+	char name[0];
 };
 
 struct fuse_mknod_out {
@@ -108,17 +108,17 @@
 
 struct fuse_mkdir_in {
 	unsigned short mode;
-	char name[1];
+	char name[0];
 };
 
 struct fuse_rename_in {
 	unsigned long newdir;
-	char names[1];
+	char names[0];
 };
 
 struct fuse_link_in {
 	unsigned long newdir;
-	char name[1];
+	char name[0];
 };
 
 struct fuse_setattr_in {
@@ -142,7 +142,7 @@
 struct fuse_write_in {
 	unsigned long long offset;
 	unsigned int size;
-	char buf[1];
+	char buf[0];
 };
 
 struct fuse_in_header {
diff --git a/kernel/dev.c b/kernel/dev.c
index d4206e1..4395736 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -12,13 +12,39 @@
 #include <linux/proc_fs.h>
 #include <linux/file.h>
 
-#define IHSIZE sizeof(struct fuse_in_header)
-#define OHSIZE sizeof(struct fuse_out_header)
+/* If more requests are outstanding, then the operation will block */
+#define MAX_OUTSTANDING 10
 
 static struct proc_dir_entry *proc_fs_fuse;
 struct proc_dir_entry *proc_fuse_dev;
+static kmem_cache_t *fuse_req_cachep;
 
-static int interrupt_error(enum fuse_opcode opcode)
+static struct fuse_req *request_new(void)
+{
+	struct fuse_req *req;
+
+	req = (struct fuse_req *) kmem_cache_alloc(fuse_req_cachep, SLAB_NOFS);
+	if(req) {
+		INIT_LIST_HEAD(&req->list);
+		req->issync = 0;
+		req->locked = 0;
+		req->interrupted = 0;
+		req->sent = 0;
+		req->finished = 0;
+		req->in = NULL;
+		req->out = NULL;
+		init_waitqueue_head(&req->waitq);
+	}
+
+	return req;
+}
+
+static void request_free(struct fuse_req *req)
+{
+	kmem_cache_free(fuse_req_cachep, req);
+}
+
+static int request_restartable(enum fuse_opcode opcode)
 {
 	switch(opcode) {
 	case FUSE_LOOKUP:
@@ -28,222 +54,214 @@
 	case FUSE_OPEN:
 	case FUSE_READ:
 	case FUSE_WRITE:
-		return -ERESTARTSYS;
+		return 1;
 
 	default:
-		/* Operations which modify the filesystem cannot be safely
-                   restarted, because it is uncertain whether the
-                   operation has completed or not... */
-		return -EINTR;
+		return 0;
 	}
 }
 
-static int request_wait_answer(struct fuse_req *req)
+/* Called with fuse_lock held.  Releases, and then reaquires it. */
+static void request_wait_answer(struct fuse_req *req)
 {
-	int ret = 0;
-	DECLARE_WAITQUEUE(wait, current);
+	int intr;
+	
+	spin_unlock(&fuse_lock);
+	intr = wait_event_interruptible(req->waitq, req->finished);
+	spin_lock(&fuse_lock);
+	if(!intr)
+		return;
 
-	add_wait_queue(&req->waitq, &wait);
-	while(!list_empty(&req->list)) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		if(signal_pending(current)) {
-			ret = interrupt_error(req->opcode);
-			break;
-		}
+	/* Request interrupted... Wait for it to be unlocked */
+	if(req->locked) {
+		req->interrupted = 1;
 		spin_unlock(&fuse_lock);
-		schedule();
+		wait_event(req->waitq, !req->locked);
 		spin_lock(&fuse_lock);
 	}
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&req->waitq, &wait);
-
-	return ret;
-}
-
-static int request_check(struct fuse_req *req, struct fuse_out *outp)
-{
-	struct fuse_out_header *oh;
-	unsigned int size;
-
-	if(!req->out)
-		return -ECONNABORTED;
-
-	oh = (struct fuse_out_header *) req->out;
-	size = req->outsize - OHSIZE;
 	
-	if (oh->error <= -512 || oh->error > 0) {
-		printk("fuse: bad error value: %i\n", oh->error);
-		return -EPROTO;
-	}
-
-	if(size > outp->argsize || 
-	   (oh->error == 0 && !outp->argvar && size != outp->argsize) ||
-	   (oh->error != 0 && size != 0)) {
-		printk("fuse: invalid argument length: %i (%i)\n", size,
-		       req->opcode);
-		return -EPROTO;
-	}
-	
-	memcpy(&outp->h, oh, OHSIZE);
-	outp->argsize = size;
-	if(size)
-		memcpy(outp->arg, req->out + OHSIZE, size);
-	
-	return oh->error;
-}
-
-static void request_free(struct fuse_req *req)
-{
-	kfree(req->in);
-	kfree(req->out);
-	kfree(req);
-}
-
-
-static struct fuse_req *request_new(struct fuse_conn *fc, struct fuse_in *inp,
-				    struct fuse_out *outp)
-{
-	struct fuse_req *req;
-	
-	req = kmalloc(sizeof(*req), GFP_NOFS);
-	if(!req)
-		return NULL;
-
-	if(outp)
-		req->outsize = OHSIZE + outp->argsize;
+	/* Operations which modify the filesystem cannot safely be
+	   restarted, because it is uncertain whether the operation has
+	   completed or not... */
+	if(req->sent && !request_restartable(req->in->h.opcode))
+		req->out->h.error = -EINTR;
 	else
-		req->outsize = 0;
-	req->out = NULL;
-
-	req->insize = IHSIZE + inp->argsize;
-	req->in = kmalloc(req->insize, GFP_NOFS);
-	if(!req->in) {
-		request_free(req);
-		return NULL;
-	}
-	memcpy(req->in, &inp->h, IHSIZE);
-	if(inp->argsize)
-		memcpy(req->in + IHSIZE, inp->arg, inp->argsize);
-
-	req->opcode = inp->h.opcode;
-	init_waitqueue_head(&req->waitq);
-
-	return req;
+		req->out->h.error = -ERESTARTSYS;
 }
 
-/* If 'outp' is NULL then the request this is asynchronous */
-void request_send(struct fuse_conn *fc, struct fuse_in *inp,
-		  struct fuse_out *outp)
+static int get_unique(struct fuse_conn *fc)
 {
-	int ret;
-	struct fuse_in_header *ih;
+	do fc->reqctr++;
+	while(!fc->reqctr);
+	return fc->reqctr;
+}
+
+void request_send(struct fuse_conn *fc, struct fuse_in *in,
+		  struct fuse_out *out)
+{
 	struct fuse_req *req;
 
-	ret = -ENOMEM;
-	req = request_new(fc, inp, outp);
+	out->h.error = -ERESTARTSYS;
+	if(down_interruptible(&fc->outstanding))
+		return;
+
+	out->h.error = -ENOMEM;
+	req = request_new();
 	if(!req)
-		goto out;
+		return;
+
+	req->in = in;
+	req->out = out;
+	req->issync = 1;
 
 	spin_lock(&fuse_lock);
-	ret = -ENOTCONN;
-	if(!fc->file)
-		goto out_unlock_free;
-	
-	ih = (struct fuse_in_header *) req->in;
-	if(outp) {
-		do fc->reqctr++;
-		while(!fc->reqctr);
-		ih->unique = req->unique = fc->reqctr;
+	out->h.error = -ENOTCONN;
+	if(fc->file) {
+		in->h.unique = get_unique(fc);		
+		list_add_tail(&req->list, &fc->pending);
+		wake_up(&fc->waitq);
+		request_wait_answer(req);
+		list_del(&req->list);
 	}
-	else
-		ih->unique = req->unique = 0;
+	spin_unlock(&fuse_lock);
+	request_free(req);
+
+	up(&fc->outstanding);
+}
+
+
+static inline void destroy_request(struct fuse_conn *fc, struct fuse_req *req)
+{
+	if(req) {
+		int i;
+
+		for(i = 0; i < req->in->numargs; i++)
+			kfree(req->in->args[i].value);
+		kfree(req->in);
+		request_free(req);
+	}
+}
+
+/* This one is currently only used for sending the FORGET request, which is
+   a kernel initiated request.  So the outstanding semaphore is not used.  */
+int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in)
+{
+	struct fuse_req *req;
+
+	req = request_new();
+	if(!req)
+		return -ENOMEM;
+
+	req->in = in;
+	req->issync = 0;
+
+	spin_lock(&fuse_lock);
+	if(!fc->file) {
+		spin_unlock(&fuse_lock);
+		request_free(req);
+		return -ENOTCONN;
+	}
 
 	list_add_tail(&req->list, &fc->pending);
 	wake_up(&fc->waitq);
-
-	/* Async reqests are freed in fuse_dev_read() */
-	if(!outp) 
-		goto out_unlock; 
-	
-	ret = request_wait_answer(req);
-	list_del(&req->list);
-	if(!ret)
-		ret = request_check(req, outp);
-
-  out_unlock_free:
-	request_free(req);
-  out_unlock:
 	spin_unlock(&fuse_lock);
-  out:
-	if(outp)
-		outp->h.error = ret;
+	return 0;
 }
 
-static int request_wait(struct fuse_conn *fc)
+static void request_wait(struct fuse_conn *fc)
 {
-	int ret = 0;
 	DECLARE_WAITQUEUE(wait, current);
-	
+
 	add_wait_queue_exclusive(&fc->waitq, &wait);
 	while(list_empty(&fc->pending)) {
 		set_current_state(TASK_INTERRUPTIBLE);
-		if(signal_pending(current)) {
-			ret = -ERESTARTSYS;
+		if(signal_pending(current))
 			break;
-		}
+
 		spin_unlock(&fuse_lock);
 		schedule();
 		spin_lock(&fuse_lock);
 	}
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&fc->waitq, &wait);
-
-	return ret;
 }
 
+static inline int copy_in_one(const void *src, size_t srclen, char **dstp,
+			      size_t *dstlenp)
+{
+	if(*dstlenp < srclen) {
+		printk("fuse_dev_read: buffer too small\n");
+		return -EIO;
+	}
+			
+	if(copy_to_user(*dstp, src, srclen))
+		return -EFAULT;
+
+	*dstp += srclen;
+	*dstlenp -= srclen;
+
+	return 0;
+}
+
+static inline int copy_in_args(struct fuse_in *in, char *buf, size_t nbytes)
+{
+	int err;
+	int i;
+	size_t orignbytes = nbytes;
+		
+	err = copy_in_one(&in->h, sizeof(in->h), &buf, &nbytes);
+	if(err)
+		return err;
+
+	for(i = 0; i < in->numargs; i++) {
+		struct fuse_in_arg *arg = &in->args[i];
+		err = copy_in_one(arg->value, arg->size, &buf, &nbytes);
+		if(err)
+			return err;
+	}
+
+	return orignbytes - nbytes;
+}
 
 static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
 			     loff_t *off)
 {
-	int ret;
+	ssize_t ret;
 	struct fuse_conn *fc = DEV_FC(file);
-	struct fuse_req *req;
-	char *tmpbuf;
-	unsigned int size;
+	struct fuse_req *req = NULL;
 
 	if(fc->sb == NULL)
 		return -EPERM;
-	
+
 	spin_lock(&fuse_lock);
-	ret = request_wait(fc);
-	if(ret)
-		goto err;
-	
-	req = list_entry(fc->pending.next, struct fuse_req, list);
-	size = req->insize;
-	if(nbytes < size) {
-		printk("fuse_dev_read: buffer too small\n");
-		ret = -EIO;
-		goto err;
+	request_wait(fc);
+	if(!list_empty(&fc->pending)) {
+		req = list_entry(fc->pending.next, struct fuse_req, list);
+		list_del_init(&req->list);
+		req->locked = 1;
 	}
-	tmpbuf = req->in;
-	req->in = NULL;
-
-	list_del(&req->list);
-	if(req->outsize)
-		list_add_tail(&req->list, &fc->processing);
-	else
-		request_free(req);
 	spin_unlock(&fuse_lock);
+	if(req == NULL)
+		return -ERESTARTSYS;
 
-	if(copy_to_user(buf, tmpbuf, size))
-		return -EFAULT;
-	
-	kfree(tmpbuf);
-	return size;
-
-  err:
+	ret = copy_in_args(req->in, buf, nbytes);
+	spin_lock(&fuse_lock);
+	if(req->issync || ret < 0) {
+		if(ret < 0) 
+			list_add_tail(&req->list, &fc->pending);
+		else {
+			list_add_tail(&req->list, &fc->processing);
+			req->sent = 1;
+		}
+		req->locked = 0;
+		if(req->interrupted)
+			wake_up(&req->waitq);
+		
+		req = NULL;
+	}
 	spin_unlock(&fuse_lock);
+	destroy_request(fc, req);
+
 	return ret;
 }
 
@@ -255,7 +273,7 @@
 	list_for_each(entry, &fc->processing) {
 		struct fuse_req *tmp;
 		tmp = list_entry(entry, struct fuse_req, list);
-		if(tmp->unique == unique) {
+		if(tmp->in->h.unique == unique) {
 			req = tmp;
 			break;
 		}
@@ -264,58 +282,135 @@
 	return req;
 }
 
+static void process_getdir(struct fuse_req *req)
+{
+	struct fuse_getdir_out *arg;
+	arg = (struct fuse_getdir_out *) req->out->args[0].value;
+	arg->file = fget(arg->fd);
+}
+
+static inline int copy_out_one(struct fuse_out_arg *arg, const char **srcp,
+			       size_t *srclenp, int allowvar)
+{
+	size_t dstlen = arg->size;
+	if(*srclenp < dstlen) {
+		if(!allowvar) {
+			printk("fuse_dev_write: write is short\n");
+			return -EIO;
+		}
+		dstlen = *srclenp;
+	}
+
+	if(dstlen) {
+		if(copy_from_user(arg->value, *srcp, dstlen))
+			return -EFAULT;
+	}
+
+	*srcp += dstlen;
+	*srclenp -= dstlen;
+	arg->size = dstlen;
+
+	return 0;
+}
+
+static inline int copy_out_args(struct fuse_out *out, const char *buf,
+				size_t nbytes)
+{
+	int err;
+	int i;
+
+	buf += sizeof(struct fuse_out_header);
+	nbytes -= sizeof(struct fuse_out_header);
+		
+	if(!out->h.error) {
+		for(i = 0; i < out->numargs; i++) {
+			struct fuse_out_arg *arg = &out->args[i];
+			int allowvar;
+
+			if(out->argvar && i == out->numargs - 1)
+				allowvar = 1;
+			else
+				allowvar = 0;
+
+			err = copy_out_one(arg, &buf, &nbytes, allowvar);
+			if(err)
+				return err;
+		}
+	}
+
+	if(nbytes != 0) {
+		printk("fuse_dev_write: write is long\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static inline int copy_out_header(struct fuse_out_header *oh, const char *buf,
+				  size_t nbytes)
+{
+	if(nbytes < sizeof(struct fuse_out_header)) {
+		printk("fuse_dev_write: write is short\n");
+		return -EIO;
+	}
+	
+	if(copy_from_user(oh, buf, sizeof(struct fuse_out_header)))
+		return -EFAULT;
+
+        if (oh->error <= -512 || oh->error > 0) {
+                printk("fuse_dev_write: bad error value\n");
+                return -EIO;
+        }
+
+	return 0;
+}
+
 static ssize_t fuse_dev_write(struct file *file, const char *buf,
 			      size_t nbytes, loff_t *off)
 {
-	ssize_t ret;
+	int err;
 	struct fuse_conn *fc = DEV_FC(file);
 	struct fuse_req *req;
-	char *tmpbuf;
-	struct fuse_out_header *oh;
+	struct fuse_out_header oh;
 
 	if(!fc->sb)
 		return -EPERM;
 
-	ret = -EIO;
-	if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) {
-		printk("fuse_dev_write: write is short or long\n");
-		goto out;
-	}
-	
-	ret = -ENOMEM;
-	tmpbuf = kmalloc(nbytes, GFP_NOFS);
-	if(!tmpbuf)
-		goto out;
-	
-	ret = -EFAULT;
-	if(copy_from_user(tmpbuf, buf, nbytes))
-		goto out_free;
+	err = copy_out_header(&oh, buf, nbytes);
+	if(err)
+		return err;
 	
 	spin_lock(&fuse_lock);
-	oh =  (struct fuse_out_header *) tmpbuf;
-	req = request_find(fc, oh->unique);
-	if(req == NULL) {
-		ret = -ENOENT;
-		goto out_free_unlock;
+	req = request_find(fc, oh.unique);
+	if(req != NULL) {
+		list_del_init(&req->list);
+		req->locked = 1;
 	}
-	list_del_init(&req->list);
-	if(req->opcode == FUSE_GETDIR) {
-		/* fget() needs to be done in this context */
-		struct fuse_getdir_out *arg;
-		arg = (struct fuse_getdir_out *) (tmpbuf + OHSIZE);
-		arg->file = fget(arg->fd);
-	}
-	req->out = tmpbuf;
-	req->outsize = nbytes;
-	tmpbuf = NULL;
-	ret = nbytes;
-	wake_up(&req->waitq);
-  out_free_unlock:
 	spin_unlock(&fuse_lock);
-  out_free:
-	kfree(tmpbuf);
-  out:
-	return ret;
+	if(!req)
+		return -ENOENT;
+
+	req->out->h = oh;
+	err = copy_out_args(req->out, buf, nbytes);
+
+	spin_lock(&fuse_lock);
+	if(err)
+		list_add_tail(&fc->processing, &req->list);
+	else {
+		/* fget() needs to be done in this context */
+		if(req->in->h.opcode == FUSE_GETDIR && !oh.error)
+			process_getdir(req);
+		req->finished = 1;
+	}	
+	req->locked = 0;
+	if(!err || req->interrupted)
+		wake_up(&req->waitq);
+	spin_unlock(&fuse_lock);
+
+	if(!err)
+		return nbytes;
+	else
+		return err;
 }
 
 
@@ -350,6 +445,7 @@
 		init_waitqueue_head(&fc->waitq);
 		INIT_LIST_HEAD(&fc->pending);
 		INIT_LIST_HEAD(&fc->processing);
+		sema_init(&fc->outstanding, MAX_OUTSTANDING);
 		fc->reqctr = 1;
 	}
 	return fc;
@@ -369,16 +465,18 @@
 	return 0;
 }
 
-static void end_requests(struct list_head *head)
+static void end_requests(struct fuse_conn *fc, struct list_head *head)
 {
 	while(!list_empty(head)) {
 		struct fuse_req *req;
 		req = list_entry(head->next, struct fuse_req, list);
 		list_del_init(&req->list);
-		if(req->outsize)
+		if(req->issync) {
+			req->out->h.error = -ECONNABORTED;
 			wake_up(&req->waitq);
+		}
 		else
-			request_free(req);
+			destroy_request(fc, req);
 	}
 }
 
@@ -388,8 +486,8 @@
 
 	spin_lock(&fuse_lock);
 	fc->file = NULL;
-	end_requests(&fc->pending);
-	end_requests(&fc->processing);
+	end_requests(fc, &fc->pending);
+	end_requests(fc, &fc->processing);
 	fuse_release_conn(fc);
 	spin_unlock(&fuse_lock);
 	return 0;
@@ -411,6 +509,12 @@
 	proc_fs_fuse = NULL;
 	proc_fuse_dev = NULL;
 
+	fuse_req_cachep = kmem_cache_create("fuser_request",
+					     sizeof(struct fuse_req),
+					     0, 0, NULL, NULL);
+	if(!fuse_req_cachep)
+		return -ENOMEM;
+
 	ret = -EIO;
 	proc_fs_fuse = proc_mkdir("fuse", proc_root_fs);
 	if(!proc_fs_fuse) {
@@ -440,6 +544,8 @@
 		remove_proc_entry("dev", proc_fs_fuse);
 		remove_proc_entry("fuse", proc_root_fs);
 	}
+	
+	kmem_cache_destroy(fuse_req_cachep);
 }
 
 /* 
diff --git a/kernel/dir.c b/kernel/dir.c
index 537b71c..a3f1485 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -79,29 +79,52 @@
 	return inode;
 }
 
+/* If the inode belongs to an existing directory, then it cannot be
+ assigned to new dentry */
+static int inode_ok(struct inode *inode)
+{
+	struct dentry *alias;
+	if(S_ISDIR(inode->i_mode) && (alias = d_find_alias(inode)) != NULL) {
+		dput(alias);
+		printk("fuse: cannot assign an existing directory\n");
+		return 0;
+
+	}
+	return 1;
+}
+
 static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
 {
 	int ret;
 	struct fuse_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_lookup_out arg;
+	struct fuse_lookup_out outarg;
 	struct inode *inode;
 
 	in.h.opcode = FUSE_LOOKUP;
 	in.h.ino = dir->i_ino;
-	in.argsize = entry->d_name.len + 1;
-	in.arg = entry->d_name.name;
-	out.argsize = sizeof(arg);
-	out.arg = &arg;
+	in.numargs = 1;
+	in.args[0].size = entry->d_name.len + 1;
+	in.args[0].value = entry->d_name.name;
+	out.numargs = 1;
+	out.args[0].size = sizeof(outarg);
+	out.args[0].value = &outarg;
 	request_send(fc, &in, &out);
 	
 	inode = NULL;
 	if(!out.h.error) {
 		ret = -ENOMEM;
-		inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr, out.h.unique);
+		inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr,
+				  out.h.unique);
 		if(!inode) 
 			goto err;
+
+		ret = -EPROTO;
+		if(!inode_ok(inode)) {
+			iput(inode);
+			goto err;
+		}
 	}
 	else if(out.h.error != -ENOENT) {
 		ret = out.h.error;
@@ -126,28 +149,25 @@
 	struct fuse_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_mknod_in *inarg;
-	unsigned int insize;
+	struct fuse_mknod_in inarg;
 	struct fuse_mknod_out outarg;
 	struct inode *inode;
-	
-	insize = offsetof(struct fuse_mknod_in, name) + entry->d_name.len + 1;
-	inarg = kmalloc(insize, GFP_KERNEL);
-	if(!inarg)
-		return -ENOMEM;
-	
-	inarg->mode = mode;
-	inarg->rdev = rdev;
-	strcpy(inarg->name, entry->d_name.name);
+
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.mode = mode;
+	inarg.rdev = rdev;
 
 	in.h.opcode = FUSE_MKNOD;
 	in.h.ino = dir->i_ino;
-	in.argsize = insize;
-	in.arg = inarg;
-	out.argsize = sizeof(outarg);
-	out.arg = &outarg;
+	in.numargs = 2;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	in.args[1].size = entry->d_name.len + 1;
+	in.args[1].value = entry->d_name.name;
+	out.numargs = 1;
+	out.args[0].size = sizeof(outarg);
+	out.args[0].value = &outarg;
 	request_send(fc, &in, &out);
-	kfree(inarg);
 
 	if(out.h.error) 
 		return out.h.error;
@@ -156,6 +176,18 @@
 	if(!inode) 
 		return -ENOMEM;
 
+	/* Don't allow userspace to do really stupid things... */
+	if((inode->i_mode ^ mode) & S_IFMT) {
+		iput(inode);
+		printk("fuse_mknod: inode has wrong type\n");
+		return -EPROTO;
+	}
+
+	if(!inode_ok(inode)) {
+		iput(inode);
+		return -EPROTO;
+	}
+
 	d_instantiate(entry, inode);
 	return 0;
 }
@@ -171,23 +203,19 @@
 	struct fuse_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_mkdir_in *inarg;
-	unsigned int insize;
-	
-	insize = offsetof(struct fuse_mkdir_in, name) + entry->d_name.len + 1;
-	inarg = kmalloc(insize, GFP_KERNEL);
-	if(!inarg)
-		return -ENOMEM;
-	
-	inarg->mode = mode;
-	strcpy(inarg->name, entry->d_name.name);
+	struct fuse_mkdir_in inarg;
+
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.mode = mode;
 
 	in.h.opcode = FUSE_MKDIR;
 	in.h.ino = dir->i_ino;
-	in.argsize = insize;
-	in.arg = inarg;
+	in.numargs = 2;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	in.args[1].size = entry->d_name.len + 1;
+	in.args[1].value = entry->d_name.name;
 	request_send(fc, &in, &out);
-	kfree(inarg);
 
 	return out.h.error;
 }
@@ -198,24 +226,16 @@
 	struct fuse_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	char *inarg;
-	unsigned int insize;
-	
-	insize = entry->d_name.len + 1 + strlen(link) + 1;
-	inarg = kmalloc(insize, GFP_KERNEL);
-	if(!inarg)
-		return -ENOMEM;
-	
-	strcpy(inarg, entry->d_name.name);
-	strcpy(inarg + entry->d_name.len + 1, link);
 
 	in.h.opcode = FUSE_SYMLINK;
 	in.h.ino = dir->i_ino;
-	in.argsize = insize;
-	in.arg = inarg;
+	in.numargs = 2;
+	in.args[0].size = entry->d_name.len + 1;
+	in.args[0].value = entry->d_name.name;
+	in.args[1].size = strlen(link) + 1;
+	in.args[1].value = link;
 	request_send(fc, &in, &out);
-	kfree(inarg);
-	
+
 	return out.h.error;
 }
 
@@ -228,9 +248,11 @@
 
 	in.h.opcode = op;
 	in.h.ino = dir->i_ino;
-	in.argsize = entry->d_name.len + 1;
-	in.arg = entry->d_name.name;
+	in.numargs = 1;
+	in.args[0].size = entry->d_name.len + 1;
+	in.args[0].value = entry->d_name.name;
 	request_send(fc, &in, &out);
+
 	return out.h.error;
 }
 
@@ -250,27 +272,21 @@
 	struct fuse_conn *fc = INO_FC(olddir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_rename_in *inarg;
-	unsigned int oldnamsize = oldent->d_name.len + 1;
-	unsigned int newnamsize = newent->d_name.len + 1;
-	unsigned int insize;
+	struct fuse_rename_in inarg;
 	
-	insize = offsetof(struct fuse_rename_in, names) + oldnamsize +
-		newnamsize;
-	inarg = kmalloc(insize, GFP_KERNEL);
-	if(!inarg)
-		return -ENOMEM;
-	
-	inarg->newdir = newdir->i_ino;
-	strcpy(inarg->names, oldent->d_name.name);
-	strcpy(inarg->names + oldnamsize, newent->d_name.name);
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.newdir = newdir->i_ino;
 
 	in.h.opcode = FUSE_RENAME;
 	in.h.ino = olddir->i_ino;
-	in.argsize = insize;
-	in.arg = inarg;
+	in.numargs = 3;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	in.args[1].size = oldent->d_name.len + 1;
+	in.args[1].value = oldent->d_name.name;
+	in.args[2].size = newent->d_name.len + 1;
+	in.args[2].value = newent->d_name.name;
 	request_send(fc, &in, &out);
-	kfree(inarg);
 
 	return out.h.error;
 }
@@ -282,23 +298,19 @@
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_link_in *inarg;
-	unsigned int insize;
+	struct fuse_link_in inarg;
 	
-	insize = offsetof(struct fuse_link_in, name) + newent->d_name.len + 1;
-	inarg = kmalloc(insize, GFP_KERNEL);
-	if(!inarg)
-		return -ENOMEM;
-	
-	inarg->newdir = newdir->i_ino;
-	strcpy(inarg->name, newent->d_name.name);
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.newdir = newdir->i_ino;
 
 	in.h.opcode = FUSE_LINK;
 	in.h.ino = inode->i_ino;
-	in.argsize = insize;
-	in.arg = inarg;
+	in.numargs = 2;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	in.args[1].size = newent->d_name.len + 1;
+	in.args[1].value = newent->d_name.name;
 	request_send(fc, &in, &out);
-	kfree(inarg);
 
 	return out.h.error;
 }
@@ -328,8 +340,9 @@
 
 	in.h.opcode = FUSE_GETATTR;
 	in.h.ino = inode->i_ino;
-	out.argsize = sizeof(arg);
-	out.arg = &arg;
+	out.numargs = 1;
+	out.args[0].size = sizeof(arg);
+	out.args[0].value = &arg;
 	request_send(fc, &in, &out);
 	
 	if(!out.h.error)
@@ -400,16 +413,17 @@
 
 	in.h.opcode = FUSE_READLINK;
 	in.h.ino = inode->i_ino;
-	out.arg = link;
-	out.argsize = PAGE_SIZE - 1;
 	out.argvar = 1;
+	out.numargs = 1;
+	out.args[0].size = PAGE_SIZE - 1;
+	out.args[0].value = link;
 	request_send(fc, &in, &out);
 	if(out.h.error) {
 		free_page((unsigned long) link);
 		return ERR_PTR(out.h.error);
 	}
 
-	link[out.argsize] = '\0';
+	link[out.args[0].size] = '\0';
 	return link;
 }
 
@@ -453,8 +467,9 @@
 	
 	in.h.opcode = FUSE_GETDIR;
 	in.h.ino = inode->i_ino;
-	out.argsize = sizeof(outarg);
-	out.arg = &outarg;
+	out.numargs = 1;
+	out.args[0].size = sizeof(outarg);
+	out.args[0].value = &outarg;
 	request_send(fc, &in, &out);
 	if(!out.h.error) {
 		struct file *cfile = outarg.file;
@@ -521,14 +536,17 @@
 	struct fuse_setattr_in inarg;
 	struct fuse_setattr_out outarg;
 
+	memset(&inarg, 0, sizeof(inarg));
 	inarg.valid = iattr_to_fattr(attr, &inarg.attr);
 	
 	in.h.opcode = FUSE_SETATTR;
 	in.h.ino = inode->i_ino;
-	in.argsize = sizeof(inarg);
-	in.arg = &inarg;
-	out.argsize = sizeof(outarg);
-	out.arg = &outarg;
+	in.numargs = 1;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	out.numargs = 1;
+	out.args[0].size = sizeof(outarg);
+	out.args[0].value = &outarg;
 	request_send(fc, &in, &out);
 
 	if(!out.h.error) {
diff --git a/kernel/file.c b/kernel/file.c
index a67bdcd..b7cb2f4 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -10,19 +10,21 @@
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 
-
 static int fuse_open(struct inode *inode, struct file *file)
 {
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_open_in arg;
+	struct fuse_open_in inarg;
 
-	arg.flags = file->f_flags & ~O_EXCL;
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.flags = file->f_flags & ~O_EXCL;
+
 	in.h.opcode = FUSE_OPEN;
 	in.h.ino = inode->i_ino;
-	in.argsize = sizeof(arg);
-	in.arg = &arg;
+	in.numargs = 1;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
 	request_send(fc, &in, &out);
 	if(!out.h.error)
 		invalidate_inode_pages(inode);
@@ -37,27 +39,30 @@
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_read_in arg;
+	struct fuse_read_in inarg;
 	char *buffer;
 
 	buffer = kmap(page);
-
-	arg.offset = page->index << PAGE_CACHE_SHIFT;
-	arg.size = PAGE_CACHE_SIZE;
+	
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.offset = page->index << PAGE_CACHE_SHIFT;
+	inarg.size = PAGE_CACHE_SIZE;
 
 	in.h.opcode = FUSE_READ;
 	in.h.ino = inode->i_ino;
-	in.argsize = sizeof(arg);
-	in.arg = &arg;
-	out.argsize = PAGE_CACHE_SIZE;
+	in.numargs = 1;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
 	out.argvar = 1;
-	out.arg = buffer;
+	out.numargs = 1;
+	out.args[0].size = PAGE_CACHE_SIZE;
+	out.args[0].value = buffer;
 
 	request_send(fc, &in, &out);
 	if(!out.h.error) {
-		if(out.argsize < PAGE_CACHE_SIZE) 
-			memset(buffer + out.argsize, 0,
-			       PAGE_CACHE_SIZE - out.argsize);
+		size_t outsize = out.args[0].size;
+		if(outsize < PAGE_CACHE_SIZE) 
+			memset(buffer + outsize, 0, PAGE_CACHE_SIZE - outsize);
 		SetPageUptodate(page);
 	}
 
@@ -73,27 +78,25 @@
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_write_in *arg;
-	size_t argsize;
+	struct fuse_write_in inarg;
 	char *buffer;
 
-	argsize = offsetof(struct fuse_write_in, buf) + count;
-	arg = kmalloc(argsize, GFP_KERNEL);
-	if(!arg)
-		return -ENOMEM;
-
-	arg->offset = (page->index << PAGE_CACHE_SHIFT) + offset;
-	arg->size = count;
 	buffer = kmap(page);
-	memcpy(arg->buf, buffer + offset, count);
-	kunmap(page);
+
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.offset = (page->index << PAGE_CACHE_SHIFT) + offset;
+	inarg.size = count;
 	
 	in.h.opcode = FUSE_WRITE;
 	in.h.ino = inode->i_ino;
-	in.argsize = argsize;
-	in.arg = arg;
+	in.numargs = 2;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	in.args[1].size = count;
+	in.args[1].value = buffer + offset;
 	request_send(fc, &in, &out);
-	kfree(arg);
+
+	kunmap(page);
 
 	return out.h.error;
 }
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index e8dde37..27d8eb3 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -14,14 +14,12 @@
 #include <linux/list.h>
 #include <linux/spinlock.h>
 
-#define MAX_CLEARED 256
-
 /**
  * A Fuse connection.
  *
  * This structure is created, when the client device is opened, and is
  * destroyed, when the client device is closed _and_ the filesystem is
- * umounted.
+ * unmounted.
  */
 struct fuse_conn {
 	/** The superblock of the mounted filesystem */
@@ -36,7 +34,7 @@
 	/** The fuse mount flags for this mount */
 	unsigned int flags;
 
-	/** The client wait queue */
+	/** Readers of the connection are waiting on this */
 	wait_queue_head_t waitq;
 
 	/** The list of pending requests */
@@ -45,10 +43,43 @@
 	/** The list of requests being processed */
 	struct list_head processing;
 
-	/** The request id */
+	/** Controls the maximum number of outstanding requests */
+	struct semaphore outstanding;
+	
+	/** The next unique request id */
 	int reqctr;
 };
 
+/** One input argument of a request */
+struct fuse_in_arg {
+	unsigned int size;
+	const void *value;
+};
+
+/** The request input */
+struct fuse_in {
+	struct fuse_in_header h;
+	unsigned int numargs;
+	struct fuse_in_arg args[3];
+};
+
+/** One output argument of a request */
+struct fuse_out_arg {
+	unsigned int size;
+	void *value;
+};
+
+/** The request output */
+struct fuse_out {
+	struct fuse_out_header h;
+	unsigned int argvar;
+	unsigned int numargs;
+	struct fuse_out_arg args[3];
+};
+
+#define FUSE_IN_INIT { {0, 0, 0}, 0}
+#define FUSE_OUT_INIT { {0, 0}, 0, 0}
+
 /**
  * A request to the client
  */
@@ -56,25 +87,28 @@
 	/** The request list */
 	struct list_head list;
 
-	/** The request ID */
-	int unique;
+	/** True if the request is synchronous */
+	unsigned int issync:1;
 
-	/** The opcode */
-	enum fuse_opcode opcode;
-	
-	/** The request input size */
-	unsigned int insize;
+	/** The request is locked */
+	unsigned int locked:1;
+
+	/** The request has been interrupted while it was locked */
+	unsigned int interrupted:1;
+
+	/* The request has been sent to the client */
+	unsigned int sent:1;
+
+	/* The request is finished */
+	unsigned int finished:1;
 
 	/** The request input */
-	char *in;
-	
-	/** The maximum request output size */
-	unsigned int outsize;
+	struct fuse_in *in;
 
 	/** The request output */
-	char *out;
+	struct fuse_out *out;
 
-	/** The request wait queue */
+	/** Used to wake up the task waiting for completion of request*/
 	wait_queue_head_t waitq;
 };
 
@@ -82,22 +116,6 @@
 #define INO_FC(inode) ((struct fuse_conn *) (inode)->i_sb->u.generic_sbp)
 #define DEV_FC(file) ((struct fuse_conn *) (file)->private_data)
 
-struct fuse_in {
-	struct fuse_in_header h;
-	unsigned int argsize;
-	const void *arg;
-};
-
-struct fuse_out {
-	struct fuse_out_header h;
-	unsigned int argsize;
-	unsigned int argvar;
-	void *arg;
-};
-
-#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
-#define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }
-
 
 /**
  * The proc entry for the client device ("/proc/fs/fuse/dev")
@@ -155,6 +173,11 @@
 void request_send(struct fuse_conn *fc, struct fuse_in *in,
 		  struct fuse_out *out);
 
+/**
+ * Send a request for which a reply is not expected
+ */
+int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in);
+
 /*
  * Local Variables:
  * indent-tabs-mode: t
diff --git a/kernel/inode.c b/kernel/inode.c
index 36819b7..df402ba 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -23,17 +23,35 @@
 static void fuse_clear_inode(struct inode *inode)
 {
 	struct fuse_conn *fc = INO_FC(inode);
-	struct fuse_in in = FUSE_IN_INIT;
-	struct fuse_forget_in arg;
+	struct fuse_in *in = NULL;
+	struct fuse_forget_in *inarg = NULL;
 	
-	arg.version = inode->i_version;
+	if(fc == NULL)
+		return;
+
+	in = kmalloc(sizeof(struct fuse_in), GFP_NOFS);
+	if(!in)
+		return;
+
+	inarg = kmalloc(sizeof(struct fuse_forget_in), GFP_NOFS);
+	if(!inarg) 
+		goto out_free;
 	
-	in.h.opcode = FUSE_FORGET;
-	in.h.ino = inode->i_ino;
-	in.argsize = sizeof(arg);
-	in.arg = &arg;
-	
-	request_send(fc, &in, NULL);
+	memset(inarg, 0, sizeof(struct fuse_forget_in));
+	inarg->version = inode->i_version;
+		
+	in->h.opcode = FUSE_FORGET;
+	in->h.ino = inode->i_ino;
+	in->numargs = 1;
+	in->args[0].size = sizeof(struct fuse_forget_in);
+	in->args[0].value = inarg;
+		
+	if(!request_send_noreply(fc, in))
+		return;
+
+  out_free:
+	kfree(inarg);
+	kfree(in);
 }
 
 static void fuse_put_super(struct super_block *sb)
@@ -45,6 +63,7 @@
 	fc->uid = 0;
 	fc->flags = 0;
 	fuse_release_conn(fc);
+	sb->u.generic_sbp = NULL;
 	spin_unlock(&fuse_lock);
 }
 
diff --git a/lib/fuse.c b/lib/fuse.c
index 8ea13ad..2ed5169 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -165,20 +165,6 @@
     return get_ino(node);
 }
 
-static fino_t find_node_dir(struct fuse *f, fino_t parent, char *name)
-{
-    struct node *node;
-
-    pthread_mutex_lock(&f->lock);
-    node = lookup_node(f, parent, name);
-    pthread_mutex_unlock(&f->lock);
-
-    if(node != NULL)
-        return get_ino(node);
-    else
-        return (fino_t) -1;
-}
-
 static char *add_name(char *buf, char *s, const char *name)
 {
     size_t len = strlen(name);
@@ -310,7 +296,7 @@
     size_t reclen;
     size_t res;
 
-    dirent.ino = find_node_dir(dh->fuse, dh->dir, name);
+    dirent.ino = (unsigned long) -1;
     dirent.namelen = strlen(name);
     strncpy(dirent.name, name, sizeof(dirent.name));
     dirent.type = type;
@@ -323,10 +309,28 @@
     return 0;
 }
 
+static void send_reply_raw(struct fuse *f, char *outbuf, size_t outsize)
+{
+    int res;
+
+    if((f->flags & FUSE_DEBUG)) {
+        struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+        printf("   unique: %i, error: %i (%s), outsize: %i\n", out->unique,
+               out->error, strerror(-out->error), outsize);
+        fflush(stdout);
+    }
+                
+    res = write(f->fd, outbuf, outsize);
+    if(res == -1) {
+        /* ENOENT means the operation was interrupted */
+        if(errno != ENOENT)
+            perror("writing fuse device");
+    }
+}
+
 static void send_reply(struct fuse *f, struct fuse_in_header *in, int error,
                        void *arg, size_t argsize)
 {
-    int res;
     char *outbuf;
     size_t outsize;
     struct fuse_out_header *out;
@@ -347,18 +351,7 @@
     if(argsize != 0)
         memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
 
-    if((f->flags & FUSE_DEBUG)) {
-        printf("   unique: %i, error: %i (%s), outsize: %i\n", out->unique,
-               out->error, strerror(-out->error), outsize);
-        fflush(stdout);
-    }
-                
-    res = write(f->fd, outbuf, outsize);
-    if(res == -1) {
-        /* ENOENT means the operation was interrupted */
-        if(errno != ENOENT)
-            perror("writing fuse device");
-    }
+    send_reply_raw(f, outbuf, outsize);
 
     free(outbuf);
 }
@@ -388,6 +381,10 @@
 static void do_forget(struct fuse *f, struct fuse_in_header *in,
                       struct fuse_forget_in *arg)
 {
+    if(f->flags & FUSE_DEBUG) {
+        printf("FORGET %li/%i\n", in->ino, arg->version);
+        fflush(stdout);
+    }
     destroy_node(f, in->ino, arg->version);
 }
 
@@ -697,8 +694,11 @@
 {
     int res;
     char *path;
-    char *buf = (char *) malloc(arg->size);
+    char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
+    struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+    char *buf = outbuf + sizeof(struct fuse_out_header);
     size_t size;
+    size_t outsize;
 
     res = -ENOENT;
     path = get_path(f, in->ino);
@@ -714,9 +714,12 @@
         size = res;
         res = 0;
     }
-
-    send_reply(f, in, res, buf, size);
-    free(buf);
+    out->unique = in->unique;
+    out->error = res;
+    outsize = sizeof(struct fuse_out_header) + size;
+    
+    send_reply_raw(f, outbuf, outsize);
+    free(outbuf);
 }
 
 static void do_write(struct fuse *f, struct fuse_in_header *in,
@@ -747,6 +750,12 @@
     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 *f, struct fuse_cmd *cmd)
 {
     struct fuse_in_header *in = (struct fuse_in_header *) cmd->buf;
@@ -766,10 +775,6 @@
         do_lookup(f, in, (char *) inarg);
         break;
 
-    case FUSE_FORGET:
-        do_forget(f, in, (struct fuse_forget_in *) inarg);
-        break;
-
     case FUSE_GETATTR:
         do_getattr(f, in);
         break;
@@ -826,37 +831,45 @@
 
     default:
         fprintf(stderr, "Operation %i not implemented\n", in->opcode);
-        /* No need to send reply to async requests */
-        if(in->unique != 0)
-            send_reply(f, in, -ENOSYS, NULL, 0);
+        send_reply(f, in, -ENOSYS, NULL, 0);
     }
-    
-    free(cmd->buf);
-    free(cmd);
+
+    free_cmd(cmd);
 }
 
 struct fuse_cmd *__fuse_read_cmd(struct fuse *f)
 {
     ssize_t res;
-    char inbuf[FUSE_MAX_IN];
     struct fuse_cmd *cmd;
-    
-    res = read(f->fd, inbuf, sizeof(inbuf));
-    if(res == -1) {
-        perror("reading fuse device");
-        /* BAD... This will happen again */
-        return NULL;
-    }
-    if((size_t) res < sizeof(struct fuse_in_header)) {
-        fprintf(stderr, "short read on fuse device\n");
-        /* Cannot happen */
-        return NULL;
-    }
+    struct fuse_in_header *in;
 
-    cmd = (struct fuse_cmd *) malloc(sizeof(*cmd));
-    cmd->buflen = res;
-    cmd->buf = (char *) malloc(cmd->buflen);
-    memcpy(cmd->buf, inbuf, cmd->buflen);
+    cmd = (struct fuse_cmd *) malloc(sizeof(struct fuse_cmd));
+    cmd->buf = (char *) malloc(FUSE_MAX_IN);
+
+    do {
+        res = read(f->fd, cmd->buf, FUSE_MAX_IN);
+        if(res == -1) {
+            perror("reading fuse device");
+            /* BAD... This will happen again */
+            free_cmd(cmd);
+            return NULL;
+        }
+        if((size_t) res < sizeof(struct fuse_in_header)) {
+            fprintf(stderr, "short read on fuse device\n");
+            /* Cannot happen */
+            free_cmd(cmd);
+            return NULL;
+        }
+        cmd->buflen = res;
+
+        /* FORGET is special: it can be done without calling filesystem
+           methods. */
+        in = (struct fuse_in_header *) cmd->buf;
+        if(in->opcode == FUSE_FORGET) {
+            void *inarg = cmd->buf + sizeof(struct fuse_in_header);
+            do_forget(f, in, (struct fuse_forget_in *) inarg);
+        }
+    } while(in->opcode == FUSE_FORGET);
 
     return cmd;
 }
diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c
index 19cc33c..6120554 100644
--- a/lib/fuse_mt.c
+++ b/lib/fuse_mt.c
@@ -13,23 +13,64 @@
 #include <string.h>
 #include <pthread.h>
 #include <signal.h>
+#include <errno.h>
+#include <sys/time.h>
 
-struct fuse_thr_data {
+#define FUSE_WORKER_IDLE 10
+
+static pthread_mutex_t fuse_mt_lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+struct fuse_worker {
+    struct fuse_worker *next;
+    struct fuse_worker *prev;
     struct fuse *f;
     void *data;
     fuse_processor_t proc;
     struct fuse_cmd *cmd;
+    int avail;
+    pthread_cond_t start;
 };
 
 static void *do_work(void *data)
 {
-    struct fuse_thr_data *d = (struct fuse_thr_data *) data;
-    d->proc(d->f, d->cmd, d->data);
-    free(d);
+    struct fuse_worker *w = (struct fuse_worker *) data;
+    int ret;
+    
+    do {
+        struct timeval now;
+        struct timespec timeout;
+
+        w->proc(w->f, w->cmd, w->data);
+
+        pthread_mutex_lock(&fuse_mt_lock);
+        w->avail = 1;
+        w->cmd = NULL;
+        gettimeofday(&now, NULL);
+        timeout.tv_sec = now.tv_sec + FUSE_WORKER_IDLE;
+        timeout.tv_nsec = now.tv_usec * 1000;
+
+        ret = 0;
+        while(w->cmd == NULL && ret != ETIMEDOUT)
+            ret = pthread_cond_timedwait(&w->start, &fuse_mt_lock, &timeout);
+
+        if(ret == ETIMEDOUT) {
+            struct fuse_worker *next = w->next;
+            struct fuse_worker *prev = w->prev;
+            prev->next = next;
+            next->prev = prev;
+            pthread_cond_destroy(&w->start);
+            free(w);
+        }
+        w->avail = 0;
+        pthread_mutex_unlock(&fuse_mt_lock);
+
+    } while(ret != ETIMEDOUT);
+
     return NULL;
 }
 
-static void start_thread(struct fuse_thr_data *d)
+static void start_thread(struct fuse_worker *w)
 {
     pthread_t thrid;
     sigset_t oldset;
@@ -39,7 +80,7 @@
     /* Disallow signal reception in worker threads */
     sigfillset(&newset);
     pthread_sigmask(SIG_SETMASK, &newset, &oldset);
-    res = pthread_create(&thrid, NULL, do_work, d);
+    res = pthread_create(&thrid, NULL, do_work, w);
     pthread_sigmask(SIG_SETMASK, &oldset, NULL);
     if(res != 0) {
         fprintf(stderr, "Error creating thread: %s\n", strerror(res));
@@ -50,19 +91,47 @@
 
 void __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data)
 {
+    struct fuse_worker *head;
+
+    head = malloc(sizeof(struct fuse_worker));
+    head->next = head;
+    head->prev = head;
+
     while(1) {
-        struct fuse_thr_data *d;
+        struct fuse_worker *w;
         struct fuse_cmd *cmd = __fuse_read_cmd(f);
         if(cmd == NULL)
             exit(1);
 
-        d = malloc(sizeof(struct fuse_thr_data));
-        d->proc = proc;
-        d->f = f;
-        d->cmd = cmd;
-        d->data = data;
-        
-        start_thread(d);
+        pthread_mutex_lock(&fuse_mt_lock);
+        for(w = head->next; w != head; w = w->next) 
+            if(w->avail)
+                break;
+
+        if(w != head) {
+            pthread_cond_signal(&w->start);
+            w->cmd = cmd;
+            w = NULL;
+        }
+        else {
+            struct fuse_worker *prev = head->prev;
+            struct fuse_worker *next = head;
+            w = malloc(sizeof(struct fuse_worker));
+            w->prev = prev;
+            w->next = next;
+            next->prev = w;
+            prev->next = w;
+            w->f = f;
+            w->data = data;
+            w->proc = proc;
+            w->cmd = cmd;
+            w->avail = 0;
+            pthread_cond_init(&w->start, NULL);
+        }
+        pthread_mutex_unlock(&fuse_mt_lock);
+
+        if(w != NULL)
+            start_thread(w);
     }
 }