x
diff --git a/.cvsignore b/.cvsignore
index be4407b..26633aa 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1 +1 @@
-fusemount
+fusepro
diff --git a/fusepro.c b/fusepro.c
index 4c0582c..cbe80aa 100644
--- a/fusepro.c
+++ b/fusepro.c
@@ -64,6 +64,50 @@
     return 0;
 }
 
+static int pro_symlink(const char *from, const char *to)
+{
+    int res;
+
+    res = symlink(from, to);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int pro_mkdir(const char *path, int mode)
+{
+    int res;
+
+    res = mkdir(path, mode);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int pro_unlink(const char *path)
+{
+    int res;
+
+    res = unlink(path);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int pro_rmdir(const char *path)
+{
+    int res;
+
+    res = rmdir(path);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
 static void exit_handler()
 {
     exit(0);
@@ -104,6 +148,10 @@
     readlink:	pro_readlink,
     getdir:     pro_getdir,
     mknod:	pro_mknod,
+    mkdir:	pro_mkdir,
+    symlink:	pro_symlink,
+    unlink:	pro_unlink,
+    rmdir:	pro_rmdir,
 };
 
 int main(int argc, char *argv[])
diff --git a/include/fuse.h b/include/fuse.h
index 6b1a151..6837b9e 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -20,8 +20,12 @@
 struct fuse_operations {
     int (*getattr) (const char *path, struct stat *stbuf);
     int (*readlink) (const char *path, char *buf, size_t size);
-    int (*mknod) (const char *path, int mode, int rdev);
     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);
 };
 
 struct fuse *fuse_new();
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index fb3aca0..b519a8d 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -39,6 +39,11 @@
 	FUSE_READLINK,
 	FUSE_GETDIR,
 	FUSE_MKNOD,
+	FUSE_MKDIR,
+	FUSE_SYMLINK,
+	FUSE_UNLINK,
+	FUSE_RMDIR,
+	FUSE_RENAME,
 };
 
 /* Conservative buffer size for the client */
@@ -69,10 +74,21 @@
 	struct fuse_attr attr;
 };
 
+struct fuse_mkdir_in {
+	unsigned short mode;
+	char name[1];
+};
+
+
+struct fuse_rename_in {
+	unsigned long newdir;
+	char names[2];
+};
+
 struct fuse_in_header {
 	int unique;
 	enum fuse_opcode opcode;
-	unsigned long ino;	
+	unsigned long ino;
 };
 
 struct fuse_out_header {
diff --git a/kernel/dir.c b/kernel/dir.c
index 92ddc55..065ac01 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -14,6 +14,15 @@
 #include <linux/file.h>
 #include <linux/slab.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 inode_operations fuse_special_inode_operations;
+
+static struct file_operations fuse_dir_operations;
+static struct file_operations fuse_file_operations;
+
+
 static void change_attributes(struct inode *inode, struct fuse_attr *attr)
 {
 	inode->i_mode    = attr->mode;
@@ -28,18 +37,22 @@
 	inode->i_ctime   = attr->ctime;
 }
 
-static void init_inode(struct inode *inode, struct fuse_attr *attr)
+void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
 {
 	change_attributes(inode, attr);
 	
-	if(S_ISREG(inode->i_mode))
-		fuse_file_init(inode);
-	else if(S_ISDIR(inode->i_mode))
-		fuse_dir_init(inode);
+	if(S_ISREG(inode->i_mode)) {
+		inode->i_op = &fuse_file_inode_operations;
+		inode->i_fop = &fuse_file_operations;
+	}
+	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))
-		fuse_symlink_init(inode);
+		inode->i_op = &fuse_symlink_inode_operations;
 	else {
-		fuse_special_init(inode);
+		inode->i_op = &fuse_special_inode_operations;
 		init_special_inode(inode, inode->i_mode, attr->rdev);
 	}
 }
@@ -73,11 +86,12 @@
 	if(!inode) 
 		return ERR_PTR(-ENOMEM);
 
-	init_inode(inode, &arg.attr);
+	fuse_init_inode(inode, &arg.attr);
 	d_add(entry, inode);
 	return NULL;
 }
 
