interrupted request improvements
diff --git a/ChangeLog b/ChangeLog
index 7b46d61..0395c38 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2005-01-04  Miklos Szeredi <miklos@szeredi.hu>
+
+	* KERNEL: if request is interrupted, still keep reference to used
+	inode(s) and file, so that FORGET and RELEASE are not sent until
+	userspace finishes the request.
+
+2004-12-16  Miklos Szeredi <miklos@szeredi.hu>
+
+	* KERNEL ABI: update interface to make it independent of type
+	sizes.  This will help on 64 bit architectures which can run
+	legacy 32 bit applications.
+
+	* KERNEL ABI: add "len" field to request headers.  This will allow
+	sending/receiving requests in multiple chunks.
+
+	* KERNEL: handle file type change more intelligently
+
+	* LIB: "-o debug" option should disable backgrounding (fix by
+	Fabien Reygrobellet)
+
 2004-12-13  Miklos Szeredi <miklos@szeredi.hu>
 
 	* KERNEL: invalidate dentry/attributes if interrupted request
diff --git a/Filesystems b/Filesystems
index 553b714..6e147e5 100644
--- a/Filesystems
+++ b/Filesystems
@@ -1,6 +1,6 @@
 Name: OW
 
-Author: Paul H. Alfille     palfille at partners org
+Author: Paul H. Alfille / palfille at partners org
 
 Homepage: http://owfs.sourceforge.net
 
@@ -12,7 +12,7 @@
 ==============================================================================
 Name: FunFS (status: alpha)
 
-Author: Michael Grigoriev (Net Integration Technologies)     mag at luminal org
+Author: Michael Grigoriev (Net Integration Technologies) / mag at luminal org
 
 Homepage: http://www.luminal.org/wiki/index.php/FunFS/FunFS
 
@@ -24,7 +24,7 @@
 ==============================================================================
 Name: EncFS
 
-Author:  Valient Gough     vgough at pobox com
+Author:  Valient Gough / vgough at pobox com
 
 Homepage: http://pobox.com/~vgough/encfs.html
 
@@ -37,10 +37,12 @@
 ==============================================================================
 Name: FUSE-J
 
-Author: Peter Levart     peter.levart at select-tech si
+Author: Peter Levart / peter.levart at select-tech si
 
 Download: http://www.select-tech.si/fuse/
 
+Alternate download: http://www.cl.cam.ac.uk/~tdm25/fuse-j/
+
 Description: 
 
   FUSE-J provides Java binding for FUSE.  It comes with the
@@ -49,7 +51,7 @@
 ==============================================================================
 Name: SMB for FUSE
 
-Author: Vincent Wagelaar     vincent at ricardis tudelft nl
+Author: Vincent Wagelaar / vincent at ricardis tudelft nl
 
 Homepage: http://hannibal.lr-s.tudelft.nl/~vincent/fusesmb/
 
@@ -61,7 +63,7 @@
 ==============================================================================
 Name: Run-Time-Access
 
-Author: Bob Smith     bsmith at linuxtoys org
+Author: Bob Smith / bsmith at linuxtoys org
 
 Homepage: http://www.runtimeaccess.com
 
@@ -76,7 +78,7 @@
 ==============================================================================
 Name: PhoneBook
 
-Author: David McNab     david at rebirthing co nz
+Author: David McNab / david at rebirthing co nz
 
 Homepage: http://www.freenet.org.nz/phonebook
 
@@ -91,7 +93,7 @@
 ==============================================================================
 Name: KIO Fuse Gateway
 
-Author: Alexander Neundorf     neundorf at kde org
+Author: Alexander Neundorf / neundorf at kde org
 
 Homepage: http://kde.ground.cz/tiki-index.php?page=KIO+Fuse+Gateway
 
@@ -104,7 +106,7 @@
 ==============================================================================
 Name: C# bindings
 
-Author: Valient Gough     vgough at pobox com
+Author: Valient Gough / vgough at pobox com
 
 Homepage: http://pobox.com/~vgough/fuse-csharp.html
 
@@ -117,7 +119,7 @@
 ==============================================================================
 Name: LUFS bridge (alpha)
 
-Author: Miklos Szeredi     miklos at szeredi hu
+Author: Miklos Szeredi / miklos at szeredi hu
 
 Homepage: http://sourceforge.net/project/showfiles.php?group_id=121684&package_id=132803
 
@@ -130,7 +132,7 @@
 ==============================================================================
 Name: btfs (Bluetooth FileSystemMapping)
 
-Author: Collin R. Mulliner     collin at betaversion net
+Author: Collin R. Mulliner / collin at betaversion net
 
 Homepage: http://www.mulliner.org/bluetooth/btfs.php
 
@@ -144,7 +146,7 @@
 ==============================================================================
 Name: mcachefs
 
-Author: Michael Still     mikal at stillhq com
+Author: Michael Still / mikal at stillhq com
 
 Homepage: http://lists.samba.org/archive/linux/2004-March/010211.html
 
@@ -159,7 +161,7 @@
 ==============================================================================
 Name: Fusedav
 
-Author: Lennart Poettering     mzshfrqni at 0pointer de
+Author: Lennart Poettering / mzshfrqni at 0pointer de
 
 Homepage: http://0pointer.de/lennart/projects/fusedav/
 
@@ -172,7 +174,7 @@
 ==============================================================================
 Name: RelFS
 
-Author: Vincenzo Ciancia     vincenzo_ml at yahoo it
+Author: Vincenzo Ciancia / vincenzo_ml at yahoo it
 
 Homepage: http://relfs.sourceforge.net/
 
@@ -186,7 +188,7 @@
 ==============================================================================
 Name: GmailFS
 
-Author: Richard Jones     richard at jones name
+Author: Richard Jones / richard at jones name
 
 Homepage: http://richard.jones.name/google-hacks/gmail-filesystem/gmail-filesystem.html
 
@@ -200,7 +202,7 @@
 ==============================================================================
 Name: DataDraw
 
-Author: Bill Cox     bill at viasic com
+Author: Bill Cox / bill at viasic com
 
 Homepage: http://www.viasic.com/opensource/
 
@@ -212,7 +214,7 @@
 ==============================================================================
 Name: gphoto2-fuse-fs
 
-Author: Christopher Lester     lester at hep phy cam ac uk
+Author: Christopher Lester / lester at hep phy cam ac uk
 
 Homepage: http://www.hep.phy.cam.ac.uk/~lester/gphoto2-fuse-fs/
 
@@ -226,7 +228,7 @@
 ==============================================================================
 Name: cvsfs-fuse
 
-Author: Patrick Frank     pfrank at gmx de
+Author: Patrick Frank / pfrank at gmx de
 
 Homepage: http://sourceforge.net/projects/cvsfs
 
@@ -240,7 +242,7 @@
 ==============================================================================
 Name: Wayback (User-level Versioning File System for Linux)
 
-Author: Brian Cornell     techie at northwestern edu
+Author: Brian Cornell / techie at northwestern edu
 
 Homepage: http://wayback.sourceforge.net/
 
@@ -254,3 +256,18 @@
   directory.
 
 ==============================================================================
+Name: Trivial Rolebased Authorisation & Capability Statemachine (TRACS)
+
+Author: Rob J Meijer / rmeijer at xs4all nl
+
+Homepage: http://www.xs4all.nl/~rmeijer/tracs.html
+
+Description:
+
+  This project is the first spin-off project of the Security Incident
+  Policy Enforcement System project. In the process of designing a
+  SIPES, the need was recognized for the implementation of an
+  authorisation server that provides functionality not provided by any
+  of the current authorisation solutions.
+
+==============================================================================
diff --git a/kernel/dev.c b/kernel/dev.c
index 3f2d85a..bfd2bd7 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -55,15 +55,6 @@
 	kmem_cache_free(fuse_req_cachep, req);
 }
 
-static int get_unique(struct fuse_conn *fc)
-{
-	fc->reqctr++;
-	/* zero is special */
-	if (fc->reqctr == 0)
-		fc->reqctr = 1;
-	return fc->reqctr;
-}
-
 #ifdef KERNEL_2_6
 static inline void block_sigs(sigset_t *oldset)
 {
@@ -112,6 +103,7 @@
 /* Must be called with > 1 refcount */
 static void __fuse_put_request(struct fuse_req *req)
 {
+	BUG_ON(atomic_read(&req->count) < 2);
 	atomic_dec(&req->count);
 }
 
@@ -134,7 +126,7 @@
 
 struct fuse_req *fuse_get_request(struct fuse_conn *fc)
 {
-	if (down_interruptible(&fc->unused_sem))
+	if (down_interruptible(&fc->outstanding_sem))
 		return NULL;
 	return  do_get_request(fc);
 }
@@ -145,7 +137,7 @@
 	sigset_t oldset;
 
 	block_sigs(&oldset);
-	intr = down_interruptible(&fc->unused_sem);
+	intr = down_interruptible(&fc->outstanding_sem);
 	restore_sigs(&oldset);
 	return intr ? NULL : do_get_request(fc);
 }
@@ -154,12 +146,16 @@
 {
 	if (!req->preallocated)
 		fuse_request_free(req);
-	else {
-		spin_lock(&fuse_lock);
+
+	spin_lock(&fuse_lock);
+	if (req->preallocated)
 		list_add(&req->list, &fc->unused_list);
-		spin_unlock(&fuse_lock);
-		up(&fc->unused_sem);
-	}
+
+	if (fc->outstanding_debt)
+		fc->outstanding_debt--;
+	else
+		up(&fc->outstanding_sem);
+	spin_unlock(&fuse_lock);
 }
 
 void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
@@ -175,6 +171,14 @@
 	req->finished = 1;
 	putback = atomic_dec_and_test(&req->count);
 	spin_unlock(&fuse_lock);
+	if (req->background) {
+		if (req->inode)
+			iput(req->inode);
+		if (req->inode2)
+			iput(req->inode2);
+		if (req->file)
+			fput(req->file);
+	}
 	wake_up(&req->waitq);
 	if (putback)
 		fuse_putback_request(fc, req);
@@ -190,9 +194,21 @@
 	return err;
 }
 
-/* Called with fuse_lock held.  Releases, and then reaquires it. */
-static void request_wait_answer(struct fuse_req *req, int interruptible,
-				int background)
+static void background_request(struct fuse_req *req)
+{
+	/* Need to get hold of the inode(s) and/or file used in the
+	   request, so FORGET and RELEASE are not sent too early */
+	req->background = 1;
+	if (req->inode)
+		req->inode = igrab(req->inode);
+	if (req->inode2)
+		req->inode2 = igrab(req->inode2);
+	if (req->file)
+		get_file(req->file);
+}
+
+/* Called with fuse_lock held.  Releases, and then reacquires it. */
+static void request_wait_answer(struct fuse_req *req, int interruptible)
 {
 	int intr;
 
@@ -212,10 +228,6 @@
 	if (!intr)
 		return;
 
-	if (background && !req->sent) {
-		req->isreply = 0;
-		return;
-	}
 	if (!interruptible || req->sent)
 		req->out.h.error = -EINTR;
 	else
@@ -232,50 +244,74 @@
 		wait_event(req->waitq, !req->locked);
 		spin_lock(&fuse_lock);
 	}
