fix
diff --git a/ChangeLog b/ChangeLog
index 0e9e54d..e1051c1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2005-05-08  Miklos Szeredi <miklos@szeredi.hu>
+
+	* Better fix for out of order FORGET messages.  Now the
+	LOOKUP/FORGET messages are balanced exactly (one FORGET can
+	balance many lookups), so the order no longer matters.  This
+	changes the ABI slightly, but the library remains backward
+	compatible.
+
 2005-05-06  Miklos Szeredi <miklos@szeredi.hu>
 
 	* Fix abort for out of order FORGET messages.  Again.  Spotted by
diff --git a/kernel/dir.c b/kernel/dir.c
index 0912d50..078d517 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -48,7 +48,6 @@
 		return 0;
 	else if (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);
@@ -59,15 +58,19 @@
 
 		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;
+		if (!err) {
+			if (outarg.nodeid != get_node_id(inode)) {
+				fuse_send_forget(fc, req, outarg.nodeid, 1);
+				return 0;
+			}
+			fi->nlookup ++;
+		}
 		fuse_put_request(fc, req);
-		if (err || outarg.nodeid != get_node_id(inode) ||
-		    (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
+		if (err || (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,
@@ -94,7 +97,6 @@
 			    struct inode **inodep)
 {
 	int err;
-	int version;
 	struct fuse_entry_out outarg;
 	struct inode *inode = NULL;
 	struct fuse_conn *fc = get_fuse_conn(dir);
@@ -109,13 +111,12 @@
 
 	fuse_lookup_init(req, dir, entry, &outarg);
 	request_send(fc, req);
-	version = req->out.h.unique;
 	err = req->out.h.error;
 	if (!err) {
 		inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
-				  &outarg.attr, version);
+				  &outarg.attr);
 		if (!inode) {
-			fuse_send_forget(fc, req, outarg.nodeid, version);
+			fuse_send_forget(fc, req, outarg.nodeid, 1);
 			return -ENOMEM;
 		}
 	}
@@ -154,7 +155,6 @@
 	struct fuse_entry_out outarg;
 	struct inode *inode;
 	struct fuse_inode *fi;
-	int version;
 	int err;
 
 	req->in.h.nodeid = get_node_id(dir);
@@ -163,16 +163,15 @@
 	req->out.args[0].size = sizeof(outarg);
 	req->out.args[0].value = &outarg;
 	request_send(fc, req);
-	version = req->out.h.unique;
 	err = req->out.h.error;
 	if (err) {
 		fuse_put_request(fc, req);
 		return err;
 	}
 	inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
-			  &outarg.attr, version);
+			  &outarg.attr);
 	if (!inode) {
-		fuse_send_forget(fc, req, outarg.nodeid, version);
+		fuse_send_forget(fc, req, outarg.nodeid, 1);
 		return -ENOMEM;
 	}
 	fuse_put_request(fc, req);
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index 25724b4..6ba851e 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -110,6 +110,9 @@
 	 * and kernel */
 	u64 nodeid;
 
+	/** Number of lookups on this inode */
+	u64 nlookup;
+
 	/** The request used for sending the FORGET message */
 	struct fuse_req *forget_req;
 
@@ -390,13 +393,13 @@
  * Get a filled in inode
  */
 struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
-			int generation, struct fuse_attr *attr, int version);
+			int generation, struct fuse_attr *attr);
 
 /**
  * Send FORGET command
  */
 void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
-		      unsigned long nodeid, int version);
+		      unsigned long nodeid, u64 nlookup);
 
 /**
  * Send READ or READDIR request
diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h
index 3c06967..104d31a 100644
--- a/kernel/fuse_kernel.h
+++ b/kernel/fuse_kernel.h
@@ -11,7 +11,7 @@
 #include <asm/types.h>
 
 /** Version number of this interface */
-#define FUSE_KERNEL_VERSION 6
+#define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
 #define FUSE_KERNEL_MINOR_VERSION 1
@@ -113,7 +113,7 @@
 };
 
 struct fuse_forget_in {
-	__u64	version;
+	__u64	nlookup;
 };
 
 struct fuse_attr_out {
diff --git a/kernel/inode.c b/kernel/inode.c
index 6ef9491..4c36ad0 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -63,6 +63,7 @@
 	fi = get_fuse_inode(inode);
 	fi->i_time = jiffies - 1;
 	fi->nodeid = 0;
+	fi->nlookup = 0;
 	fi->forget_req = fuse_request_alloc();
 	if (!fi->forget_req) {
 		kmem_cache_free(fuse_inode_cachep, inode);
@@ -86,10 +87,10 @@
 }
 
 void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
-		      unsigned long nodeid, int version)
+		      unsigned long nodeid, u64 nlookup)
 {
 	struct fuse_forget_in *inarg = &req->misc.forget_in;
-	inarg->version = version;
+	inarg->nlookup = nlookup;
 	req->in.h.opcode = FUSE_FORGET;
 	req->in.h.nodeid = nodeid;
 	req->in.numargs = 1;
@@ -103,7 +104,7 @@
 	if (inode->i_sb->s_flags & MS_ACTIVE) {
 		struct fuse_conn *fc = get_fuse_conn(inode);
 		struct fuse_inode *fi = get_fuse_inode(inode);
-		fuse_send_forget(fc, fi->forget_req, fi->nodeid, inode->i_version);
+		fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup);
 		fi->forget_req = NULL;
 	}
 }
@@ -181,9 +182,10 @@
 }
 
 struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
