x
diff --git a/fusepro.c b/fusepro.c
index cbe80aa..4f593e7 100644
--- a/fusepro.c
+++ b/fusepro.c
@@ -108,6 +108,28 @@
     return 0;
 }
 
+static int pro_rename(const char *from, const char *to)
+{
+    int res;
+
+    res = rename(from, to);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int pro_link(const char *from, const char *to)
+{
+    int res;
+
+    res = link(from, to);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
 static void exit_handler()
 {
     exit(0);
@@ -152,6 +174,8 @@
     symlink:	pro_symlink,
     unlink:	pro_unlink,
     rmdir:	pro_rmdir,
+    rename:     pro_rename,
+    link:	pro_link,
 };
 
 int main(int argc, char *argv[])
diff --git a/include/fuse.h b/include/fuse.h
index 6837b9e..e18f606 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -23,9 +23,11 @@
     int (*getdir) (const char *path, struct fuse_dh *h, dirfiller_t filler);
     int (*mknod) (const char *path, int mode, int rdev);
     int (*mkdir) (const char *path, int mode);
-    int (*symlink) (const char *from, const char *to);
     int (*unlink) (const char *path);
     int (*rmdir) (const char *path);
+    int (*rename) (const char *from, const char *to);
+    int (*symlink) (const char *from, const char *to);
+    int (*link) (const char *from, const char *to);
 };
 
 struct fuse *fuse_new();
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index b519a8d..64a4af3 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -44,6 +44,7 @@
 	FUSE_UNLINK,
 	FUSE_RMDIR,
 	FUSE_RENAME,
+	FUSE_LINK,
 };
 
 /* Conservative buffer size for the client */
@@ -79,10 +80,14 @@
 	char name[1];
 };
 