-	if (!list_empty(&req->list)) {
-		/* request is still on one of the lists */
+	if (!req->sent && !list_empty(&req->list)) {
 		list_del(&req->list);
 		__fuse_put_request(req);
+	} else if (req->sent)
+		background_request(req);
+}
+
+static unsigned len_args(unsigned numargs, struct fuse_arg *args)
+{
+	unsigned nbytes = 0;
+	unsigned i;
+
+	for (i = 0; i < numargs; i++)
+		nbytes += args[i].size;
+
+	return nbytes;
+}
+
+static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
+{
+	fc->reqctr++;
+	/* zero is special */
+	if (fc->reqctr == 0)
+		fc->reqctr = 1;
+	req->in.h.unique = fc->reqctr;
+	req->in.h.len = sizeof(struct fuse_in_header) + 
+		len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
+	if (!req->preallocated) {
+		/* decrease outstanding_sem, but without blocking... */
+		if (down_trylock(&fc->outstanding_sem))
+			fc->outstanding_debt++;
 	}
+	list_add_tail(&req->list, &fc->pending);
+	wake_up(&fc->waitq);
 }
 
 static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
-			      int interruptible, int background)
+			      int interruptible)
 {
 	req->isreply = 1;
 	spin_lock(&fuse_lock);
 	req->out.h.error = -ENOTCONN;
 	if (fc->file) {
-		req->in.h.unique = get_unique(fc);
-		list_add_tail(&req->list, &fc->pending);
-		wake_up(&fc->waitq);
+		queue_request(fc, req);
 		/* acquire extra reference, since request is still needed
 		   after request_end() */
 		__fuse_get_request(req);
 
-		request_wait_answer(req, interruptible, background);
+		request_wait_answer(req, interruptible);
 	}
 	spin_unlock(&fuse_lock);
 }
 
 void request_send(struct fuse_conn *fc, struct fuse_req *req)
 {
-	request_send_wait(fc, req, 1, 0);
+	request_send_wait(fc, req, 1);
 }
 
-void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
-			 int background)
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req)
 {
-	request_send_wait(fc, req, 0, background);
+	request_send_wait(fc, req, 0);
 }
 
-void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
 {
-	req->isreply = 0;
 	spin_lock(&fuse_lock);
 	if (fc->file) {
-		list_add_tail(&req->list, &fc->pending);
-		wake_up(&fc->waitq);
+		queue_request(fc, req);
 		spin_unlock(&fuse_lock);
 	} else {
 		req->out.h.error = -ENOTCONN;
@@ -283,6 +319,19 @@
 	}
 }
 
+void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
+{
+	req->isreply = 0;
+	request_send_nowait(fc, req);
+}
+
+void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
+{
+	req->isreply = 1;
+	background_request(req);
+	request_send_nowait(fc, req);
+}
+
 static inline int lock_request(struct fuse_req *req)
 {
 	int err = 0;
@@ -486,17 +535,6 @@
 	return err;
 }
 
-static unsigned len_args(unsigned numargs, struct fuse_arg *args)
-{
-	unsigned nbytes = 0;
-	unsigned i;
-
-	for (i = 0; i < numargs; i++)
-		nbytes += args[i].size;
-
-	return nbytes;
-}
-
 static void request_wait(struct fuse_conn *fc)
 {
 	DECLARE_WAITQUEUE(wait, current);
@@ -544,8 +582,7 @@
 	spin_unlock(&fuse_lock);
 
 	in = &req->in;
-	reqsize = sizeof(struct fuse_in_header);
-	reqsize += len_args(in->numargs, (struct fuse_arg *) in->args);
+	reqsize = req->in.h.len;
 	nbytes = fuse_copy_init(&cs, 1, req, iov, nr_segs);
 	err = -EINVAL;
 	if (nbytes >= reqsize) {
@@ -652,16 +689,22 @@
 	if (err)
 		goto err_finish;
 	err = -EINVAL;
-	if (!oh.unique || oh.error <= -1000 || oh.error > 0)
+	if (!oh.unique || oh.error <= -1000 || oh.error > 0 || 
+	    oh.len != nbytes)
 		goto err_finish;
 
 	spin_lock(&fuse_lock);
 	req = request_find(fc, oh.unique);
-	err = -ENOENT;
+	err = -EINVAL;
 	if (!req)
 		goto err_unlock;
 
 	list_del_init(&req->list);
+	if (req->interrupted) {
+		request_end(fc, req);
+		fuse_copy_finish(&cs);
+		return -ENOENT;
+	}
 	req->out.h = oh;
 	req->locked = 1;
 	cs.req = req;
diff --git a/kernel/dir.c b/kernel/dir.c
index 19aebb1..08427a9 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -17,174 +17,6 @@
 #endif
 #include <linux/sched.h>
 
-static struct inode_operations fuse_dir_inode_operations;
-static struct inode_operations fuse_file_inode_operations;
-static struct inode_operations fuse_symlink_inode_operations;
-static struct file_operations fuse_dir_operations;
-static struct dentry_operations fuse_dentry_operations;
-
-#ifndef KERNEL_2_6
-#define new_decode_dev(x) (x)
-#define new_encode_dev(x) (x)
-#endif
-static void change_attributes(struct inode *inode, struct fuse_attr *attr)
-{
-	if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) {
-#ifdef KERNEL_2_6
-		invalidate_inode_pages(inode->i_mapping);
-#else
-		invalidate_inode_pages(inode);
-#endif
-	}
-
-	inode->i_ino     = attr->ino;
-	inode->i_mode    = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
-	inode->i_nlink   = attr->nlink;
-	inode->i_uid     = attr->uid;
-	inode->i_gid     = attr->gid;
-	i_size_write(inode, attr->size);
-	inode->i_blksize = PAGE_CACHE_SIZE;
-	inode->i_blocks  = attr->blocks;
-#ifdef KERNEL_2_6
-	inode->i_atime.tv_sec   = attr->atime;
-	inode->i_atime.tv_nsec  = attr->atimensec;
-	inode->i_mtime.tv_sec   = attr->mtime;
-	inode->i_mtime.tv_nsec  = attr->mtimensec;
-	inode->i_ctime.tv_sec   = attr->ctime;
-	inode->i_ctime.tv_nsec  = attr->ctimensec;
-#else
-	inode->i_atime   = attr->atime;
-	inode->i_mtime   = attr->mtime;
-	inode->i_ctime   = attr->ctime;
-#endif
-}
-
-static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
-{
-	inode->i_mode = attr->mode & S_IFMT;
-	i_size_write(inode, attr->size);
-	if (S_ISREG(inode->i_mode)) {
-		inode->i_op = &fuse_file_inode_operations;
-		fuse_init_file_inode(inode);
-	}
-	else if (S_ISDIR(inode->i_mode)) {
-		inode->i_op = &fuse_dir_inode_operations;
-		inode->i_fop = &fuse_dir_operations;
-	}
-	else if (S_ISLNK(inode->i_mode)) {
-		inode->i_op = &fuse_symlink_inode_operations;
-	}
-	else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
-		 S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
-		inode->i_op = &fuse_file_inode_operations;
-		init_special_inode(inode, inode->i_mode,
-				   new_decode_dev(attr->rdev));
-	} else {
-		/* Don't let user create weird files */
-		inode->i_mode = S_IFREG;
-		inode->i_op = &fuse_file_inode_operations;
-		fuse_init_file_inode(inode);
-	}
-}
-
-#ifdef KERNEL_2_6
-static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
-{
-	unsigned long nodeid = *(unsigned long *) _nodeidp;
-	if (get_node_id(inode) == nodeid)
-		return 1;
-	else
-		return 0;
-}
-
-static int fuse_inode_set(struct inode *inode, void *_nodeidp)
-{
-	unsigned long nodeid = *(unsigned long *) _nodeidp;
-	get_fuse_inode(inode)->nodeid = nodeid;
-	return 0;
-}
-
-struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
-			int generation, struct fuse_attr *attr, int version)
-{
-	struct inode *inode;
-	struct fuse_conn *fc = get_fuse_conn_super(sb);
-
-	inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
-	if (!inode)
-		return NULL;
-
-	if ((inode->i_state & I_NEW)) {
-		inode->i_generation = generation;
-		inode->i_data.backing_dev_info = &fc->bdi;
-		fuse_init_inode(inode, attr);
-		unlock_new_inode(inode);
-	}
-
-	change_attributes(inode, attr);
-	inode->i_version = version;
-	return inode;
-}
-
-struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid)
-{
-	return ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
-}
-#else
-static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){
-	unsigned long nodeid = *(unsigned long *) _nodeidp;
-	if (inode->u.generic_ip && get_node_id(inode) == nodeid)
-		return 1;
-	else
-		return 0;
-}
-
-struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
-			int generation, struct fuse_attr *attr, int version)
-{
-	struct inode *inode;
-
-	inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid);
-	if (!inode)
-		return NULL;
-
-	if (!inode->u.generic_ip) {
-		get_fuse_inode(inode)->nodeid = nodeid;
-		inode->u.generic_ip = inode;
-		inode->i_generation = generation;
-		fuse_init_inode(inode, attr);
-	}
-
-	change_attributes(inode, attr);
-	inode->i_version = version;
-	return inode;
-}
-
-struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long nodeid)
-{
-	struct inode *inode = iget4(sb, ino, fuse_inode_eq, &nodeid);
-	if (inode && !inode->u.generic_ip) {
-		iput(inode);
-		inode = NULL;
-	}
-	return inode;
-}
-#endif
-
-static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
-			     struct dentry *entry,
-			     struct fuse_entry_out *outarg)
-{
-	req->in.h.opcode = FUSE_LOOKUP;
-	req->in.h.nodeid = get_node_id(dir);
-	req->in.numargs = 1;
-	req->in.args[0].size = entry->d_name.len + 1;
-	req->in.args[0].value = entry->d_name.name;
-	req->out.numargs = 1;
-	req->out.args[0].size = sizeof(struct fuse_entry_out);
-	req->out.args[0].value = outarg;
-}
-
 static inline unsigned long time_to_jiffies(unsigned long sec,
 					    unsigned long nsec)
 {
@@ -195,6 +27,69 @@
 	return jiffies + sec * HZ + nsec / (1000000000 / HZ);
 }
 