+/* create needs to return a positive entry, so this also does a lookup */
 static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
 		      int rdev)
 {
@@ -106,26 +120,142 @@
 	out.arg = &outarg;
 	request_send(fc, &in, &out);
 	kfree(inarg);
-	if(out.h.result)
+
+	if(out.h.result) 
 		return out.h.result;
 
 	inode = iget(dir->i_sb, outarg.ino);
 	if(!inode) 
 		return -ENOMEM;
-
-	init_inode(inode, &outarg.attr);
+	
+	fuse_init_inode(inode, &outarg.attr);
 	d_add(entry, inode);
+
 	return 0;
 }
 
+
 static int fuse_create(struct inode *dir, struct dentry *entry, int mode)
 {
 	return fuse_mknod(dir, entry, mode, 0);
 }
 
+static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
+{
+	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_mkdir_in *inarg;
+	unsigned int insize;
+	
+	insize = offsetof(struct fuse_mkdir_in, name) + entry->d_name.len + 1;
+	inarg = kmalloc(insize, GFP_KERNEL);
+	if(!inarg)
+		return -ENOMEM;
+	
+	inarg->mode = mode;
+	strcpy(inarg->name, entry->d_name.name);
+
+	in.h.opcode = FUSE_MKDIR;
+	in.h.ino = dir->i_ino;
+	in.argsize = insize;
+	in.arg = inarg;
+	request_send(fc, &in, &out);
+	kfree(inarg);
+
+	return out.h.result;
+}
+
+static int fuse_symlink(struct inode *dir, struct dentry *entry,
+			const char *link)
+{
+	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	char *inarg;
+	unsigned int insize;
+	
+	insize = entry->d_name.len + 1 + strlen(link) + 1;
+	inarg = kmalloc(insize, GFP_KERNEL);
+	if(!inarg)
+		return -ENOMEM;
+	
+	strcpy(inarg, entry->d_name.name);
+	strcpy(inarg + entry->d_name.len + 1, link);
+
+	in.h.opcode = FUSE_SYMLINK;
+	in.h.ino = dir->i_ino;
+	in.argsize = insize;
+	in.arg = inarg;
+	request_send(fc, &in, &out);
+	kfree(inarg);
+	
+	return out.h.result;
+}
+
+static int fuse_remove(struct inode *dir, struct dentry *entry, 
+		       enum fuse_opcode op)
+{
+	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+
+	in.h.opcode = op;
+	in.h.ino = dir->i_ino;
+	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;
+}
+
+static int fuse_unlink(struct inode *dir, struct dentry *entry)
+{
+	return fuse_remove(dir, entry, FUSE_UNLINK);
+}
+
+static int fuse_rmdir(struct inode *dir, struct dentry *entry)
+{
+	return fuse_remove(dir, entry, FUSE_RMDIR);
+}
+
+static int fuse_rename(struct inode *olddir, struct dentry *oldent,
+		       struct inode *newdir, struct dentry *newent)
+{
+	struct fuse_conn *fc = olddir->i_sb->u.generic_sbp;
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_rename_in *inarg;
+	unsigned int oldnamsize = oldent->d_name.len + 1;
+	unsigned int newnamsize = newent->d_name.len + 1;
+	unsigned int insize;
+	
+	insize = offsetof(struct fuse_rename_in, names) + oldnamsize +
+		newnamsize;
+	inarg = kmalloc(insize, GFP_KERNEL);
+	if(!inarg)
+		return -ENOMEM;
+	
+	inarg->newdir = newdir->i_ino;
+	strcpy(inarg->names, oldent->d_name.name);
+	strcpy(inarg->names + oldnamsize, newent->d_name.name);
+
+	in.h.opcode = FUSE_RENAME;
+	in.h.ino = olddir->i_ino;
+	in.argsize = insize;
+	in.arg = inarg;
+	request_send(fc, &in, &out);
+	kfree(inarg);
+
+	return out.h.result;
+}
+
 static int fuse_permission(struct inode *inode, int mask)
 {
-
 	return 0;
 }
 
@@ -145,41 +275,22 @@
 	
 	if(out.h.result == 0)
 		change_attributes(inode, &arg.attr);
+	else
+		d_drop(dentry);
 
 	return out.h.result;
 }
 