-
 struct fuse_rename_in {
 	unsigned long newdir;
-	char names[2];
+	char names[1];
+};
+
+struct fuse_link_in {
+	unsigned long newdir;
+	char name[1];
 };
 
 struct fuse_in_header {
@@ -93,7 +98,7 @@
 
 struct fuse_out_header {
 	int unique;
-	int result;
+	int error;
 };
 
 struct fuse_dirent {
diff --git a/kernel/dev.c b/kernel/dev.c
index a91a176..d32589d 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -53,14 +53,14 @@
 	oh = (struct fuse_out_header *) req->out;
 	size = req->outsize - OHSIZE;
 	
-	if (oh->result <= -512 || oh->result > 0) {
-		printk("fuse: bad result\n");
+	if (oh->error <= -512 || oh->error > 0) {
+		printk("fuse: bad error value: %i\n", oh->error);
 		return -EPROTO;
 	}
 
 	if(size > outp->argsize || 
-	   (oh->result == 0 && !outp->argvar && size != outp->argsize) ||
-	   (oh->result != 0 && size != 0)) {
+	   (oh->error == 0 && !outp->argvar && size != outp->argsize) ||
+	   (oh->error != 0 && size != 0)) {
 		printk("fuse: invalid argument length: %i (%i)\n", size,
 		       req->opcode);
 		return -EPROTO;
@@ -71,7 +71,7 @@
 	if(size)
 		memcpy(outp->arg, req->out + OHSIZE, size);
 	
-	return oh->result;
+	return oh->error;
 }
 
 static void request_free(struct fuse_req *req)
@@ -158,7 +158,7 @@
 	spin_unlock(&fuse_lock);
   out:
 	if(outp)
-		outp->h.result = ret;
+		outp->h.error = ret;
 }
 
 static int request_wait(struct fuse_conn *fc)
diff --git a/kernel/dir.c b/kernel/dir.c
index 065ac01..b23def7 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -22,6 +22,7 @@
 static struct file_operations fuse_dir_operations;
 static struct file_operations fuse_file_operations;
 
+static struct dentry_operations fuse_dentry_opertations;
 
 static void change_attributes(struct inode *inode, struct fuse_attr *attr)
 {
@@ -57,7 +58,6 @@
 	}
 }
 
-
 static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
 {
 	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
@@ -74,19 +74,18 @@
 	out.arg = &arg;
 	request_send(fc, &in, &out);
 	
-	if(out.h.result) {
-		/* Negative dentries are not hashed */
-		if(out.h.result == -ENOENT)
-			return NULL;
-		else
-			return ERR_PTR(out.h.result);
+	inode = NULL;
+	if(!out.h.error) {
+		inode = iget(dir->i_sb, arg.ino);
+		if(!inode) 
+			return ERR_PTR(-ENOMEM);
+		
+		fuse_init_inode(inode, &arg.attr);
 	}
+	else if(out.h.error != -ENOENT)
+		return ERR_PTR(out.h.error);
 
-	inode = iget(dir->i_sb, arg.ino);
-	if(!inode) 
-		return ERR_PTR(-ENOMEM);
-
-	fuse_init_inode(inode, &arg.attr);
+	entry->d_op = &fuse_dentry_opertations;
 	d_add(entry, inode);
 	return NULL;
 }
@@ -121,15 +120,15 @@
 	request_send(fc, &in, &out);
 	kfree(inarg);
 
-	if(out.h.result) 
-		return out.h.result;
+	if(out.h.error) 
+		return out.h.error;
 
 	inode = iget(dir->i_sb, outarg.ino);
 	if(!inode) 
 		return -ENOMEM;
 	
 	fuse_init_inode(inode, &outarg.attr);
-	d_add(entry, inode);
+	d_instantiate(entry, inode);
 
 	return 0;
 }
@@ -163,7 +162,7 @@
 	request_send(fc, &in, &out);
 	kfree(inarg);
 
-	return out.h.result;
+	return out.h.error;
 }
 
 static int fuse_symlink(struct inode *dir, struct dentry *entry,
@@ -190,7 +189,7 @@
 	request_send(fc, &in, &out);
 	kfree(inarg);
 	
-	return out.h.result;
+	return out.h.error;
 }
 
 static int fuse_remove(struct inode *dir, struct dentry *entry, 
@@ -205,12 +204,7 @@
 	in.argsize = entry->d_name.len + 1;
 	in.arg = entry->d_name.name;
 	request_send(fc, &in, &out);
-	if(!out.h.result) {
-		d_drop(entry);
-		entry->d_inode->i_nlink = 0;
-	}
-
-	return out.h.result;
+	return out.h.error;
 }
 
 static int fuse_unlink(struct inode *dir, struct dentry *entry)
@@ -251,7 +245,35 @@
 	request_send(fc, &in, &out);
 	kfree(inarg);
 
-	return out.h.result;
+	return out.h.error;
+}
+
+static int fuse_link(struct dentry *entry, struct inode *newdir,
+		     struct dentry *newent)
+{
+	struct inode *inode = entry->d_inode;
+	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_link_in *inarg;
+	unsigned int insize;
+	
+	insize = offsetof(struct fuse_link_in, name) + newent->d_name.len + 1;
+	inarg = kmalloc(insize, GFP_KERNEL);
+	if(!inarg)
+		return -ENOMEM;
+	
+	inarg->newdir = newdir->i_ino;
+	strcpy(inarg->name, newent->d_name.name);
+
+	in.h.opcode = FUSE_LINK;
+	in.h.ino = inode->i_ino;
+	in.argsize = insize;
+	in.arg = inarg;
+	request_send(fc, &in, &out);
+	kfree(inarg);
+
+	return out.h.error;
 }
 
 static int fuse_permission(struct inode *inode, int mask)
@@ -259,6 +281,8 @@
 	return 0;
 }
 