+static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
+			     struct dentry *entry,
+			     struct fuse_entry_out *outarg)
+{
+	req->in.h.opcode = FUSE_LOOKUP;
+	req->in.h.nodeid = get_node_id(dir);
+	req->inode = dir;
+	req->in.numargs = 1;
+	req->in.args[0].size = entry->d_name.len + 1;
+	req->in.args[0].value = entry->d_name.name;
+	req->out.numargs = 1;
+	req->out.args[0].size = sizeof(struct fuse_entry_out);
+	req->out.args[0].value = outarg;
+}
+
+static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
+{
+	if (!entry->d_inode || is_bad_inode(entry->d_inode))
+		return 0;
+	else if (entry->d_time && time_after(jiffies, entry->d_time)) {
+		int err;
+		int version;
+		struct fuse_entry_out outarg;
+		struct inode *inode = entry->d_inode;
+		struct fuse_inode *fi = get_fuse_inode(inode);
+		struct fuse_conn *fc = get_fuse_conn(inode);
+		struct fuse_req *req = fuse_get_request_nonint(fc);
+		if (!req)
+			return 0;
+
+		fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
+		request_send_nonint(fc, req);
+		version = req->out.h.unique;
+		err = req->out.h.error;
+		fuse_put_request(fc, req);
+		if (err || outarg.nodeid != get_node_id(inode) ||
+		    (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
+			return 0;
+
+		fuse_change_attributes(inode, &outarg.attr);
+		inode->i_version = version;
+		entry->d_time = time_to_jiffies(outarg.entry_valid,
+						outarg.entry_valid_nsec);
+		fi->i_time = time_to_jiffies(outarg.attr_valid,
+					     outarg.attr_valid_nsec);
+	}
+	return 1;
+}
+#ifndef KERNEL_2_6
+static int fuse_dentry_revalidate_2_4(struct dentry *entry, int flags)
+{
+	return fuse_dentry_revalidate(entry, NULL);
+}
+#endif
+
+static struct dentry_operations fuse_dentry_operations = {
+#ifdef KERNEL_2_6
+	.d_revalidate	= fuse_dentry_revalidate,
+#else
+	.d_revalidate	= fuse_dentry_revalidate_2_4,
+#endif
+};
+
 static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
 			    struct inode **inodep)
 {
@@ -207,6 +102,7 @@
 
 	if (entry->d_name.len > FUSE_NAME_MAX)
 		return -ENAMETOOLONG;
+
 	req = fuse_get_request(fc);
 	if (!req)
 		return -ERESTARTNOINTR;
@@ -262,6 +158,7 @@
 	int err;
 
 	req->in.h.nodeid = get_node_id(dir);
+	req->inode = dir;
 	req->out.numargs = 1;
 	req->out.args[0].size = sizeof(outarg);
 	req->out.args[0].value = &outarg;
@@ -377,6 +274,7 @@
 
 	req->in.h.opcode = FUSE_UNLINK;
 	req->in.h.nodeid = get_node_id(dir);
+	req->inode = dir;
 	req->in.numargs = 1;
 	req->in.args[0].size = entry->d_name.len + 1;
 	req->in.args[0].value = entry->d_name.name;
@@ -407,6 +305,7 @@
 
 	req->in.h.opcode = FUSE_RMDIR;
 	req->in.h.nodeid = get_node_id(dir);
+	req->inode = dir;
 	req->in.numargs = 1;
 	req->in.args[0].size = entry->d_name.len + 1;
 	req->in.args[0].value = entry->d_name.name;
@@ -435,6 +334,8 @@
 	inarg.newdir = get_node_id(newdir);
 	req->in.h.opcode = FUSE_RENAME;
 	req->in.h.nodeid = get_node_id(olddir);
+	req->inode = olddir;
+	req->inode2 = newdir;
 	req->in.numargs = 3;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -454,8 +355,7 @@
 		   rename actually took place.  If the invalidation
 		   fails (e.g. some process has CWD under the renamed
 		   directory), then there can be inconsistency between
-		   the dcache and the real filesystem.  But not a lot
-		   can be done about that */
+		   the dcache and the real filesystem.  Tough luck. */
 		fuse_invalidate_entry(oldent);
 		if (newent->d_inode)
 			fuse_invalidate_entry(newent);
@@ -478,6 +378,7 @@
 	memset(&inarg, 0, sizeof(inarg));
 	inarg.newdir = get_node_id(newdir);
 	req->in.h.opcode = FUSE_LINK;
+	req->inode2 = newdir;
 	req->in.numargs = 2;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -506,6 +407,7 @@
 
 	req->in.h.opcode = FUSE_GETATTR;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->out.numargs = 1;
 	req->out.args[0].size = sizeof(arg);
 	req->out.args[0].value = &arg;
@@ -513,10 +415,15 @@
 	err = req->out.h.error;
 	fuse_put_request(fc, req);
 	if (!err) {
-		struct fuse_inode *fi = get_fuse_inode(inode);
-		change_attributes(inode, &arg.attr);
-		fi->i_time = time_to_jiffies(arg.attr_valid,
-					     arg.attr_valid_nsec);
+		if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) {
+			make_bad_inode(inode);
+			err = -EIO;
+		} else {
+			struct fuse_inode *fi = get_fuse_inode(inode);
+			fuse_change_attributes(inode, &arg.attr);
+			fi->i_time = time_to_jiffies(arg.attr_valid,
+						     arg.attr_valid_nsec);
+		}
 	}
 	return err;
 }
@@ -594,7 +501,7 @@
 		struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
 		size_t reclen = FUSE_DIRENT_SIZE(dirent);
 		int over;
-		if (dirent->namelen > NAME_MAX)
+		if (dirent->namelen > FUSE_NAME_MAX)
 			return -EIO;
 		if (reclen > nbytes)
 			break;
@@ -640,6 +547,7 @@
 
 	req->in.h.opcode = FUSE_GETDIR;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->out.numargs = 1;
 	req->out.args[0].size = sizeof(struct fuse_getdir_out);
 	req->out.args[0].value = &outarg;
@@ -694,6 +602,7 @@
 	}
 	req->in.h.opcode = FUSE_READLINK;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->out.argvar = 1;
 	req->out.numargs = 1;
 	req->out.args[0].size = PAGE_SIZE - 1;
@@ -823,6 +732,7 @@
 	inarg.valid = iattr_to_fattr(attr, &inarg.attr);
 	req->in.h.opcode = FUSE_SETATTR;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -833,57 +743,26 @@
 	err = req->out.h.error;
 	fuse_put_request(fc, req);
 	if (!err) {
-		if (is_truncate) {
-			loff_t origsize = i_size_read(inode);
-			i_size_write(inode, outarg.attr.size);
-			if (origsize > outarg.attr.size)
-				vmtruncate(inode, outarg.attr.size);
+		if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+			make_bad_inode(inode);
+			err = -EIO;
+		} else {
+			if (is_truncate) {
+				loff_t origsize = i_size_read(inode);
+				i_size_write(inode, outarg.attr.size);
+				if (origsize > outarg.attr.size)
+					vmtruncate(inode, outarg.attr.size);
+			}
+			fuse_change_attributes(inode, &outarg.attr);
+			fi->i_time = time_to_jiffies(outarg.attr_valid,
+						     outarg.attr_valid_nsec);
 		}
-		change_attributes(inode, &outarg.attr);
-		fi->i_time = time_to_jiffies(outarg.attr_valid,
-					     outarg.attr_valid_nsec);
 	} else if (err == -EINTR)
 		fuse_invalidate_attr(inode);
 
 	return err;
 }
 
-static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
-{
-	if (!entry->d_inode)
-		return 0;
-	else if (entry->d_time && time_after(jiffies, entry->d_time)) {
-		int err;
-		int version;
-		struct fuse_entry_out outarg;
-		struct inode *inode = entry->d_inode;
-		struct fuse_inode *fi = get_fuse_inode(inode);
-		struct fuse_conn *fc = get_fuse_conn(inode);
-		struct fuse_req *req = fuse_get_request_nonint(fc);
-		if (!req)
-			return 0;
-
-		fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
-		request_send_nonint(fc, req, 0);
-		version = req->out.h.unique;
-		err = req->out.h.error;
-		fuse_put_request(fc, req);
-		if (err)
-			return 0;
-
-		if (outarg.nodeid != get_node_id(inode))
-			return 0;
-
-		change_attributes(inode, &outarg.attr);
-		inode->i_version = version;
-		entry->d_time = time_to_jiffies(outarg.entry_valid,
-						outarg.entry_valid_nsec);
-		fi->i_time = time_to_jiffies(outarg.attr_valid,
-					     outarg.attr_valid_nsec);
-	}
-	return 1;
-}
-
 #ifdef KERNEL_2_6
 static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
 			struct kstat *stat)
@@ -932,11 +811,6 @@
 	return fuse_mknod(dir, entry, mode, rdev);
 }
 
-static int fuse_dentry_revalidate_2_4(struct dentry *entry, int flags)
-{
-	return fuse_dentry_revalidate(entry, NULL);
-}
-
 static int fuse_create_2_4(struct inode *dir, struct dentry *entry, int mode)
 {
 	return fuse_create(dir, entry, mode, NULL);
@@ -978,6 +852,7 @@
 	inarg.flags = flags;
 	req->in.h.opcode = FUSE_SETXATTR;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->in.numargs = 3;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -1016,6 +891,7 @@
 	inarg.size = size;
 	req->in.h.opcode = FUSE_GETXATTR;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->in.numargs = 2;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -1065,6 +941,7 @@
 	inarg.size = size;
 	req->in.h.opcode = FUSE_LISTXATTR;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -1108,6 +985,7 @@
 
 	req->in.h.opcode = FUSE_REMOVEXATTR;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->in.numargs = 1;
 	req->in.args[0].size = strlen(name) + 1;
 	req->in.args[0].value = name;
@@ -1157,7 +1035,7 @@
 	.release	= fuse_dir_release,
 };
 
-static struct inode_operations fuse_file_inode_operations = {
+static struct inode_operations fuse_common_inode_operations = {
 	.setattr	= fuse_setattr,
 #ifdef KERNEL_2_6
 	.permission	= fuse_permission,
@@ -1191,10 +1069,18 @@
 #endif
 };
 
-static struct dentry_operations fuse_dentry_operations = {
-#ifdef KERNEL_2_6
-	.d_revalidate	= fuse_dentry_revalidate,
-#else
-	.d_revalidate	= fuse_dentry_revalidate_2_4,
-#endif
-};
+void fuse_init_common(struct inode *inode)
+{
+	inode->i_op = &fuse_common_inode_operations;
+}
+
+void fuse_init_dir(struct inode *inode)
+{
+	inode->i_op = &fuse_dir_inode_operations;
+	inode->i_fop = &fuse_dir_operations;
+}
+
+void fuse_init_symlink(struct inode *inode)
+{
+	inode->i_op = &fuse_symlink_inode_operations;
+}
diff --git a/kernel/file.c b/kernel/file.c
index aa02651..b7359c5 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -37,12 +37,9 @@
 		 	return err;
 	}
 
-	/* Prevent concurrent unlink or rename */
-	down(&inode->i_sem);
-	err = -ERESTARTSYS;
 	req = fuse_get_request(fc);
 	if (!req)
-		goto out;
+		return -ERESTARTSYS;
 
 	err = -ENOMEM;
 	ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
@@ -56,9 +53,10 @@
 	}
 
 	memset(&inarg, 0, sizeof(inarg));
-	inarg.flags = file->f_flags & ~O_EXCL;
+	inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 	req->in.h.opcode = FUSE_OPEN;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -85,8 +83,6 @@
 
  out_put_request:
 	fuse_put_request(fc, req);
- out:
-	up(&inode->i_sem);
 	return err;
 }
 
@@ -101,11 +97,11 @@
 	inarg->flags = file->f_flags & ~O_EXCL;
 	req->in.h.opcode = FUSE_RELEASE;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(struct fuse_release_in);
 	req->in.args[0].value = inarg;
-	request_send_nonint(fc, req, 1);
-	fuse_put_request(fc, req);
+	request_send_background(fc, req);
 	kfree(ff);
 
 	/* Return value is ignored by VFS */
@@ -132,10 +128,12 @@
 	inarg.fh = ff->fh;
 	req->in.h.opcode = FUSE_FLUSH;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
