improvements to the kernel interface
diff --git a/ChangeLog b/ChangeLog
index 54cfd16..cdc2579 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,7 +3,18 @@
 	* statfs library API changed to match other methods.  Since this
 	  is not backward compatible FUSE_MAJOR_VERSION is changed to 2
 
-	* statfs kernel interface changed to 64 bits, added 'bavail' field
+	* kernel interface changes follow:
+
+	* statfs changed to 64 bits, added 'bavail' field
+
+	* add generation number to lookup result
+
+	* optimized mknod/mkdir/symlink/link (no separate lookup is
+	needed)
+
+	* rdev size increased to 32 bits for mknod
+
+	* kernel interface version changed to 3.1
 	
 2004-02-18  Miklos Szeredi <mszeredi@inf.bme.hu>
 
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 2b516a5..9bca31c 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -117,6 +117,7 @@
 
 struct fuse_lookup_out {
 	unsigned long ino;
+	unsigned long generation;
 	struct fuse_attr attr;
 };
 
@@ -133,19 +134,13 @@
 	void *file; /* Used by kernel only */
 };
 
-/* FIXME: 2.6 needs 32 bit rdev */
 struct fuse_mknod_in {
-	unsigned short mode;
-	unsigned short rdev;
-};
-
-struct fuse_mknod_out {
-	unsigned long ino;
-	struct fuse_attr attr;
+	unsigned int mode;
+	unsigned int rdev;
 };
 
 struct fuse_mkdir_in {
-	unsigned short mode;
+	unsigned int mode;
 };
 
 struct fuse_rename_in {
diff --git a/kernel/dir.c b/kernel/dir.c
index 5fec669..b9c26e6 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -74,23 +74,29 @@
 	else if(S_ISLNK(inode->i_mode)) {
 		inode->i_op = &fuse_symlink_inode_operations;
 	}
-	else {
+	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
+		printk("fuse_init_inode: bad file type: %o\n", inode->i_mode);
+
 	inode->u.generic_ip = inode;
 }
 
-struct inode *fuse_iget(struct super_block *sb, ino_t ino,
+struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation,
 			struct fuse_attr *attr, int version)
 {
 	struct inode *inode;
 
 	inode = iget(sb, ino);
 	if(inode) {
-		if(!inode->u.generic_ip)
+		if(!inode->u.generic_ip) {
+			inode->i_generation = generation;
 			fuse_init_inode(inode, attr);
+		} else if(inode->i_generation != generation)
+			printk("fuse_iget: bad generation for ino %lu\n", ino);
 
 		change_attributes(inode, attr);
 		inode->i_version = version;
@@ -130,7 +136,8 @@
 
 	err = fuse_do_lookup(dir, entry, &outarg, &version);
 	if(!err) {
-		inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, version);
+		inode = fuse_iget(dir->i_sb, outarg.ino, outarg.generation,
+				  &outarg.attr, version);
 		if(!inode)
 			return -ENOMEM;
 	} else if(err != -ENOENT)
@@ -142,8 +149,28 @@
 	return 0;
 }
 
-/* create needs to return a positive entry, so this is actually an
-   mknod+lookup */
+static int lookup_new_entry(struct inode *dir, struct dentry *entry,
+				   struct fuse_lookup_out *outarg, int version,
+				   int mode)
+{
+	struct inode *inode;
+	inode = fuse_iget(dir->i_sb, outarg->ino, outarg->generation, 
+			  &outarg->attr, version);
+	if(!inode) 
+		return -ENOMEM;
+
+	/* Don't allow userspace to do really stupid things... */
+	if((inode->i_mode ^ mode) & S_IFMT) {
+		iput(inode);
+		printk("fuse_mknod: inode has wrong type\n");
+		return -EINVAL;
+	}
+
+	d_instantiate(entry, inode);
+	return 0;
+}
+
+
 static int _fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
 		      dev_t rdev)
 {
@@ -151,8 +178,7 @@
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_mknod_in inarg;
-	struct fuse_mknod_out outarg;
-	struct inode *inode;
+	struct fuse_lookup_out outarg;
 
 	memset(&inarg, 0, sizeof(inarg));
 	inarg.mode = mode;
@@ -173,19 +199,7 @@
 	if(out.h.error) 
 		return out.h.error;
 
-	inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, out.h.unique);
-	if(!inode) 
-		return -ENOMEM;
-
-	/* Don't allow userspace to do really stupid things... */
-	if((inode->i_mode ^ mode) & S_IFMT) {
-		iput(inode);
-		printk("fuse_mknod: inode has wrong type\n");
-		return -EPROTO;
-	}
-
-	d_instantiate(entry, inode);
-	return 0;
+	return lookup_new_entry(dir, entry, &outarg, out.h.unique, mode);
 }
 
 static int _fuse_create(struct inode *dir, struct dentry *entry, int mode)