-			int generation, struct fuse_attr *attr, int version)
+			int generation, struct fuse_attr *attr)
 {
 	struct inode *inode;
+	struct fuse_inode *fi;
 	struct fuse_conn *fc = get_fuse_conn_super(sb);
 	int retried = 0;
 
@@ -206,8 +208,9 @@
 		goto retry;
 	}
 
+	fi = get_fuse_inode(inode);
+	fi->nlookup ++;
 	fuse_change_attributes(inode, attr);
-	inode->i_version = version;
 	return inode;
 }
 #else
@@ -220,9 +223,10 @@
 }
 
 struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
-			int generation, struct fuse_attr *attr, int version)
+			int generation, struct fuse_attr *attr)
 {
 	struct inode *inode;
+	struct fuse_inode *fi;
 	int retried = 0;
 
  retry:
@@ -245,8 +249,9 @@
 		goto retry;
 	}
 
+	fi = get_fuse_inode(inode);
+	fi->nlookup ++;
 	fuse_change_attributes(inode, attr);
-	inode->i_version = version;
 	return inode;
 }
 #endif
@@ -523,7 +528,7 @@
 
 	attr.mode = mode;
 	attr.ino = FUSE_ROOT_ID;
-	return fuse_iget(sb, 1, 0, &attr, 0);
+	return fuse_iget(sb, 1, 0, &attr);
 }
 
 #ifdef KERNEL_2_6
diff --git a/lib/fuse.c b/lib/fuse.c
index 9bc933f..04fb2f0 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -68,6 +68,7 @@
     nodeid_t parent;
     char *name;
     uint64_t version;
+    uint64_t nlookup;
     int open_count;
     int is_hidden;
 };
@@ -254,6 +255,10 @@
 
 static void delete_node(struct fuse *f, struct node *node)
 {
+    if (f->flags & FUSE_DEBUG) {
+        printf("delete: %lu\n", node->nodeid);
+        fflush(stdout);
+    }
     assert(!node->name);
     unhash_id(f, node);
     free_node(node);
@@ -325,6 +330,7 @@
         hash_id(f, node);
     }
     node->version = version;
+    node->nlookup ++;
  out_err:
     pthread_mutex_unlock(&f->lock);
     return node;
@@ -386,7 +392,23 @@
     return get_path_name(f, nodeid, NULL);
 }
 
-static void forget_node(struct fuse *f, nodeid_t nodeid, uint64_t version)
+static void forget_node(struct fuse *f, nodeid_t nodeid, uint64_t nlookup)
+{
+    struct node *node;
+    if (nodeid == FUSE_ROOT_ID)
+        return;
+    pthread_mutex_lock(&f->lock);
+    node = get_node(f, nodeid);
+    assert(node->nlookup >= nlookup);
+    node->nlookup -= nlookup;
+    if (!node->nlookup) {
+        unhash_name(f, node);
+        unref_node(f, node);
+    }
+    pthread_mutex_unlock(&f->lock);
+}
+
+static void forget_node_old(struct fuse *f, nodeid_t nodeid, uint64_t version)
 {
     struct node *node;
 
@@ -398,7 +420,14 @@
         unref_node(f, node);
     }
     pthread_mutex_unlock(&f->lock);
+}
 
+static void cancel_lookup(struct fuse *f, nodeid_t nodeid, uint64_t version)
+{
+    if (f->major <= 6)
+        forget_node_old(f, nodeid, version);
+    else
+        forget_node(f, nodeid, 1);
 }
 
 static void remove_node(struct fuse *f, nodeid_t dir, const char *name)
@@ -658,7 +687,7 @@
     }
     res2 = send_reply(f, in, res, &arg, sizeof(arg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, arg.nodeid, in->unique);
+        cancel_lookup(f, arg.nodeid, in->unique);
 }
 
 static void do_forget(struct fuse *f, struct fuse_in_header *in,
@@ -666,10 +695,13 @@
 {
     if (f->flags & FUSE_DEBUG) {
         printf("FORGET %lu/%llu\n", (unsigned long) in->nodeid,
-               arg->version);
+               arg->nlookup);
         fflush(stdout);
     }
-    forget_node(f, in->nodeid, arg->version);
+    if (f->major <= 6)
+        forget_node_old(f, in->nodeid, arg->nlookup);
+    else
+        forget_node(f, in->nodeid, arg->nlookup);
 }
 
 static void do_getattr(struct fuse *f, struct fuse_in_header *in)
@@ -836,7 +868,7 @@
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, outarg.nodeid, in->unique);
+        cancel_lookup(f, outarg.nodeid, in->unique);
 }
 
 static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
@@ -865,7 +897,7 @@
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, outarg.nodeid, in->unique);
+        cancel_lookup(f, outarg.nodeid, in->unique);
 }
 
 static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name)
@@ -944,7 +976,7 @@
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, outarg.nodeid, in->unique);
+        cancel_lookup(f, outarg.nodeid, in->unique);
 
 }
 
@@ -1019,7 +1051,7 @@
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, outarg.nodeid, in->unique);
+        cancel_lookup(f, outarg.nodeid, in->unique);
 }
 
 static void do_open(struct fuse *f, struct fuse_in_header *in,
@@ -1483,6 +1515,9 @@
     if (arg->major == 5) {
         f->major = 5;
         f->minor = 1;
+    } else if (arg->major == 6) {
+        f->major = 6;
+        f->minor = 1;
     } else {
         f->major = FUSE_KERNEL_VERSION;
         f->minor = FUSE_KERNEL_MINOR_VERSION;
@@ -2113,6 +2148,7 @@
     root->nodeid = FUSE_ROOT_ID;
     root->generation = 0;
     root->refctr = 1;
+    root->nlookup = 1;
     hash_id(f, root);
 
     f->owner = getuid();