+	req->file = file;
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
-	request_send_nonint(fc, req, 0);
+	request_send_nonint(fc, req);
 	err = req->out.h.error;
 	fuse_put_request(fc, req);
 	if (err == -ENOSYS) {
@@ -163,9 +161,11 @@
 
 	memset(&inarg, 0, sizeof(inarg));
 	inarg.fh = ff->fh;
-	inarg.datasync = datasync;
+	inarg.fsync_flags = datasync ? 1 : 0;
 	req->in.h.opcode = FUSE_FSYNC;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
+	req->file = file;
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(inarg);
 	req->in.args[0].value = &inarg;
@@ -192,6 +192,8 @@
 	inarg.size = count;
 	req->in.h.opcode = FUSE_READ;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
+	req->file = file;
 	req->in.numargs = 1;
 	req->in.args[0].size = sizeof(struct fuse_read_in);
 	req->in.args[0].value = &inarg;
@@ -199,19 +201,19 @@
 	req->out.argvar = 1;
 	req->out.numargs = 1;
 	req->out.args[0].size = count;
-	request_send_nonint(fc, req, 0);
+	request_send_nonint(fc, req);
 	return req->out.args[0].size;
 }
 
 static int fuse_readpage(struct file *file, struct page *page)
 {
-	int err;
 	struct inode *inode = page->mapping->host;
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT;
 	struct fuse_req *req = fuse_get_request_nonint(fc);
+	int err = -EINTR;
 	if (!req)
-		return -EINTR;
+		goto out;
 
 	req->out.page_zeroing = 1;
 	req->num_pages = 1;
@@ -221,6 +223,7 @@
 	fuse_put_request(fc, req);
 	if (!err)
 		SetPageUptodate(page);
+ out:
 	unlock_page(page);
 	return err;
 }
@@ -261,9 +264,10 @@
 	     (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
 	     req->pages[req->num_pages - 1]->index + 1 != page->index)) {
 		int err = fuse_send_readpages(req, data->file, inode);
-		if (err)
+		if (err) {
+			unlock_page(page);
 			return err;
-
+		}
 		fuse_reset_request(req);
 	}
 	req->pages[req->num_pages] = page;
@@ -278,7 +282,6 @@
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	struct fuse_readpages_data data;
 	int err;
-
 	data.file = file;
 	data.inode = inode;
 	data.req = fuse_get_request_nonint(fc);
@@ -287,7 +290,7 @@
 
 	err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
 	if (!err && data.req->num_pages)
-		fuse_send_readpages(data.req, file, inode);
+		err = fuse_send_readpages(data.req, file, inode);
 	fuse_put_request(fc, data.req);
 	return err;
 }
@@ -400,12 +403,13 @@
 	struct fuse_write_out outarg;
 
 	memset(&inarg, 0, sizeof(struct fuse_write_in));
-	inarg.writepage = 0;
 	inarg.fh = ff->fh;
 	inarg.offset = pos;
 	inarg.size = count;
 	req->in.h.opcode = FUSE_WRITE;
 	req->in.h.nodeid = get_node_id(inode);
+	req->inode = inode;
+	req->file = file;
 	req->in.argpages = 1;
 	req->in.numargs = 2;
 	req->in.args[0].size = sizeof(struct fuse_write_in);
@@ -414,7 +418,7 @@
 	req->out.numargs = 1;
 	req->out.args[0].size = sizeof(struct fuse_write_out);
 	req->out.args[0].value = &outarg;
-	request_send_nonint(fc, req, 0);
+	request_send_nonint(fc, req);
 	return outarg.size;
 }
 
@@ -584,6 +588,7 @@
 
 	if (fc->flags & FUSE_DIRECT_IO) {
 		ssize_t res;
+		/* Don't allow parallel writes to the same file */
 		down(&inode->i_sem);
 		res = fuse_direct_io(file, buf, count, ppos, 1);
 		up(&inode->i_sem);
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index a5660b1..9812f31 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -37,6 +37,8 @@
 #     define i_size_read(inode) ((inode)->i_size)
 #     define i_size_write(inode, size) do { (inode)->i_size = size; } while(0)
 #  endif
+#  define new_decode_dev(x) (x)
+#  define new_encode_dev(x) (x)
 #endif /* KERNEL_2_6 */
 #endif /* FUSE_MAINLINE */
 #include <linux/fs.h>
@@ -64,10 +66,10 @@
 	unlock_page(page);
 }
 #endif
-/* Max number of pages that can be used in a single read request */
+/** Max number of pages that can be used in a single read request */
 #define FUSE_MAX_PAGES_PER_REQ 32
 
-/* If more requests are outstanding, then the operation will block */
+/** If more requests are outstanding, then the operation will block */
 #define FUSE_MAX_OUTSTANDING 10
 
 /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
@@ -99,7 +101,7 @@
 struct fuse_inode {
 	/** Unique ID, which identifies the inode between userspace
 	 * and kernel */
-	unsigned long nodeid;
+	u64 nodeid;
 
 	/** The request used for sending the FORGET message */
 	struct fuse_req *forget_req;
@@ -114,7 +116,7 @@
 	struct fuse_req *release_req;
 
 	/** File handle used by userspace */
-	unsigned long fh;
+	u64 fh;
 };
 
 /** One input argument of a request */
@@ -189,6 +191,9 @@
 	/** The request was interrupted */
 	unsigned interrupted:1;
 
+	/** Request is sent in the background */
+	unsigned background:1;
+	
 	/** Data is being copied to/from the request */
 	unsigned locked:1;
 
@@ -221,6 +226,15 @@
 
 	/** offset of data on first page */
 	unsigned page_offset;
+
+	/** Inode used in the request */
+	struct inode *inode;
+	
+	/** Second inode used in the request (or NULL) */
+	struct inode *inode2;
+
+	/** File used in the request (or NULL) */
+	struct file *file;
 };
 
 /**
@@ -259,7 +273,11 @@
 	struct list_head processing;
 
 	/** Controls the maximum number of outstanding requests */
-	struct semaphore unused_sem;
+	struct semaphore outstanding_sem;
+
+	/** This counts the number of outstanding requests if
+	    outstanding_sem would go negative */
+	unsigned outstanding_debt;
 
 	/** The list of unused requests */
 	struct list_head unused_list;
@@ -320,7 +338,7 @@
 	return (struct fuse_inode *) (&inode[1]);
 }
 
-static inline unsigned long get_node_id(struct inode *inode)
+static inline u64 get_node_id(struct inode *inode)
 {
 	return get_fuse_inode(inode)->nodeid;
 }
@@ -349,26 +367,37 @@
 			int generation, struct fuse_attr *attr, int version);
 
 /**
- * Lookup an inode by nodeid
- */
-#ifdef KERNEL_2_6
-struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid);
-#else
-struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long nodeid);
-#endif
-
-/**
  * Send FORGET command
  */
 void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
 		      unsigned long nodeid, int version);
 
 /**
- * Initialise operations on regular file
+ * Initialise file operations on a regular file
  */
 void fuse_init_file_inode(struct inode *inode);
 
 /**
+ * Initialise inode operations on regular files and special files
+ */
+void fuse_init_common(struct inode *inode);
+
+/**
+ * Initialise inode and file operations on a directory
+ */
+void fuse_init_dir(struct inode *inode);
+
+/**
+ * Initialise inode operations on a symlink
+ */
+void fuse_init_symlink(struct inode *inode);
+
+/**
+ * Change attributes of an inode
+ */
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr);
+
+/**
  * Check if the connection can be released, and if yes, then free the
  * connection structure
  */
@@ -431,12 +460,8 @@
 
 /**
  * Send a request (synchronous, non-interruptible except by SIGKILL)
- *
- * If background is non-zero and SIGKILL is received still send
- * request asynchronously
  */
-void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req,
-			 int background);
+void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
  * Send a request with no reply
@@ -444,6 +469,11 @@
 void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
+ * Send a request in the background
+ */
+void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
+
+/**
  * Get the attributes of a file
  */
 int fuse_do_getattr(struct inode *inode);
diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h
index a97c3e8..bad4546 100644
--- a/kernel/fuse_kernel.h
+++ b/kernel/fuse_kernel.h
@@ -8,6 +8,8 @@
 
 /* This file defines the kernel interface of FUSE */
 
+#include <asm/types.h>
+
 /** Version number of this interface */
 #define FUSE_KERNEL_VERSION 5
 
@@ -24,30 +26,30 @@
 #define FUSE_MINOR 229
 
 struct fuse_attr {
-	unsigned long       ino;
-	unsigned int        mode;
-	unsigned int        nlink;
-	unsigned int        uid;
-	unsigned int        gid;
-	unsigned int        rdev;
-	unsigned long long  size;
-	unsigned long       blocks;
-	unsigned long       atime;
-	unsigned long       atimensec;
-	unsigned long       mtime;
-	unsigned long       mtimensec;
-	unsigned long       ctime;
-	unsigned long       ctimensec;
+	__u64	ino;
+	__u64	size;
+	__u64	blocks;
+	__u64	atime;
+	__u64	mtime;
+	__u64	ctime;
+	__u32	atimensec;
+	__u32	mtimensec;
+	__u32	ctimensec;
+	__u32	mode;
+	__u32	nlink;
+	__u32	uid;
+	__u32	gid;
+	__u32	rdev;
 };
 
 struct fuse_kstatfs {
-	unsigned int        bsize;
-	unsigned long long  blocks;
-	unsigned long long  bfree;
-	unsigned long long  bavail;
-	unsigned long long  files;
-	unsigned long long  ffree;
-	unsigned int        namelen;
+	__u64	blocks;
+	__u64	bfree;
+	__u64	bavail;
+	__u64	files;
+	__u64	ffree;
+	__u32	bsize;
+	__u32	namelen;
 };
 
 #define FATTR_MODE	(1 << 0)