@@ -193,20 +207,6 @@
 	return _fuse_mknod(dir, entry, mode, 0);
 }
 
-/* knfsd needs the new entry instantiated in mkdir/symlink/link. this
-   should rather be done like mknod: attributes returned in out arg to
-   save a call to userspace */
-static int lookup_new_entry(struct inode *dir, struct dentry *entry)
-{
-	struct inode *inode;
-	int err = fuse_lookup_iget(dir, entry, &inode);
-	if(err || !inode) {
-		printk("fuse_mkdir: failed to look up new entry\n");
-		return err ? err : -ENOENT;
-	}
-	d_instantiate(entry, inode);
-	return 0;
-}
 
 static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
 {
@@ -214,6 +214,7 @@
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_mkdir_in inarg;
+	struct fuse_lookup_out outarg;
 
 	memset(&inarg, 0, sizeof(inarg));
 	inarg.mode = mode;
@@ -225,11 +226,14 @@
 	in.args[0].value = &inarg;
 	in.args[1].size = entry->d_name.len + 1;
 	in.args[1].value = entry->d_name.name;
+	out.numargs = 1;
+	out.args[0].size = sizeof(outarg);
+	out.args[0].value = &outarg;
 	request_send(fc, &in, &out);
 	if(out.h.error)
 		return out.h.error;
 
-	return lookup_new_entry(dir, entry);
+	return lookup_new_entry(dir, entry, &outarg, out.h.unique, S_IFDIR);
 }
 
 static int fuse_symlink(struct inode *dir, struct dentry *entry,
@@ -238,6 +242,7 @@
 	struct fuse_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_lookup_out outarg;
 
 	in.h.opcode = FUSE_SYMLINK;
 	in.h.ino = dir->i_ino;
@@ -246,11 +251,14 @@
 	in.args[0].value = entry->d_name.name;
 	in.args[1].size = strlen(link) + 1;
 	in.args[1].value = link;
+	out.numargs = 1;
+	out.args[0].size = sizeof(outarg);
+	out.args[0].value = &outarg;
 	request_send(fc, &in, &out);
 	if(out.h.error)
 		return out.h.error;
 
-	return lookup_new_entry(dir, entry);
+	return lookup_new_entry(dir, entry, &outarg, out.h.unique, S_IFLNK);
 }
 
 static int fuse_remove(struct inode *dir, struct dentry *entry, 
@@ -325,6 +333,7 @@
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_link_in inarg;
+	struct fuse_lookup_out outarg;
 	
 	memset(&inarg, 0, sizeof(inarg));
 	inarg.newdir = newdir->i_ino;
@@ -336,13 +345,17 @@
 	in.args[0].value = &inarg;
 	in.args[1].size = newent->d_name.len + 1;
 	in.args[1].value = newent->d_name.name;
+	out.numargs = 1;
+	out.args[0].size = sizeof(outarg);
+	out.args[0].value = &outarg;
 	request_send(fc, &in, &out);
 	if(out.h.error)
 		return out.h.error;
 
 	/* Invalidate old entry, so attributes are refreshed */
 	d_invalidate(entry);
-	return lookup_new_entry(newdir, newent);
+	return lookup_new_entry(newdir, newent, &outarg, out.h.unique,
+				inode->i_mode);
 }
 
 int fuse_do_getattr(struct inode *inode)
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index d4a677e..48e52c9 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -174,7 +174,7 @@
 /**
  * Get a filled in inode
  */
-struct inode *fuse_iget(struct super_block *sb, ino_t ino,
+struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation,
 			struct fuse_attr *attr, int version);
 
 
diff --git a/kernel/inode.c b/kernel/inode.c
index 9ab0dc3..08bf3c9 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -148,7 +148,7 @@
 	memset(&attr, 0, sizeof(attr));
 
 	attr.mode = mode;
-	return fuse_iget(sb, 1, &attr, 0);
+	return fuse_iget(sb, 1, 0, &attr, 0);
 }
 
 
@@ -158,7 +158,7 @@
 {
 	__u32 *objp = vobjp;
 	unsigned long ino = objp[0];
-	/* __u32 generation = objp[1]; */
+	__u32 generation = objp[1];
 	struct inode *inode;
 	struct dentry *entry;
 
@@ -166,7 +166,7 @@
 		return ERR_PTR(-ESTALE);
 
 	inode = ilookup(sb, ino);
-	if(!inode)
+	if(!inode || inode->i_generation != generation)
 		return ERR_PTR(-ESTALE);
 
 	entry = d_alloc_anon(inode);
diff --git a/lib/fuse.c b/lib/fuse.c
index ffb3489..079220a 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -81,11 +81,9 @@
     abort();
 }
 