-
-#define DIR_BUFSIZE 2048
-
-static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
+static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
+			 void *dstbuf, filldir_t filldir)
 {
-	struct file *cfile = file->private_data;
-	char *buf;
-	char *p;
-	int ret;
-	size_t nbytes;
-	
-	buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL);
-	if(!buf)
-		return -ENOMEM;
-
-	ret = kernel_read(cfile, file->f_pos, buf, DIR_BUFSIZE);
-	if(ret < 0) {
-		printk("fuse_readdir: failed to read container file\n");
-		goto out;
-	}
-	nbytes = ret;
-	p = buf;
-	ret = 0;
 	while(nbytes >= FUSE_NAME_OFFSET) {
-		struct fuse_dirent *dirent = (struct fuse_dirent *) p;
+		struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
 		size_t reclen = FUSE_DIRENT_SIZE(dirent);
 		int over;
 		if(dirent->namelen > NAME_MAX) {
 			printk("fuse_readdir: name too long\n");
-			ret = -EPROTO;
-			goto out;
+			return -EPROTO;
 		}
 		if(reclen > nbytes)
 			break;
@@ -189,18 +300,35 @@
 		if(over)
 			break;
 
-		p += reclen;
+		buf += reclen;
 		file->f_pos += reclen;
 		nbytes -= reclen;
 	}
 
-  out:
+	return 0;
+}
+
+#define DIR_BUFSIZE 2048
+static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
+{
+	struct file *cfile = file->private_data;
+	char *buf;
+	int ret;
+	
+	buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL);
+	if(!buf)
+		return -ENOMEM;
+
+	ret = kernel_read(cfile, file->f_pos, buf, DIR_BUFSIZE);
+	if(ret < 0)
+		printk("fuse_readdir: failed to read container file\n");
+	else 
+		ret = parse_dirfile(buf, ret, file, dstbuf, filldir);
+
 	kfree(buf);	
 	return ret;
 }
 
-
-
 static int read_link(struct dentry *dentry, char **bufp)
 {
 	struct inode *inode = dentry->d_inode;
@@ -314,14 +442,14 @@
 	lookup:		fuse_lookup,
 	create:         fuse_create,
 	mknod:          fuse_mknod,
-#if 0
-
-	link:           fuse_link,
-	unlink:         fuse_unlink,
-	symlink:        fuse_symlink,
 	mkdir:          fuse_mkdir,
+	symlink:        fuse_symlink,
+	unlink:         fuse_unlink,
 	rmdir:          fuse_rmdir,
 	rename:         fuse_rename,
+#if 0
+	link:           fuse_link,
+	setattr:	fuse_setattr,
 #endif
 	permission:	fuse_permission,
         revalidate:	fuse_revalidate,
@@ -354,28 +482,6 @@
         revalidate:	fuse_revalidate,
 };
 
-void fuse_dir_init(struct inode *inode)
-{
-	inode->i_op = &fuse_dir_inode_operations;
-	inode->i_fop = &fuse_dir_operations;
-}
-
-void fuse_file_init(struct inode *inode)
-{
-	inode->i_op = &fuse_file_inode_operations;
-	inode->i_fop = &fuse_file_operations;
-}
-
-void fuse_symlink_init(struct inode *inode)
-{
-	inode->i_op = &fuse_symlink_inode_operations;
-}
-
-void fuse_special_init(struct inode *inode)
-{
-	inode->i_op = &fuse_special_inode_operations;
-}
-
 /* 
  * Local Variables:
  * indent-tabs-mode: t
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index 1eb0bb4..de0a9e6 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -109,25 +109,11 @@
  */
 extern spinlock_t fuse_lock;
 
-/**
- * Fill in the directory operations
- */
-void fuse_dir_init(struct inode *inode);
 
 /**
- * Fill in the file operations
+ * Initialize inode
  */