@@ -94,86 +96,87 @@
 #define FUSE_XATTR_SIZE_MAX 4096
 
 struct fuse_entry_out {
-	unsigned long nodeid;      /* Inode ID */
-	unsigned long generation;  /* Inode generation: nodeid:gen must
-                                      be unique for the fs's lifetime */
-	unsigned long entry_valid; /* Cache timeout for the name */
-	unsigned long entry_valid_nsec;
-	unsigned long attr_valid;  /* Cache timeout for the attributes */
-	unsigned long attr_valid_nsec;
+	__u64	nodeid;		/* Inode ID */
+	__u64	generation;	/* Inode generation: nodeid:gen must
+				   be unique for the fs's lifetime */
+	__u64	entry_valid;	/* Cache timeout for the name */
+	__u64	attr_valid;	/* Cache timeout for the attributes */
+	__u32	entry_valid_nsec;
+	__u32	attr_valid_nsec;
 	struct fuse_attr attr;
 };
 
 struct fuse_forget_in {
-	int version;
+	__u64	version;
 };
 
 struct fuse_attr_out {
-	unsigned long attr_valid;  /* Cache timeout for the attributes */
-	unsigned long attr_valid_nsec;
+	__u64	attr_valid;	/* Cache timeout for the attributes */
+	__u32	attr_valid_nsec;
+	__u32	dummy;
 	struct fuse_attr attr;
 };
 
 struct fuse_getdir_out {
-	int fd;
+	__u32	fd;
 };
 
 struct fuse_mknod_in {
-	unsigned int mode;
-	unsigned int rdev;
+	__u32	mode;
+	__u32	rdev;
 };
 
 struct fuse_mkdir_in {
-	unsigned int mode;
+	__u32	mode;
 };
 
 struct fuse_rename_in {
-	unsigned long newdir;
+	__u64	newdir;
 };
 
 struct fuse_link_in {
-	unsigned long newdir;
+	__u64	newdir;
 };
 
 struct fuse_setattr_in {
+	__u32	valid;
 	struct fuse_attr attr;
-	unsigned int valid;
 };
 
 struct fuse_open_in {
-	unsigned int flags;
+	__u32	flags;
 };
 
 struct fuse_open_out {
-	unsigned long fh;
-	unsigned int _open_flags;
+	__u64	fh;
+	__u32	open_flags;
 };
 
 struct fuse_release_in {
-	unsigned long fh;
-	unsigned int flags;
+	__u64	fh;
+	__u32	flags;
 };
 
 struct fuse_flush_in {
-	unsigned long fh;
-	unsigned int _nref;
+	__u64	fh;
+	__u32	flush_flags;
 };
 
 struct fuse_read_in {
-	unsigned long fh;
-	unsigned long long offset;
-	unsigned int size;
+	__u64	fh;
+	__u64	offset;
+	__u32	size;
 };
 
 struct fuse_write_in {
-	int writepage;
-	unsigned long fh;
-	unsigned long long offset;
-	unsigned int size;
+	__u64	fh;
+	__u64	offset;
+	__u32	size;
+	__u32	write_flags;
 };
 
 struct fuse_write_out {
-	unsigned int size;
+	__u32	size;
 };
 
 struct fuse_statfs_out {
@@ -181,45 +184,47 @@
 };
 
 struct fuse_fsync_in {
-	unsigned long fh;
-	int datasync;
+	__u64	fh;
+	__u32	fsync_flags;
 };
 
 struct fuse_setxattr_in {
-	unsigned int size;
-	unsigned int flags;
+	__u32	size;
+	__u32	flags;
 };
 
 struct fuse_getxattr_in {
-	unsigned int size;
+	__u32	size;
 };
 
 struct fuse_getxattr_out {
-	unsigned int size;
+	__u32	size;
 };
 
 struct fuse_in_header {
-	int unique;
-	enum fuse_opcode opcode;
-	unsigned long nodeid;
-	unsigned int uid;
-	unsigned int gid;
-	unsigned int pid;
+	__u32	len;
+	__u32	opcode;
+	__u64	unique;
+	__u64	nodeid;
+	__u32	uid;
+	__u32	gid;
+	__u32	pid;
 };
 
 struct fuse_out_header {
-	int unique;
-	int error;
+	__u32	len;
+	__s32	error;
+	__u64	unique;
 };
 
 struct fuse_dirent {
-	unsigned long ino;
-	unsigned short namelen;
-	unsigned char type;
-	char name[256];
+	__u64	ino;
+	__u32	namelen;
+	__u32	type;
+	char name[0];
 };
 
-#define FUSE_NAME_OFFSET ((unsigned int) ((struct fuse_dirent *) 0)->name)
-#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+#define FUSE_NAME_OFFSET ((unsigned) ((struct fuse_dirent *) 0)->name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1))
 #define FUSE_DIRENT_SIZE(d) \
 	FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
diff --git a/kernel/inode.c b/kernel/inode.c
index ac80b63..eb40b7b 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -24,14 +24,19 @@
 #endif
 
 static kmem_cache_t *fuse_inode_cachep;
+static int mount_count;
 
 static int user_allow_other;
+static int mount_max = 1000;
 #ifdef KERNEL_2_6
 module_param(user_allow_other, int, 0644);
+module_param(mount_max, int, 0644);
 #else
 MODULE_PARM(user_allow_other, "i");
+MODULE_PARM(mount_max, "i");
 #endif
 MODULE_PARM_DESC(user_allow_other, "Allow non root user to specify the \"allow_other\" or \"allow_root\" mount options");
+MODULE_PARM_DESC(mount_max, "Maximum number of FUSE mounts allowed, if -1 then unlimited (default: 1000)");
 
 #define FUSE_SUPER_MAGIC 0x65735546
 
@@ -112,11 +117,155 @@
 	}
 }
 
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
+{
+	if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
+#ifdef KERNEL_2_6
+		invalidate_inode_pages(inode->i_mapping);
+#else
+		invalidate_inode_pages(inode);
+#endif
+
+	inode->i_ino     = attr->ino;
+	inode->i_mode    = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
+	inode->i_nlink   = attr->nlink;
+	inode->i_uid     = attr->uid;
+	inode->i_gid     = attr->gid;
+	i_size_write(inode, attr->size);
+	inode->i_blksize = PAGE_CACHE_SIZE;
+	inode->i_blocks  = attr->blocks;
+#ifdef KERNEL_2_6
+	inode->i_atime.tv_sec   = attr->atime;
+	inode->i_atime.tv_nsec  = attr->atimensec;
+	inode->i_mtime.tv_sec   = attr->mtime;
+	inode->i_mtime.tv_nsec  = attr->mtimensec;
+	inode->i_ctime.tv_sec   = attr->ctime;
+	inode->i_ctime.tv_nsec  = attr->ctimensec;
+#else
+	inode->i_atime   = attr->atime;
+	inode->i_mtime   = attr->mtime;
+	inode->i_ctime   = attr->ctime;
+#endif
+}
+
+static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
+{
+	inode->i_mode = attr->mode & S_IFMT;
+	i_size_write(inode, attr->size);
+	if (S_ISREG(inode->i_mode)) {
+		fuse_init_common(inode);
+		fuse_init_file_inode(inode);
+	} else if (S_ISDIR(inode->i_mode))
+		fuse_init_dir(inode);
+	else if (S_ISLNK(inode->i_mode))
+		fuse_init_symlink(inode);
+	else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
+		 S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+		fuse_init_common(inode);
+		init_special_inode(inode, inode->i_mode,
+				   new_decode_dev(attr->rdev));
+	} else {
+		/* Don't let user create weird files */
+		inode->i_mode = S_IFREG;
+		fuse_init_common(inode);
+		fuse_init_file_inode(inode);
+	}
+}
+
+#ifdef KERNEL_2_6
+static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
+{
+	unsigned long nodeid = *(unsigned long *) _nodeidp;
+	if (get_node_id(inode) == nodeid)
+		return 1;
+	else
+		return 0;
+}
+
+static int fuse_inode_set(struct inode *inode, void *_nodeidp)
+{
+	unsigned long nodeid = *(unsigned long *) _nodeidp;
+	get_fuse_inode(inode)->nodeid = nodeid;
+	return 0;
+}
+
+struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
+			int generation, struct fuse_attr *attr, int version)
+{
+	struct inode *inode;
+	struct fuse_conn *fc = get_fuse_conn_super(sb);
+	int retried = 0;
+
+ retry:
+	inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
+	if (!inode)
+		return NULL;
+
+	if ((inode->i_state & I_NEW)) {
+		inode->i_generation = generation;
+		inode->i_data.backing_dev_info = &fc->bdi;
+		fuse_init_inode(inode, attr);
+		unlock_new_inode(inode);
+	} else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
+		BUG_ON(retried);
+		/* Inode has changed type, any I/O on the old should fail */
+		make_bad_inode(inode);
+		iput(inode);
+		retried = 1;
+		goto retry;
+	}
+
+	fuse_change_attributes(inode, attr);
+	inode->i_version = version;
+	return inode;
+}
+#else
+static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){
+	unsigned long nodeid = *(unsigned long *) _nodeidp;
+	if (inode->u.generic_ip && get_node_id(inode) == nodeid)
+		return 1;
+	else
+		return 0;
+}
+
+struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
+			int generation, struct fuse_attr *attr, int version)
+{
+	struct inode *inode;
+	int retried = 0;
+
+ retry:
+	inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid);
+	if (!inode)
+		return NULL;
+
+	if (!inode->u.generic_ip) {
+		get_fuse_inode(inode)->nodeid = nodeid;
+		inode->u.generic_ip = inode;
+		inode->i_generation = generation;
+		fuse_init_inode(inode, attr);
+	} else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
+		BUG_ON(retried);
+		/* Inode has changed type, any I/O on the old should fail */
+		remove_inode_hash(inode);
+		make_bad_inode(inode);
+		iput(inode);
+		retried = 1;
+		goto retry;
+	}
+
+	fuse_change_attributes(inode, attr);
+	inode->i_version = version;
+	return inode;
+}
+#endif
+
 static void fuse_put_super(struct super_block *sb)
 {
 	struct fuse_conn *fc = get_fuse_conn_super(sb);
 
 	spin_lock(&fuse_lock);
+	mount_count --;
 	fc->sb = NULL;
 	fc->uid = 0;
 	fc->flags = 0;
@@ -329,7 +478,7 @@
 		INIT_LIST_HEAD(&fc->pending);
 		INIT_LIST_HEAD(&fc->processing);
 		INIT_LIST_HEAD(&fc->unused_list);
-		sema_init(&fc->unused_sem, FUSE_MAX_OUTSTANDING);
+		sema_init(&fc->outstanding_sem, FUSE_MAX_OUTSTANDING);
 		for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
 			struct fuse_req *req = fuse_request_alloc();
 			if (!req) {
@@ -392,7 +541,7 @@
 	if (nodeid == 0)
 		return ERR_PTR(-ESTALE);
 
-	inode = fuse_ilookup(sb, nodeid);
+	inode = ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
 	if (!inode || inode->i_generation != generation)
 		return ERR_PTR(-ESTALE);
 
@@ -449,12 +598,24 @@
 	.show_options	= fuse_show_options,
 };
 
+static int inc_mount_count(void)
+{
+	int success = 0;
+	spin_lock(&fuse_lock);
+	mount_count ++;
+	if (mount_max == -1 || mount_count <= mount_max)
+		success = 1;
+	spin_unlock(&fuse_lock);
+	return success;
+}
+
 static int fuse_read_super(struct super_block *sb, void *data, int silent)
 {
 	struct fuse_conn *fc;
 	struct inode *root;
 	struct fuse_mount_data d;
 	struct file *file;
+	int err;
 
 	if (!parse_fuse_opt((char *) data, &d))
 		return -EINVAL;
@@ -493,6 +654,11 @@
 
 	*get_fuse_conn_super_p(sb) = fc;
 
+	err = -ENFILE;
+	if (!inc_mount_count() && current->uid != 0)
+		goto err;
+
+	err = -ENOMEM;
 	root = get_root_inode(sb, d.rootmode);
 	if (root == NULL)
 		goto err;
@@ -507,11 +673,12 @@
 
  err:
 	spin_lock(&fuse_lock);
+	mount_count --;
 	fc->sb = NULL;
 	fuse_release_conn(fc);
 	spin_unlock(&fuse_lock);
 	*get_fuse_conn_super_p(sb) = NULL;
-	return -EINVAL;
+	return err;
 }
 
 #ifdef KERNEL_2_6
diff --git a/lib/fuse.c b/lib/fuse.c
index 1e120e8..b5be472 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -8,15 +8,31 @@
 
 #include <config.h>
 #include "fuse_i.h"
+#include "fuse_compat.h"
 #include "fuse_kernel.h"
 
+#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <limits.h>
 #include <errno.h>
+#include <assert.h>
+#include <stdint.h>
 #include <sys/param.h>
 
+/* FUSE flags: */
+
+/** Enable debuging output */
+#define FUSE_DEBUG       (1 << 1)
+
+/** If a file is removed but it's still open, don't hide the file but
+    remove it immediately */
+#define FUSE_HARD_REMOVE (1 << 2)
+
+/** Use st_ino field in getattr instead of generating inode numbers  */
+#define FUSE_USE_INO     (1 << 3)
+
 #define FUSE_KERNEL_MINOR_VERSION_NEED 1
 #define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
 #define FUSE_VERSION_FILE_NEW "/sys/fs/fuse/version"
@@ -28,6 +44,32 @@
 #define ENTRY_REVALIDATE_TIME 1 /* sec */
 #define ATTR_REVALIDATE_TIME 1 /* sec */
 
+
+struct node {
+    struct node *name_next;
+    struct node *id_next;
+    nodeid_t nodeid;
+    unsigned int generation;
+    int refctr;
+    nodeid_t parent;
+    char *name;
+    uint64_t version;
+    int open_count;
+    int is_hidden;
+};
+
+struct fuse_dirhandle {
+    struct fuse *fuse;
+    nodeid_t dir;
+    FILE *fp;
+};
+
+struct fuse_cmd {
+    char *buf;
+    size_t buflen;
+};
+
+
 static struct fuse_context *(*fuse_getcontext)(void) = NULL;
 
 static const char *opname(enum fuse_opcode opcode)
@@ -61,11 +103,18 @@
     }
 }
 