-static void hash_ino(struct fuse *f, struct node *node, fino_t ino)
+static void hash_ino(struct fuse *f, struct node *node)
 {
-    size_t hash = ino % f->ino_table_size;
-    node->ino = ino;
-    
+    size_t hash = node->ino % f->ino_table_size;
     node->ino_next = f->ino_table[hash];
     f->ino_table[hash] = node;    
 }
@@ -102,16 +100,13 @@
         }
 }
 
-static fino_t get_ino(struct node *node)
-{
-    return node->ino;
-}
-
 static fino_t next_ino(struct fuse *f)
 {
-    do f->ctr++;
-    while(f->ctr == 0 || __get_node(f, f->ctr) != NULL);
-    
+    do {
+        f->ctr++;
+        if(!f->ctr)
+            f->generation ++;
+    } while(f->ctr == 0 || __get_node(f, f->ctr) != NULL);
     return f->ctr;
 }
 
@@ -176,8 +171,8 @@
     }
 }
 
-static fino_t find_node(struct fuse *f, fino_t parent, char *name,
-                        struct fuse_attr *attr, int version)
+static struct node *find_node(struct fuse *f, fino_t parent, char *name,
+                              struct fuse_attr *attr, int version)
 {
     struct node *node;
     int mode = attr->mode & S_IFMT;
@@ -198,13 +193,15 @@
     node = (struct node *) calloc(1, sizeof(struct node));
     node->mode = mode;
     node->rdev = rdev;
-    hash_ino(f, node, next_ino(f));
+    node->ino = next_ino(f);
+    node->generation = f->generation;
+    hash_ino(f, node);
     hash_name(f, node, parent, name);
 
   out:
     node->version = version;
     pthread_mutex_unlock(&f->lock);
-    return get_ino(node);
+    return node;
 }
 
 static char *add_name(char *buf, char *s, const char *name)
@@ -407,11 +404,33 @@
     return res;
 }
 
+static int lookup_path(struct fuse *f, fino_t ino, int version,  char *name,
+                       const char *path, struct fuse_lookup_out *arg)
+{
+    int res;
+    struct stat buf;
+
+    res = f->op.getattr(path, &buf);
+    if(res == 0) {
+        struct node *node;
+
+        memset(arg, 0, sizeof(struct fuse_lookup_out));
+        convert_stat(&buf, &arg->attr);
+        node = find_node(f, ino, name, &arg->attr, version);
+        arg->ino = node->ino;
+        arg->generation = node->generation;
+        if(f->flags & FUSE_DEBUG) {
+            printf("   INO: %li\n", arg->ino);
+            fflush(stdout);
+        }
+    }
+    return res;
+}
+
 static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
 {
     int res;
     char *path;
-    struct stat buf;
     struct fuse_lookup_out arg;
 
     res = -ENOENT;
@@ -423,19 +442,9 @@
         }
         res = -ENOSYS;
         if(f->op.getattr)
-            res = f->op.getattr(path, &buf);
+            res = lookup_path(f, in->ino, in->unique, name, path, &arg);
         free(path);
     }
-
-    if(res == 0) {
-        memset(&arg, 0, sizeof(struct fuse_lookup_out));
-        convert_stat(&buf, &arg.attr);
-        arg.ino = find_node(f, in->ino, name, &arg.attr, in->unique);
-        if(f->flags & FUSE_DEBUG) {
-            printf("   LOOKUP: %li\n", arg.ino);
-            fflush(stdout);
-        }
-    }
     send_reply(f, in, res, &arg, sizeof(arg));
 }
 
@@ -609,27 +618,24 @@
 {
     int res;
     char *path;
-    struct fuse_mknod_out outarg;
-    struct stat buf;
+    char *name = PARAM(inarg);
+    struct fuse_lookup_out outarg;
 
     res = -ENOENT;
-    path = get_path_name(f, in->ino, PARAM(inarg));
+    path = get_path_name(f, in->ino, name);
     if(path != NULL) {
+        if(f->flags & FUSE_DEBUG) {
+            printf("MKNOD %s\n", path);
+            fflush(stdout);
+        }
         res = -ENOSYS;
         if(f->op.mknod && f->op.getattr) {
             res = f->op.mknod(path, inarg->mode, inarg->rdev);
             if(res == 0)
-                res = f->op.getattr(path, &buf);
+                res = lookup_path(f, in->ino, in->unique, name, path, &outarg);
         }
         free(path);
     }
-    if(res == 0) {
-        memset(&outarg, 0, sizeof(struct fuse_mknod_out));
-        convert_stat(&buf, &outarg.attr);
-        outarg.ino = find_node(f, in->ino, PARAM(inarg), &outarg.attr,
-                               in->unique);
-    }
-
     send_reply(f, in, res, &outarg, sizeof(outarg));
 }
 