-void fuse_file_init(struct inode *inode);
-
-/**
- * Fill in the symlink operations
- */
-void fuse_symlink_init(struct inode *inode);
-
-/**
- * Fill in the special inode operaions
- */
-void fuse_special_init(struct inode *inode);
+void fuse_init_inode(struct inode *inode, struct fuse_attr *attr);
 
 /**
  * Check if the connection can be released, and if yes, then free the
diff --git a/kernel/inode.c b/kernel/inode.c
index ac2d6be..b59a146 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -139,21 +139,14 @@
 
 static struct inode *get_root_inode(struct super_block *sb)
 {
-	struct inode *root	;
+	struct inode *root;
 
 	root = iget(sb, 1);
 	if(root) {
-		root->i_mode = S_IFDIR;
-		root->i_uid = 0;
-		root->i_gid = 0;
-		root->i_nlink = 2;
-		root->i_size = 0;
-		root->i_blksize = 1024;
-		root->i_blocks = 0;
-		root->i_atime = CURRENT_TIME;
-		root->i_mtime = CURRENT_TIME;
-		root->i_ctime = CURRENT_TIME;
-		fuse_dir_init(root);
+		struct fuse_attr attr;
+		memset(&attr, 0, sizeof(attr));
+		attr.mode = S_IFDIR;
+		fuse_init_inode(root, &attr);
 	}
 
 	return root;
diff --git a/lib/fuse.c b/lib/fuse.c
index f846f74..62695f0 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -43,15 +43,14 @@
 
 static inline struct node *get_node(fino_t ino)
 {
-    return (struct node *) ino;
+    return (struct node *) ((ino << 3) + 0x8000000);
 }
 
 static inline fino_t get_ino(struct node *node)
 {
-    return (fino_t) node;
+    return (((fino_t) node) - 0x8000000) >> 3;
 }
 
-
 static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
 {
     struct node *node;
@@ -95,6 +94,14 @@
     return ss;
 }
 
+static char *get_path_name(fino_t ino, const char *name)
+{
+    char *path = get_path(ino);
+    char *path2 = g_strconcat(path, "/", name, NULL);
+    g_free(path);
+    return path2;
+}
+
 static void remove_node(struct fuse *f, fino_t ino)
 {
     struct node *node = get_node(ino);
@@ -102,7 +109,6 @@
     free_node(node);
 }
 
-
 static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
 {
     attr->mode    = stbuf->st_mode;
@@ -124,11 +130,10 @@
     struct stat buf;
     int res;
 
-    if(f->op.getattr == NULL)
-        return -ENOSYS;
-    
     path = get_path(ino);
-    res = f->op.getattr(path, &buf);
+    res = -ENOSYS;
+    if(f->op.getattr)
+        res = f->op.getattr(path, &buf);
     g_free(path);
     if(res == 0) 
         convert_stat(&buf, attr);
@@ -136,21 +141,6 @@
     return res;
 }
 
-static int read_link(struct fuse *f, fino_t ino, char *buf, size_t size)
-{
-    char *path;
-    int res;
-    
-    if(f->op.readlink == NULL)
-        return -ENOSYS;
-
-    path = get_path(ino);
-    res = f->op.readlink(path, buf, size);
-    g_free(path);
-
-    return res;
-}
-
 static int fill_dir(struct fuse_dh *dh, char *name, int type)
 {
     struct fuse_dirent dirent;
@@ -170,27 +160,6 @@
     return 0;
 }
 
-static int get_dir(struct fuse *f, fino_t ino, FILE *fp)
-{
-    char *path;
-    int res;
-    struct fuse_dh dh;
-    
-    if(f->op.getdir == NULL)
-        return -ENOSYS;
-
-    dh.fuse = f;
-    dh.fp = fp;
-    dh.dir = ino;
-        
-    path = get_path(ino);
-    res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
-    g_free(path);
-
-    return res;
-}
-
-
 static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
                        void *arg, size_t argsize)
 {
@@ -237,7 +206,6 @@
     send_reply(f, in, res, &arg, sizeof(arg));
 }
 
-
 static void do_forget(struct fuse *f, unsigned long *inos, size_t num)
 {
     size_t i;
@@ -259,50 +227,112 @@
 {
     int res;
     char link[PATH_MAX + 1];
+    char *path;
 
-    res = read_link(f, in->ino, link, PATH_MAX + 1);
-    send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
-}
-
-static void do_mknod(struct fuse *f, struct fuse_in_header *in,
-                     struct fuse_mknod_in *inarg)
-{
-    int res;
-    struct fuse_mknod_out outarg;
-    
+    path = get_path(in->ino);    
     res = -ENOSYS;
-    if(f->op.mknod != NULL && f->op.getattr != NULL) {
-        char *path;
-        struct stat buf;
+    if(f->op.readlink)
+        res = f->op.readlink(path, link, sizeof(link));
+    g_free(path);
 
-        outarg.ino = find_node(f, in->ino, inarg->name, 1);
-        path = get_path(outarg.ino);
-        res = f->op.mknod(path, inarg->mode, inarg->rdev);
-        if(res == 0)
-            res = f->op.getattr(path, &buf);
-        g_free(path);
-
-        if(res == 0)
-            convert_stat(&buf, &outarg.attr);
-        else
-            remove_node(f, outarg.ino);
-    }
-    send_reply(f, in, res, &outarg, sizeof(outarg));
+    send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
 }
 
 static void do_getdir(struct fuse *f, struct fuse_in_header *in)
 {
     int res;
     struct fuse_getdir_out arg;
-    FILE *fp = tmpfile();
+    struct fuse_dh dh;
+    char *path;
 
-    res = get_dir(f, in->ino, fp);
-    fflush(fp);
-    arg.fd = fileno(fp);
+    dh.fuse = f;
+    dh.fp = tmpfile();
+    dh.dir = in->ino;
+
+    path = get_path(in->ino);
+    res = -ENOSYS;
+    if(f->op.getdir)
+        res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
+    g_free(path);
+
+    fflush(dh.fp);
+    arg.fd = fileno(dh.fp);
     send_reply(f, in, res, &arg, sizeof(arg));
-    fclose(fp);
+    fclose(dh.fp);
 }
 
+static void do_mknod(struct fuse *f, struct fuse_in_header *in,
+                     struct fuse_mknod_in *inarg)
+{
+    int res;
+    char *path;
+    struct fuse_mknod_out outarg;
+    struct stat buf;
+
+    outarg.ino = find_node(f, in->ino, inarg->name, 1);
+    path = get_path(outarg.ino);
+    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);
+    }
+    g_free(path);
+
+    if(res == 0)
+        convert_stat(&buf, &outarg.attr);
+
+    send_reply(f, in, res, &outarg, sizeof(outarg));
+}
+
+static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
+                     struct fuse_mkdir_in *inarg)
+{
+    int res;
+    char *path;
+
+    path = get_path_name(in->ino, inarg->name);
+    res = -ENOSYS;
+    if(f->op.mkdir)
+        res = f->op.mkdir(path, inarg->mode);
+    g_free(path);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name)
+{
+    int res;
+    char *path;
+
+    path = get_path_name(in->ino, name);
+    res = -ENOSYS;
+    if(in->opcode == FUSE_UNLINK) {
+        if(f->op.unlink)
+            res = f->op.unlink(path);
+    }
+    else {
+        if(f->op.rmdir)
+            res = f->op.rmdir(path);
+    }
+    g_free(path);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
+                       char *link)
+{
+    int res;
+    char *path;
+
+    path = get_path_name(in->ino, name);
+    res = -ENOSYS;
+    if(f->op.symlink)
+        res = f->op.symlink(link, path);
+    g_free(path);
+    send_reply(f, in, res, NULL, 0);
+}
+
+
 void fuse_loop(struct fuse *f)
 {
     int res;
@@ -355,6 +385,20 @@
         case FUSE_MKNOD:
             do_mknod(f, in, (struct fuse_mknod_in *) inarg);
             break;
+            
+        case FUSE_MKDIR:
+            do_mkdir(f, in, (struct fuse_mkdir_in *) inarg);
+            break;
+            
+        case FUSE_UNLINK:
+        case FUSE_RMDIR:
+            do_remove(f, in, (char *) inarg);
+            break;
+
+        case FUSE_SYMLINK:
+            do_symlink(f, in, (char *) inarg, 
+                       ((char *) inarg) + strlen((char *) inarg) + 1);
+            break;
 
         default:
             fprintf(stderr, "Operation %i not implemented\n", in->opcode);
@@ -377,7 +421,6 @@
     return f;
 }
 
-
 void fuse_set_operations(struct fuse *f, const struct fuse_operations *op)
 {
     f->op = *op;
@@ -390,4 +433,3 @@
     g_hash_table_destroy(f->nametab);
     g_free(f);
 }
-