-static inline void dec_avail(struct fuse *f)
+static inline void fuse_dec_avail(struct fuse *f)
 {
-    pthread_mutex_lock(&f->lock);
+    pthread_mutex_lock(&f->worker_lock);
     f->numavail --;
-    pthread_mutex_unlock(&f->lock);
+    pthread_mutex_unlock(&f->worker_lock);
+}
+
+static inline void fuse_inc_avail(struct fuse *f)
+{
+    pthread_mutex_lock(&f->worker_lock);
+    f->numavail ++;
+    pthread_mutex_unlock(&f->worker_lock);
 }
 
 static struct node *get_node_nocheck(struct fuse *f, nodeid_t nodeid)
@@ -83,18 +132,18 @@
 static struct node *get_node(struct fuse *f, nodeid_t nodeid)
 {
     struct node *node = get_node_nocheck(f, nodeid);
-    if (node != NULL)
-        return node;
-    
-    fprintf(stderr, "fuse internal error: inode %lu not found\n", nodeid);
-    abort();
+    if (!node) {
+        fprintf(stderr, "fuse internal error: node %lu not found\n",
+                nodeid);
+        abort();
+    }
+    return node;
 }
 
-static void hash_id(struct fuse *f, struct node *node)
+static void free_node(struct node *node)
 {
-    size_t hash = node->nodeid % f->id_table_size;
-    node->id_next = f->id_table[hash];
-    f->id_table[hash] = node;    
+    free(node->name);
+    free(node);
 }
 
 static void unhash_id(struct fuse *f, struct node *node)
@@ -109,20 +158,11 @@
         }
 }
 
-static nodeid_t next_id(struct fuse *f)
+static void hash_id(struct fuse *f, struct node *node)
 {
-    do {
-        f->ctr++;
-        if (!f->ctr)
-            f->generation ++;
-    } while (f->ctr == 0 || get_node_nocheck(f, f->ctr) != NULL);
-    return f->ctr;
-}
-
-static void free_node(struct node *node)
-{
-    free(node->name);
-    free(node);
+    size_t hash = node->nodeid % f->id_table_size;
+    node->id_next = f->id_table[hash];
+    f->id_table[hash] = node;    
 }
 
 static unsigned int name_hash(struct fuse *f, nodeid_t parent, const char *name)
@@ -136,6 +176,70 @@
     return (hash + parent) % f->name_table_size;
 }
 
+static void unref_node(struct fuse *f, struct node *node);
+
+static void unhash_name(struct fuse *f, struct node *node)
+{
+    if (node->name) {
+        size_t hash = name_hash(f, node->parent, node->name);
+        struct node **nodep = &f->name_table[hash];
+        
+        for (; *nodep != NULL; nodep = &(*nodep)->name_next)
+            if (*nodep == node) {
+                *nodep = node->name_next;
+                node->name_next = NULL;
+                unref_node(f, get_node(f, node->parent));
+                free(node->name);
+                node->name = NULL;
+                node->parent = 0;
+                return;
+            }
+        fprintf(stderr, "fuse internal error: unable to unhash node: %lu\n",
+                node->nodeid);
+        abort();
+    }
+}
+
+static int hash_name(struct fuse *f, struct node *node, nodeid_t parent,
+                     const char *name)
+{
+    size_t hash = name_hash(f, parent, name);
+    node->name = strdup(name);
+    if (node->name == NULL)
+        return -1;
+
+    get_node(f, parent)->refctr ++;
+    node->parent = parent;
+    node->name_next = f->name_table[hash];
+    f->name_table[hash] = node;
+    return 0;
+}
+
+static void delete_node(struct fuse *f, struct node *node)
+{
+    assert(!node->name);
+    unhash_id(f, node);
+    free_node(node);
+}
+
+static void unref_node(struct fuse *f, struct node *node)
+{
+    assert(node->refctr > 0);
+    node->refctr --;
+    if (!node->refctr)
+        delete_node(f, node);
+}
+
+static nodeid_t next_id(struct fuse *f)
+{
+    do {
+        f->ctr++;
+        if (!f->ctr)
+            f->generation ++;
+    } while (f->ctr == 0 || get_node_nocheck(f, f->ctr) != NULL);
+    return f->ctr;
+}
+
 static struct node *lookup_node(struct fuse *f, nodeid_t parent,
                                 const char *name)
 {
@@ -149,43 +253,8 @@
     return NULL;
 }
 
-static int hash_name(struct fuse *f, struct node *node, nodeid_t parent,
-                     const char *name)
-{
-    size_t hash = name_hash(f, parent, name);
-    node->parent = parent;
-    node->name = strdup(name);
-    if (node->name == NULL)
-        return -1;
-
-    node->name_next = f->name_table[hash];
-    f->name_table[hash] = node;
-    return 0;
-}
-
-static void unhash_name(struct fuse *f, struct node *node)
-{
-    if (node->name != NULL) {
-        size_t hash = name_hash(f, node->parent, node->name);
-        struct node **nodep = &f->name_table[hash];
-        
-        for (; *nodep != NULL; nodep = &(*nodep)->name_next)
-            if (*nodep == node) {
-                *nodep = node->name_next;
-                node->name_next = NULL;
-                free(node->name);
-                node->name = NULL;
-                node->parent = 0;
-                return;
-            }
-        fprintf(stderr, "fuse internal error: unable to unhash node: %lu\n",
-                node->nodeid);
-        abort();
-    }
-}
-
 static struct node *find_node(struct fuse *f, nodeid_t parent, char *name,
-                              struct fuse_attr *attr, int version)
+                              struct fuse_attr *attr, uint64_t version)
 {
     struct node *node;
     int mode = attr->mode & S_IFMT;
@@ -197,38 +266,27 @@
     pthread_mutex_lock(&f->lock);
     node = lookup_node(f, parent, name);
     if (node != NULL) {
-        if (node->mode == mode && node->rdev == rdev && 
-            (!(f->flags & FUSE_USE_INO) || node->ino == attr->ino)) {
-            if (!(f->flags & FUSE_USE_INO))
-                attr->ino = node->nodeid;
-
-            goto out;
-        }
+        if (!(f->flags & FUSE_USE_INO))
+            attr->ino = node->nodeid;        
+    } else {
+        node = (struct node *) calloc(1, sizeof(struct node));
+        if (node == NULL)
+            goto out_err;
         
-        unhash_name(f, node);
+        node->refctr = 1;
+        node->nodeid = next_id(f);
+        if (!(f->flags & FUSE_USE_INO))
+            attr->ino = node->nodeid;
+        node->open_count = 0;
+        node->is_hidden = 0;
+        node->generation = f->generation;
+        if (hash_name(f, node, parent, name) == -1) {
+            free(node);
+            node = NULL;
+            goto out_err;
+        }
+        hash_id(f, node);
     }
-
-    node = (struct node *) calloc(1, sizeof(struct node));
-    if (node == NULL)
-        goto out_err;
-
-    node->nodeid = next_id(f);
-    if (!(f->flags & FUSE_USE_INO))
-        attr->ino = node->nodeid;
-    node->mode = mode;
-    node->rdev = rdev;
-    node->ino = attr->ino;
-    node->open_count = 0;
-    node->is_hidden = 0;
-    node->generation = f->generation;
-    if (hash_name(f, node, parent, name) == -1) {
-        free(node);
-        node = NULL;
-        goto out_err;
-    }
-    hash_id(f, node);
-
- out:
     node->version = version;
  out_err:
     pthread_mutex_unlock(&f->lock);
@@ -265,8 +323,8 @@
     }
 
     pthread_mutex_lock(&f->lock);
-    for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
-        node = get_node(f, node->parent)) {
+    for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID;
+         node = get_node(f, node->parent)) {
         if (node->name == NULL) {
             s = NULL;
             break;
@@ -278,7 +336,7 @@
     }
     pthread_mutex_unlock(&f->lock);
 
-    if (s == NULL) 
+    if (node == NULL || s == NULL)
         return NULL;
     else if (*s == '\0')
         return strdup("/");
@@ -291,16 +349,16 @@
     return get_path_name(f, nodeid, NULL);
 }
 