+/* Only revalidate the root inode, since lookup is always redone on
+   the last path segment, and lookup refreshes the attributes */
 static int fuse_revalidate(struct dentry *dentry)
 {
 	struct inode *inode = dentry->d_inode;
@@ -267,18 +291,19 @@
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_getattr_out arg;
 	
+	if(inode->i_ino != FUSE_ROOT_INO)
+		return 0;
+
 	in.h.opcode = FUSE_GETATTR;
 	in.h.ino = inode->i_ino;
 	out.argsize = sizeof(arg);
 	out.arg = &arg;
 	request_send(fc, &in, &out);
 	
-	if(out.h.result == 0)
+	if(!out.h.error)
 		change_attributes(inode, &arg.attr);
-	else
-		d_drop(dentry);
-
-	return out.h.result;
+	
+	return out.h.error;
 }
 
 static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
@@ -347,9 +372,9 @@
 	out.argsize = PAGE_SIZE - 1;
 	out.argvar = 1;
 	request_send(fc, &in, &out);
-	if(out.h.result) {
+	if(out.h.error) {
 		free_page(page);
-		return out.h.result;
+		return out.h.error;
 	}
 
 	*bufp = (char *) page;
@@ -405,7 +430,7 @@
 	out.argsize = sizeof(outarg);
 	out.arg = &outarg;
 	request_send(fc, &in, &out);
-	if(out.h.result == 0) {
+	if(!out.h.error) {
 		struct file *cfile = outarg.file;
 		struct inode *inode;
 		if(!cfile) {
@@ -422,7 +447,7 @@
 		file->private_data = cfile;
 	}
 
-	return out.h.result;
+	return out.h.error;
 }
 
 static int fuse_dir_release(struct inode *inode, struct file *file)
@@ -437,6 +462,14 @@
 	return 0;
 }
 
+static int fuse_dentry_revalidate(struct dentry *entry, int flags)
+{
+	if(!entry->d_inode || !(flags & LOOKUP_CONTINUE))
+		return 0;
+	else
+		return 1;
+}
+
 static struct inode_operations fuse_dir_inode_operations =
 {
 	lookup:		fuse_lookup,
@@ -447,12 +480,12 @@
 	unlink:         fuse_unlink,
 	rmdir:          fuse_rmdir,
 	rename:         fuse_rename,
-#if 0
 	link:           fuse_link,
+#if 0
 	setattr:	fuse_setattr,
 #endif
 	permission:	fuse_permission,
-        revalidate:	fuse_revalidate,
+	revalidate:	fuse_revalidate,
 };
 
 static struct file_operations fuse_dir_operations = {
@@ -464,12 +497,12 @@
 
 static struct inode_operations fuse_file_inode_operations = {
 	permission:	fuse_permission,
-        revalidate:	fuse_revalidate,
+	revalidate:	fuse_revalidate,
 };
 
 static struct inode_operations fuse_special_inode_operations = {
 	permission:	fuse_permission,
-        revalidate:	fuse_revalidate,
+	revalidate:	fuse_revalidate,
 };
 
 static struct file_operations fuse_file_operations = {
@@ -479,7 +512,11 @@
 {
 	readlink:	fuse_readlink,
 	follow_link:	fuse_follow_link,
-        revalidate:	fuse_revalidate,
+	revalidate:	fuse_revalidate,
+};
+
+static struct dentry_operations fuse_dentry_opertations = {
+	d_revalidate:	fuse_dentry_revalidate,
 };
 
 /* 
diff --git a/kernel/inode.c b/kernel/inode.c
index b59a146..702ef57 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -182,6 +182,9 @@
 
         sb->u.generic_sbp = fc;
 	sb->s_root = d_alloc_root(root);
+	if(!sb->s_root)
+		goto err;
+
 	fc->sb = sb;
 	spin_unlock(&fuse_lock);
 	
diff --git a/lib/fuse.c b/lib/fuse.c
index 62695f0..f033e04 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -12,6 +12,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include <assert.h>
 
 
 static guint name_hash(const struct node *node)
@@ -29,7 +30,7 @@
 static struct node *new_node(fino_t parent, const char *name)
 {
     struct node *node = g_new0(struct node, 1);
-    node->name = strdup(name);
+    node->name = g_strdup(name);
     node->parent = parent;
     return node;
 }
@@ -51,15 +52,22 @@
     return (((fino_t) node) - 0x8000000) >> 3;
 }
 
+static struct node *lookup_node(struct fuse *f, fino_t parent,
+                                const char *name)
+{
+    struct node tmp;
+
+    tmp.name = (char *) name;
+    tmp.parent = parent;
+
+    return g_hash_table_lookup(f->nametab, &tmp);
+}
+
 static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
 {
     struct node *node;
-    struct node tmp;
-    
-    tmp.name = name;
-    tmp.parent = parent;
 
-    node = g_hash_table_lookup(f->nametab, &tmp);
+    node = lookup_node(f, parent, name);
     if(node != NULL)
         return get_ino(node);
 
@@ -109,6 +117,27 @@
     free_node(node);
 }
 
+static void rename_node(struct fuse *f, fino_t olddir, const char *oldname,
+                        fino_t newdir, const char *newname)
+{
+    struct node *node = lookup_node(f, olddir, oldname);
+    struct node *newnode = lookup_node(f, newdir, newname);
+        
+    assert(node != NULL);
+
+    /* The overwritten node is left to dangle until deleted */
+    if(newnode != NULL)
+        g_hash_table_remove(f->nametab, newnode);
+        
+    /* The renamed node is not freed, since it's pointer is the key */
+    g_hash_table_remove(f->nametab, node);
+    g_free(node->name);
+    node->name = g_strdup(newname);
+    node->parent = newdir;
+    g_hash_table_insert(f->nametab, node, node);
+}
+
+
 static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
 {
     attr->mode    = stbuf->st_mode;
@@ -124,23 +153,6 @@
     attr->ctime   = stbuf->st_ctime;
 }
 
-static int get_attributes(struct fuse *f, fino_t ino, struct fuse_attr *attr)
-{
-    char *path;
-    struct stat buf;
-    int res;
-
-    path = get_path(ino);
-    res = -ENOSYS;
-    if(f->op.getattr)
-        res = f->op.getattr(path, &buf);
-    g_free(path);
-    if(res == 0) 
-        convert_stat(&buf, attr);
-    
-    return res;
-}
-
 static int fill_dir(struct fuse_dh *dh, char *name, int type)
 {
     struct fuse_dirent dirent;
@@ -160,7 +172,7 @@
     return 0;
 }
 
-static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
+static void send_reply(struct fuse *f, struct fuse_in_header *in, int error,
                        void *arg, size_t argsize)
 {
     int res;
@@ -168,25 +180,24 @@
     size_t outsize;
     struct fuse_out_header *out;
 
-    if(result > 0) {
-        fprintf(stderr, "positive result to operation %i : %i\n", in->opcode,
-                result);
-        result = -ERANGE;
+    if(error > 0) {
+        fprintf(stderr, "positive error code: %i\n",  error);
+        error = -ERANGE;
     }
 
-    if(result != 0)
+    if(error)
         argsize = 0;
 
     outsize = sizeof(struct fuse_out_header) + argsize;
     outbuf = (char *) g_malloc(outsize);
     out = (struct fuse_out_header *) outbuf;
     out->unique = in->unique;
-    out->result = result;
+    out->error = error;
     if(argsize != 0)
         memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
 
-    printf("   unique: %i, result: %i (%s), outsize: %i\n", out->unique,
-           out->result, strerror(-out->result), outsize);
+    printf("   unique: %i, error: %i (%s), outsize: %i\n", out->unique,
+           out->error, strerror(-out->error), outsize);
                 
     res = write(f->fd, outbuf, outsize);
     if(res == -1)
@@ -198,11 +209,19 @@
 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;
 
-    arg.ino = find_node(f, in->ino, name, 1);
-    res = get_attributes(f, arg.ino, &arg.attr);
-
+    path = get_path_name(in->ino, name);
+    res = -ENOSYS;
+    if(f->op.getattr)
+        res = f->op.getattr(path, &buf);
+    g_free(path);
+    if(res == 0) {
+        convert_stat(&buf, &arg.attr);
+        arg.ino = find_node(f, in->ino, name, 1);
+    }
     send_reply(f, in, res, &arg, sizeof(arg));
 }
 
@@ -217,9 +236,18 @@
 static void do_getattr(struct fuse *f, struct fuse_in_header *in)
 {
     int res;
+    char *path;
+    struct stat buf;
     struct fuse_getattr_out arg;
 
-    res = get_attributes(f, in->ino, &arg.attr);
+    path = get_path(in->ino);
+    res = -ENOSYS;
+    if(f->op.getattr)
+        res = f->op.getattr(path, &buf);
+    g_free(path);
+    if(res == 0) 
+        convert_stat(&buf, &arg.attr);
+
     send_reply(f, in, res, &arg, sizeof(arg));
 }
 
@@ -269,8 +297,7 @@
     struct fuse_mknod_out outarg;
     struct stat buf;
 
-    outarg.ino = find_node(f, in->ino, inarg->name, 1);
-    path = get_path(outarg.ino);
+    path = get_path_name(in->ino, inarg->name);
     res = -ENOSYS;
     if(f->op.mknod && f->op.getattr) {
         res = f->op.mknod(path, inarg->mode, inarg->rdev);
@@ -278,9 +305,10 @@
             res = f->op.getattr(path, &buf);
     }
     g_free(path);
-
-    if(res == 0)
+    if(res == 0) {
         convert_stat(&buf, &outarg.attr);
+        outarg.ino = find_node(f, in->ino, inarg->name, 1);
+    }
 
     send_reply(f, in, res, &outarg, sizeof(outarg));
 }
@@ -332,6 +360,39 @@
     send_reply(f, in, res, NULL, 0);
 }
 
+static void do_rename(struct fuse *f, struct fuse_in_header *in,
+                      struct fuse_rename_in *inarg)
+{
+    int res;
+    fino_t olddir = in->ino;
+    fino_t newdir = inarg->newdir;
+    char *oldname = inarg->names;
+    char *newname = inarg->names + strlen(oldname) + 1;
+    char *oldpath = get_path_name(olddir, oldname);
+    char *newpath = get_path_name(newdir, newname);
+
+    res = -ENOSYS;
+    if(f->op.rename)
+        res = f->op.rename(oldpath, newpath);
+    if(res == 0)
+        rename_node(f, olddir, oldname, newdir, newname);
+    send_reply(f, in, res, NULL, 0);   
+}
+
+static void do_link(struct fuse *f, struct fuse_in_header *in,
+                    struct fuse_link_in *inarg)
+{
+    int res;
+    char *oldpath = get_path(in->ino);
+    char *newpath = get_path_name(inarg->newdir, inarg->name);
+
+    res = -ENOSYS;
+    if(f->op.link)
+        res = f->op.link(oldpath, newpath);
+
+    send_reply(f, in, res, NULL, 0);   
+}
+
 
 void fuse_loop(struct fuse *f)
 {
@@ -400,6 +461,14 @@
                        ((char *) inarg) + strlen((char *) inarg) + 1);
             break;
 
+        case FUSE_RENAME:
+            do_rename(f, in, (struct fuse_rename_in *) inarg);
+            break;
+            
+        case FUSE_LINK:
+            do_link(f, in, (struct fuse_link_in *) inarg);
+            break;
+
         default:
             fprintf(stderr, "Operation %i not implemented\n", in->opcode);
             /* No need to send reply to async requests */