@@ -638,16 +644,25 @@
 {
     int res;
     char *path;
+    char *name = PARAM(inarg);
+    struct fuse_lookup_out outarg;
 
     res = -ENOENT;
-    path = get_path_name(f, in->ino, PARAM(inarg));
+    path = get_path_name(f, in->ino, name);
     if(path != NULL) {
+        if(f->flags & FUSE_DEBUG) {
+            printf("MKDIR %s\n", path);
+            fflush(stdout);
+        }
         res = -ENOSYS;
-        if(f->op.mkdir)
+        if(f->op.mkdir && f->op.getattr) {
             res = f->op.mkdir(path, inarg->mode);
+            if(res == 0)
+                res = lookup_path(f, in->ino, in->unique, name, path, &outarg);
+        }
         free(path);
     }
-    send_reply(f, in, res, NULL, 0);
+    send_reply(f, in, res, &outarg, sizeof(outarg));
 }
 
 static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name)
@@ -679,16 +694,24 @@
 {
     int res;
     char *path;
+    struct fuse_lookup_out outarg;
 
     res = -ENOENT;
     path = get_path_name(f, in->ino, name);
     if(path != NULL) {
+        if(f->flags & FUSE_DEBUG) {
+            printf("SYMLINK %s\n", path);
+            fflush(stdout);
+        }
         res = -ENOSYS;
-        if(f->op.symlink)
+        if(f->op.symlink && f->op.getattr) {
             res = f->op.symlink(link, path);
+            if(res == 0)
+                res = lookup_path(f, in->ino, in->unique, name, path, &outarg);
+        }
         free(path);
     }
-    send_reply(f, in, res, NULL, 0);
+    send_reply(f, in, res, &outarg, sizeof(outarg));
 }
 
 static void do_rename(struct fuse *f, struct fuse_in_header *in,
@@ -725,20 +748,30 @@
     int res;
     char *oldpath;
     char *newpath;
+    char *name = PARAM(arg);
+    struct fuse_lookup_out outarg;
 
     res = -ENOENT;
     oldpath = get_path(f, in->ino);
     if(oldpath != NULL) {
-        newpath =  get_path_name(f, arg->newdir, PARAM(arg));
+        newpath =  get_path_name(f, arg->newdir, name);
         if(newpath != NULL) {
+            if(f->flags & FUSE_DEBUG) {
+                printf("LINK %s\n", newpath);
+                fflush(stdout);
+            }
             res = -ENOSYS;
-            if(f->op.link)
+            if(f->op.link && f->op.getattr) {
                 res = f->op.link(oldpath, newpath);
+                if(res == 0)
+                    res = lookup_path(f, arg->newdir, in->unique, name,
+                                      newpath, &outarg);
+            }
             free(newpath);
         }
         free(oldpath);
     }
-    send_reply(f, in, res, NULL, 0);   
+    send_reply(f, in, res, &outarg, sizeof(outarg));   
 }
 
 static void do_open(struct fuse *f, struct fuse_in_header *in,
@@ -1090,6 +1123,7 @@
     f->flags = flags;
     f->fd = fd;
     f->ctr = 0;
+    f->generation = 0;
     /* FIXME: Dynamic hash table */
     f->name_table_size = 14057;
     f->name_table = (struct node **)
@@ -1111,7 +1145,9 @@
     root->rdev = 0;
     root->name = strdup("/");
     root->parent = 0;
-    hash_ino(f, root, FUSE_ROOT_INO);
+    root->ino = FUSE_ROOT_INO;
+    root->generation = 0;
+    hash_ino(f, root);
 
     return f;
 }
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 52f1fdc..4eaa5ba 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -16,6 +16,7 @@
     struct node *name_next;
     struct node *ino_next;
     fino_t ino;
+    unsigned int generation;
     fino_t parent;
     char *name;
     int mode;
@@ -32,6 +33,7 @@
     struct node **ino_table;
     size_t ino_table_size;
     fino_t ctr;
+    unsigned int generation;
     pthread_mutex_t lock;
     int numworker;
     int numavail;