-static void destroy_node(struct fuse *f, nodeid_t nodeid, int version)
+static void forget_node(struct fuse *f, nodeid_t nodeid, uint64_t version)
 {
     struct node *node;
 
     pthread_mutex_lock(&f->lock);
-    node = get_node_nocheck(f, nodeid);
-    if (node && node->version == version && nodeid != FUSE_ROOT_ID) {
+    node = get_node(f, nodeid);
+    if (node->version == version && nodeid != FUSE_ROOT_ID) {
+        node->version = 0;
         unhash_name(f, node);
-        unhash_id(f, node);
-        free_node(node);
+        unref_node(f, node);
     }
     pthread_mutex_unlock(&f->lock);
 
@@ -312,12 +370,8 @@
 
     pthread_mutex_lock(&f->lock);
     node = lookup_node(f, dir, name);
-    if (node == NULL) {
-        fprintf(stderr, "fuse internal error: unable to remove node %lu/%s\n",
-                dir, name);
-        abort();
-    }
-    unhash_name(f, node);
+    if (node != NULL)
+        unhash_name(f, node);
     pthread_mutex_unlock(&f->lock);
 }
 
@@ -331,11 +385,8 @@
     pthread_mutex_lock(&f->lock);
     node  = lookup_node(f, olddir, oldname);
     newnode  = lookup_node(f, newdir, newname);
-    if (node == NULL) {
-        fprintf(stderr, "fuse internal error: unable to rename node %lu/%s\n",
-                olddir, oldname);
-        abort();
-    }
+    if (node == NULL)
+        goto out;
 
     if (newnode != NULL) {
         if (hide) {
@@ -383,19 +434,28 @@
 static int fill_dir(struct fuse_dirhandle *dh, const char *name, int type,
                     ino_t ino)
 {
-    struct fuse_dirent dirent;
+    size_t namelen = strlen(name);
+    struct fuse_dirent *dirent;
     size_t reclen;
     size_t res;
 
+    if (namelen > FUSE_NAME_MAX)
+        namelen = FUSE_NAME_MAX;
+
+    dirent = calloc(1, sizeof(struct fuse_dirent) + namelen + 8);
+    if (dirent == NULL)
+        return -ENOMEM;
+
     if ((dh->fuse->flags & FUSE_USE_INO))
-        dirent.ino = ino;
+        dirent->ino = ino;
     else
-        dirent.ino = (unsigned long) -1;
-    dirent.namelen = strlen(name);
-    strncpy(dirent.name, name, sizeof(dirent.name));
-    dirent.type = type;
-    reclen = FUSE_DIRENT_SIZE(&dirent);
-    res = fwrite(&dirent, reclen, 1, dh->fp);
+        dirent->ino = (unsigned long) -1;
+    dirent->namelen = namelen;
+    strncpy(dirent->name, name, namelen);
+    dirent->type = type;
+    reclen = FUSE_DIRENT_SIZE(dirent);
+    res = fwrite(dirent, reclen, 1, dh->fp);
+    free(dirent);
     if (res == 0) {
         perror("fuse: writing directory file");
         return -EIO;
@@ -403,26 +463,22 @@
     return 0;
 }
 
-static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize,
-                          int locked)
+static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize)
 {
     int res;
+    struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+    out->len = outsize;
 
     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);
+        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 */
-    if (!locked)
-        pthread_mutex_lock(&f->lock);
-    f->numavail ++;
-    if (!locked)
-        pthread_mutex_unlock(&f->lock);
+       could play tricks with us, and only let the counter be
+       increased long after the operation is done */
+    fuse_inc_avail(f);
 
     res = write(f->fd, outbuf, outsize);
     if (res == -1) {
@@ -434,8 +490,8 @@
     return 0;
 }
 
-static int do_send_reply(struct fuse *f, struct fuse_in_header *in, int error,
-                         void *arg, size_t argsize, int locked)
+static int send_reply(struct fuse *f, struct fuse_in_header *in, int error,
+                      void *arg, size_t argsize)
 {
     int res;
     char *outbuf;
@@ -463,25 +519,13 @@
         if (argsize != 0)
             memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
         
-        res = send_reply_raw(f, outbuf, outsize, locked);
+        res = send_reply_raw(f, outbuf, outsize);
         free(outbuf);
     }
 
     return res;
 }
 
-static int send_reply(struct fuse *f, struct fuse_in_header *in, int error,
-                      void *arg, size_t argsize)
-{
-    return do_send_reply(f, in, error, arg, argsize, 0);
-}
-
-static int send_reply_locked(struct fuse *f, struct fuse_in_header *in,
-                             int error, void *arg, size_t argsize)
-{
-    return do_send_reply(f, in, error, arg, argsize, 1);
-}
-
 static int is_open(struct fuse *f, nodeid_t dir, const char *name)
 {
     struct node *node;
@@ -511,9 +555,8 @@
         pthread_mutex_lock(&f->lock);
         node = lookup_node(f, dir, oldname);
         if (node == NULL) {
-            fprintf(stderr, "fuse internal error: node %lu/%s not found\n",
-                    dir, oldname);
-            abort();
+            pthread_mutex_unlock(&f->lock);
+            return NULL;
         }
         do {
             f->hidectr ++;
@@ -556,8 +599,9 @@
     return err;
 }
 
-static int lookup_path(struct fuse *f, nodeid_t nodeid, int version, char *name,
-                       const char *path, struct fuse_entry_out *arg)
+static int lookup_path(struct fuse *f, nodeid_t nodeid, uint64_t version,
+                       char *name, const char *path,
+                       struct fuse_entry_out *arg)
 {
     int res;
     struct stat buf;
@@ -579,7 +623,7 @@
             arg->attr_valid = ATTR_REVALIDATE_TIME;
             arg->attr_valid_nsec = 0;
             if (f->flags & FUSE_DEBUG) {
-                printf("   NODEID: %li\n", arg->nodeid);
+                printf("   NODEID: %lu\n", (unsigned long) arg->nodeid);
                 fflush(stdout);
             }
         }
@@ -608,17 +652,18 @@
     }
     res2 = send_reply(f, in, res, &arg, sizeof(arg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, arg.nodeid, in->unique);
+        forget_node(f, arg.nodeid, in->unique);
 }
 
 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->nodeid, arg->version);
+        printf("FORGET %lu/%llu\n", (unsigned long) in->nodeid,
+               arg->version);
         fflush(stdout);
     }
-    destroy_node(f, in->nodeid, arg->version);
+    forget_node(f, in->nodeid, arg->version);
 }
 
 static void do_getattr(struct fuse *f, struct fuse_in_header *in)
@@ -644,10 +689,6 @@
         convert_stat(&buf, &arg.attr);
         if (!(f->flags & FUSE_USE_INO))
             arg.attr.ino = in->nodeid;
-        else {
-            struct node *node = get_node(f, in->nodeid);
-            node->ino = arg.attr.ino;
-        }
     }
 
     send_reply(f, in, res, &arg, sizeof(arg));
@@ -737,10 +778,6 @@
                     convert_stat(&buf, &outarg.attr);
                     if (!(f->flags & FUSE_USE_INO))
                         outarg.attr.ino = in->nodeid;
-                    else {
-                        struct node *node = get_node(f, in->nodeid);
-                        node->ino = outarg.attr.ino;
-                    }
                 }
             }
         }
@@ -826,7 +863,7 @@
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, outarg.nodeid, in->unique);
+        forget_node(f, outarg.nodeid, in->unique);
 }
 
 static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
@@ -855,7 +892,7 @@
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, outarg.nodeid, in->unique);
+        forget_node(f, outarg.nodeid, in->unique);
 }
 
 static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name)
@@ -878,6 +915,7 @@
                 res = f->op.unlink(path);
                 if (res == 0)
                     remove_node(f, in->nodeid, name);
+
             }
         }
         free(path);
@@ -933,7 +971,7 @@
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, outarg.nodeid, in->unique);
+        forget_node(f, outarg.nodeid, in->unique);
 
 }
 
@@ -1008,7 +1046,7 @@
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        destroy_node(f, outarg.nodeid, in->unique);
+        forget_node(f, outarg.nodeid, in->unique);
 }
 
 static void do_open(struct fuse *f, struct fuse_in_header *in,
@@ -1025,40 +1063,36 @@
     path = get_path(f, in->nodeid);
     if (path != NULL) {
         res = -ENOSYS;
-        if (f->op.open.curr) {
+        if (f->op.open) {
             if (!f->compat)
-                res = f->op.open.curr(path, &fi);
+                res = f->op.open(path, &fi);
             else
-                res = f->op.open.compat2(path, fi.flags);
+                res = ((struct fuse_operations_compat2 *) &f->op)->open(path, fi.flags);
         }
     }
     if (res == 0) {
         int res2;
-
-        /* If the request is interrupted the lock must be held until
-           the cancellation is finished.  Otherwise there could be
-           races with rename/unlink, against which the kernel can't
-           protect */
-        pthread_mutex_lock(&f->lock);
         outarg.fh = fi.fh;
         if (f->flags & FUSE_DEBUG) {
-            printf("OPEN[%lu] flags: 0x%x\n", outarg.fh, arg->flags);
+            printf("OPEN[%lu] flags: 0x%x\n", fi.fh, arg->flags);
             fflush(stdout);
         }
-
-        res2 = send_reply_locked(f, in, res, &outarg, sizeof(outarg));
+        
+        pthread_mutex_lock(&f->lock);
+        res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
         if(res2 == -ENOENT) {
             /* The open syscall was interrupted, so it must be cancelled */
-            if(f->op.release.curr) {
+            if(f->op.release) {
                 if (!f->compat)
-                    f->op.release.curr(path, &fi);
+                    f->op.release(path, &fi);
                 else
-                    f->op.release.compat2(path, fi.flags);
+                    ((struct fuse_operations_compat2 *) &f->op)->release(path, fi.flags);
             }
-        } else
-            get_node(f, in->nodeid)->open_count ++;
+        } else {
+            struct node *node = get_node(f, in->nodeid);
+            node->open_count ++;
+        }
         pthread_mutex_unlock(&f->lock);
-
     } else
         send_reply(f, in, res, NULL, 0);
 
@@ -1079,7 +1113,7 @@
     path = get_path(f, in->nodeid);
     if (path != NULL) {
         if (f->flags & FUSE_DEBUG) {
-            printf("FLUSH[%lu]\n", arg->fh);
+            printf("FLUSH[%lu]\n", (unsigned long) arg->fh);
             fflush(stdout);
         }
         res = -ENOSYS;
@@ -1096,6 +1130,7 @@
     struct node *node;
     char *path;
     struct fuse_file_info fi;
+    int unlink_hidden;
 
     memset(&fi, 0, sizeof(fi));
     fi.flags = arg->flags;
@@ -1103,28 +1138,29 @@
 
     pthread_mutex_lock(&f->lock);
     node = get_node(f, in->nodeid);
+    assert(node->open_count > 0);
     --node->open_count;
+    unlink_hidden = (node->is_hidden && !node->open_count);
     pthread_mutex_unlock(&f->lock);
 
     path = get_path(f, in->nodeid);
-    if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
-            printf("RELEASE[%lu]\n", arg->fh);
-            fflush(stdout);
-        }
-        if (f->op.release.curr) {
-            if (!f->compat)
-                f->op.release.curr(path, &fi);
-            else
-                f->op.release.compat2(path, fi.flags);
-        }
-
-        if(node->is_hidden && node->open_count == 0)
-            /* can now clean up this hidden file */
-            f->op.unlink(path);
-        
-        free(path);
+    if (f->flags & FUSE_DEBUG) {
+        printf("RELEASE[%lu] flags: 0x%x\n", fi.fh, fi.flags);
+        fflush(stdout);
     }
+    if (f->op.release) {
+        if (!f->compat)
+            f->op.release(path ? path : "-", &fi);
+        else if (path)
+            ((struct fuse_operations_compat2 *) &f->op)->release(path, fi.flags);
+    }
+    
+    if(unlink_hidden && path)
+        f->op.unlink(path);
+    
+    if (path)
+        free(path);
+
     send_reply(f, in, 0, NULL, 0);
 }
 
@@ -1150,8 +1186,8 @@
         path = get_path(f, in->nodeid);
         if (path != NULL) {
             if (f->flags & FUSE_DEBUG) {
-                printf("READ[%lu] %u bytes from %llu\n", arg->fh, arg->size,
-                       arg->offset);
+                printf("READ[%lu] %u bytes from %llu\n",
+                       (unsigned long) arg->fh, arg->size, arg->offset);
                 fflush(stdout);
             }
             
@@ -1166,7 +1202,8 @@
             size = res;
             res = 0;
             if (f->flags & FUSE_DEBUG) {
-                printf("   READ[%lu] %u bytes\n", arg->fh, size);
+                printf("   READ[%lu] %u bytes\n", (unsigned long) arg->fh,
+                       size);
                 fflush(stdout);
             }
         }
@@ -1175,7 +1212,7 @@
         out->error = res;
         outsize = sizeof(struct fuse_out_header) + size;
         
-        send_reply_raw(f, outbuf, outsize, 0);
+        send_reply_raw(f, outbuf, outsize);
         free(outbuf);
     }
 }
@@ -1190,15 +1227,15 @@
 
     memset(&fi, 0, sizeof(fi));
     fi.fh = arg->fh;
-    fi.writepage = arg->writepage;
+    fi.writepage = arg->write_flags & 1;
 
     res = -ENOENT;
     path = get_path(f, in->nodeid);
     if (path != NULL) {
         if (f->flags & FUSE_DEBUG) {
             printf("WRITE%s[%lu] %u bytes to %llu\n",
-                   arg->writepage ? "PAGE" : "", arg->fh, arg->size,
-                   arg->offset);
+                   (arg->write_flags & 1) ? "PAGE" : "",
+                   (unsigned long) arg->fh, arg->size, arg->offset);
             fflush(stdout);
         }
 
@@ -1253,13 +1290,13 @@
     struct statfs buf;
 
     memset(&buf, 0, sizeof(struct statfs));
-    if (f->op.statfs.curr) {
+    if (f->op.statfs) {
         if (!f->compat || f->compat > 11)
-            res = f->op.statfs.curr("/", &buf);
+            res = f->op.statfs("/", &buf);
         else {
             struct fuse_statfs_compat1 compatbuf;
             memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1));
-            res = f->op.statfs.compat1(&compatbuf);
+            res = ((struct fuse_operations_compat1 *) &f->op)->statfs(&compatbuf);
             if (res == 0)
                 convert_statfs_compat(&compatbuf, &buf);
         }
@@ -1287,12 +1324,12 @@
     path = get_path(f, in->nodeid);
     if (path != NULL) {
         if (f->flags & FUSE_DEBUG) {
-            printf("FSYNC[%lu]\n", inarg->fh);
+            printf("FSYNC[%lu]\n", (unsigned long) inarg->fh);
             fflush(stdout);
         }
         res = -ENOSYS;
         if (f->op.fsync)
-            res = f->op.fsync(path, inarg->datasync, &fi);
+            res = f->op.fsync(path, inarg->fsync_flags & 1, &fi);
         free(path);
     }
     send_reply(f, in, res, NULL, 0);
@@ -1355,7 +1392,7 @@
         out->unique = in->unique;
         out->error = res;
         
-        send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0);
+        send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size);
         free(outbuf);
     }
 }
@@ -1423,7 +1460,7 @@
         out->unique = in->unique;
         out->error = res;
         
-        send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0);
+        send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size);
         free(outbuf);
     }
 }
@@ -1481,12 +1518,12 @@
     size_t argsize;
     struct fuse_context *ctx = fuse_get_context();
 
-    dec_avail(f);
+    fuse_dec_avail(f);
 
     if ((f->flags & FUSE_DEBUG)) {
-        printf("unique: %i, opcode: %s (%i), nodeid: %li, insize: %i\n",
-               in->unique, opname(in->opcode), in->opcode, in->nodeid,
-               cmd->buflen);
+        printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n",
+               in->unique, opname(in->opcode), in->opcode,
+               (unsigned long) in->nodeid, cmd->buflen);
         fflush(stdout);
     }
 
@@ -1790,9 +1827,9 @@
     struct fuse *f;
     struct node *root;
 
-    if (sizeof(struct fuse_operations_i) < op_size) {
+    if (sizeof(struct fuse_operations) < op_size) {
         fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
-        op_size = sizeof(struct fuse_operations_i);
+        op_size = sizeof(struct fuse_operations);
     }
 
     f = (struct fuse *) calloc(1, sizeof(struct fuse));
@@ -1829,12 +1866,14 @@
 
 #ifndef USE_UCLIBC
      pthread_mutex_init(&f->lock, NULL);
+     pthread_mutex_init(&f->worker_lock, NULL);
 #else
      {
          pthread_mutexattr_t attr;
          pthread_mutexattr_init(&attr);
          pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
          pthread_mutex_init(&f->lock, &attr);
+         pthread_mutex_init(&f->worker_lock, &attr);
          pthread_mutexattr_destroy(&attr);
      }
 #endif
@@ -1850,8 +1889,6 @@
         goto out_free_id_table;
     }
 
-    root->mode = 0;
-    root->rdev = 0;
     root->name = strdup("/");
     if (root->name == NULL) {
         fprintf(stderr, "fuse: memory allocation failed\n");
@@ -1861,6 +1898,7 @@
     root->parent = 0;
     root->nodeid = FUSE_ROOT_ID;
     root->generation = 0;
+    root->refctr = 1;
     hash_id(f, root);
 
     return f;
@@ -1926,6 +1964,7 @@
     free(f->id_table);
     free(f->name_table);
     pthread_mutex_destroy(&f->lock);
+    pthread_mutex_destroy(&f->worker_lock);
     free(f);
 }
 
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index f3c3076..ebe712a 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -7,83 +7,14 @@
 */
 
 #include "fuse.h"
-#include "fuse_compat.h"
-#include <stdio.h>
 #include <pthread.h>
 
-/* FUSE flags: */
-
-/** Enable debuging output */
-#define FUSE_DEBUG       (1 << 1)
-
-/** If a file is removed but it's still open, don't hide the file but
-    remove it immediately */
-#define FUSE_HARD_REMOVE (1 << 2)
-
-/** Use st_ino field in getattr instead of generating inode numbers  */
-#define FUSE_USE_INO     (1 << 3)
-
-
 typedef unsigned long nodeid_t;
 
-struct node {
-    struct node *name_next;
-    struct node *id_next;
-    nodeid_t nodeid;
-    unsigned int generation;
-    nodeid_t parent;
-    char *name;
-    int mode;
-    int rdev;
-    unsigned long ino;
-    int version;
-    int open_count;
-    int is_hidden;
-};
-
-struct fuse_operations_i {
-    int (*getattr)     (const char *, struct stat *);
-    int (*readlink)    (const char *, char *, size_t);
-    int (*getdir)      (const char *, fuse_dirh_t, fuse_dirfil_t);
-    int (*mknod)       (const char *, mode_t, dev_t);
-    int (*mkdir)       (const char *, mode_t);
-    int (*unlink)      (const char *);
-    int (*rmdir)       (const char *);
-    int (*symlink)     (const char *, const char *);
-    int (*rename)      (const char *, const char *);
-    int (*link)        (const char *, const char *);
-    int (*chmod)       (const char *, mode_t);
-    int (*chown)       (const char *, uid_t, gid_t);
-    int (*truncate)    (const char *, off_t);
-    int (*utime)       (const char *, struct utimbuf *);
-    union {
-        int (*curr)    (const char *, struct fuse_file_info *);
-        int (*compat2) (const char *, int);
-    } open;
-    int (*read)        (const char *, char *, size_t, off_t,
-                        struct fuse_file_info *);
-    int (*write)       (const char *, const char *, size_t, off_t,
-                        struct fuse_file_info *);
-    union {
-        int (*curr)    (const char *, struct statfs *);
-        int (*compat1) (struct fuse_statfs_compat1 *);
-    } statfs;
-    int (*flush)       (const char *, struct fuse_file_info *);
-    union {
-        int (*curr)    (const char *, struct fuse_file_info *);
-        int (*compat2) (const char *, int);
-    } release;
-    int (*fsync)       (const char *, int, struct fuse_file_info *);
-    int (*setxattr)    (const char *, const char *, const char *, size_t, int);
-    int (*getxattr)    (const char *, const char *, char *, size_t);
-    int (*listxattr)   (const char *, char *, size_t);
-    int (*removexattr) (const char *, const char *);
-};
-
 struct fuse {
     int flags;
     int fd;
-    struct fuse_operations_i op;
+    struct fuse_operations op;
     int compat;
     struct node **name_table;
     size_t name_table_size;
@@ -93,6 +24,7 @@
     unsigned int generation;
     unsigned int hidectr;
     pthread_mutex_t lock;
+    pthread_mutex_t worker_lock;
     int numworker;
     int numavail;
     volatile int exited;
@@ -100,17 +32,6 @@
     int minorver;
 };
 
-struct fuse_dirhandle {
-    struct fuse *fuse;
-    nodeid_t dir;
-    FILE *fp;
-};
-
-struct fuse_cmd {
-    char *buf;
-    size_t buflen;
-};
-
 struct fuse *fuse_new_common(int fd, const char *opts,
                              const struct fuse_operations *op,
                              size_t op_size, int compat);
diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c
index 94d3bb8..c02d5c4 100644
--- a/lib/fuse_mt.c
+++ b/lib/fuse_mt.c
@@ -40,9 +40,9 @@
     ctx = (struct fuse_context *) malloc(sizeof(struct fuse_context));
     if (ctx == NULL) {
         fprintf(stderr, "fuse: failed to allocate fuse context\n");
-        pthread_mutex_lock(&f->lock);
+        pthread_mutex_lock(&f->worker_lock);
         f->numavail --;
-        pthread_mutex_unlock(&f->lock);
+        pthread_mutex_unlock(&f->worker_lock);
         return NULL;
     }
     pthread_setspecific(context_key, ctx);
@@ -61,7 +61,7 @@
             continue;
 
         if (f->numavail == 0 && f->numworker < FUSE_MAX_WORKERS) {
-            pthread_mutex_lock(&f->lock);
+            pthread_mutex_lock(&f->worker_lock);
             if (f->numworker < FUSE_MAX_WORKERS) {
                 /* FIXME: threads should be stored in a list instead
                    of an array */
@@ -69,15 +69,15 @@
                 pthread_t *thread_id = &w->threads[f->numworker];
                 f->numavail ++;
                 f->numworker ++;
-                pthread_mutex_unlock(&f->lock);
+                pthread_mutex_unlock(&f->worker_lock);
                 res = start_thread(w, thread_id);
                 if (res == -1) {
-                    pthread_mutex_lock(&f->lock);
+                    pthread_mutex_lock(&f->worker_lock);
                     f->numavail --;
-                    pthread_mutex_unlock(&f->lock);
+                    pthread_mutex_unlock(&f->worker_lock);
                 }
             } else
-                pthread_mutex_unlock(&f->lock);
+                pthread_mutex_unlock(&f->worker_lock);
         }
 
         w->proc(w->f, cmd, w->data);
diff --git a/lib/helper.c b/lib/helper.c
index c14248d..25a60aa 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -7,6 +7,7 @@
 */
 
 #include "fuse_i.h"
+#include "fuse_compat.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -90,6 +91,19 @@
     return 0;
 }
 
+static int opt_member(const char *opts, const char *opt)
+{
+    const char *e, *s = opts;
+    int optlen = strlen(opt);
+    for (s = opts; s; s = e + 1) {
+        if(!(e = strchr(s, ',')))
+            break;
+        if (e - s == optlen && strncmp(s, opt, optlen) == 0)
+            return 1;
+    }
+    return (s && strcmp(s, opt) == 0);
+}
+
 static int add_option_to(const char *opt, char **optp)
 {
     unsigned len = strlen(opt);
@@ -187,7 +201,6 @@
                     res = add_options(lib_opts, kernel_opts, "debug");
                     if (res == -1)
                         goto err;
-                    *background = 0;
                     break;
                     
                 case 'r':
@@ -251,7 +264,6 @@
     free(*mountpoint);
     return -1;
 }
-                              
 
 static struct fuse *fuse_setup_common(int argc, char *argv[],
                                       const struct fuse_operations *op,
@@ -286,7 +298,7 @@
     if (fuse == NULL)
         goto err_unmount;
 
-    if (background) {
+    if (background && !opt_member(lib_opts, "debug")) {
         res = daemon(0, 0);
         if (res == -1) {
             perror("fuse: failed to daemonize program\n");