bugfixes
diff --git a/.cvsignore b/.cvsignore
index 26633aa..5743e9b 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1 +1,2 @@
 fusepro
+avfsd
diff --git a/Makefile b/Makefile
index 9f6ff4b..97d19d4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,14 @@
 CC = gcc
-CFLAGS = -Wall -W -g `glib-config --cflags`
-LDFLAGS = `glib-config --libs`
-CPPFLAGS = -Iinclude
+CFLAGS = -Wall -g `glib-config --cflags`
+LDFLAGS = `glib-config --libs` -ldl -L ../avfs/libneon/
+#LIBXML = -lxml
+LIBXML = -lxmltok -lxmlparse
+LDLIBS = -lneon $(LIBXML) -lpthread
+CPPFLAGS = -Iinclude -I ../avfs/include
 
 
 
-all: kernel/fuse.o fusepro
+all: kernel/fuse.o fusepro avfsd
 
 kernel/fuse.o: FORCE
 	make -C kernel fuse.o
@@ -15,11 +18,13 @@
 
 fusepro: fusepro.o lib/libfuse.a
 
+avfsd: usermux.o avfsd.o ../avfs/lib/avfs.o lib/libfuse.a 
+
 clean:
 	make -C kernel clean
 	make -C lib clean
 	rm -f *.o
-	rm -f fusepro
+	rm -f fusepro avfsd
 	rm -f *~
 
 FORCE:
diff --git a/avfsd.c b/avfsd.c
new file mode 100644
index 0000000..c86e831
--- /dev/null
+++ b/avfsd.c
@@ -0,0 +1,367 @@
+#include <fuse.h>
+#include <virtual.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+static int check_cred(struct fuse_cred *cred)
+{
+    if(cred->uid != getuid())
+        return -EACCES;
+    else
+        return 0;
+}
+
+static int avfs_getattr(struct fuse_cred *cred, const char *path,
+                        struct stat *stbuf)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_lstat(path, stbuf);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_readlink(struct fuse_cred *cred, const char *path, char *buf,
+                         size_t size)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_readlink(path, buf, size - 1);
+    if(res == -1)
+        return -errno;
+
+    buf[res] = '\0';
+    return 0;
+}
+
+
+static int avfs_getdir(struct fuse_cred *cred, const char *path,
+                       fuse_dirh_t h, fuse_dirfil_t filler)
+{
+    DIR *dp;
+    struct dirent *de;
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    dp = virt_opendir(path);
+    if(dp == NULL)
+        return -errno;
+
+    while((de = virt_readdir(dp)) != NULL) {
+        res = filler(h, de->d_name, de->d_type);
+        if(res != 0)
+            break;
+    }
+
+    virt_closedir(dp);
+    return res;
+}
+
+static int avfs_mknod(struct fuse_cred *cred, const char *path, mode_t mode,
+                      dev_t rdev)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_mknod(path, mode, rdev);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_mkdir(struct fuse_cred *cred, const char *path, mode_t mode)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_mkdir(path, mode);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_unlink(struct fuse_cred *cred, const char *path)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_unlink(path);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_rmdir(struct fuse_cred *cred, const char *path)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_rmdir(path);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_symlink(struct fuse_cred *cred, const char *from,
+                        const char *to)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_symlink(from, to);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_rename(struct fuse_cred *cred, const char *from,
+                       const char *to)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_rename(from, to);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_link(struct fuse_cred *cred, const char *from, const char *to)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_link(from, to);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_chmod(struct fuse_cred *cred, const char *path, mode_t mode)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_chmod(path, mode);
+    if(res == -1)
+        return -errno;
+    
+    return 0;
+}
+
+static int avfs_chown(struct fuse_cred *cred, const char *path, uid_t uid,
+                       gid_t gid)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_lchown(path, uid, gid);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_truncate(struct fuse_cred *cred, const char *path, off_t size)
+{
+    int res;
+    
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_truncate(path, size);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int avfs_utime(struct fuse_cred *cred, const char *path,
+                      struct utimbuf *buf)
+{
+    int res;
+    
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_utime(path, buf);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+
+static int avfs_open(struct fuse_cred *cred, const char *path, int flags)
+{
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    res = virt_open(path, flags, 0);
+    if(res == -1) 
+        return -errno;
+
+    virt_close(res);
+    return 0;
+}
+
+static int avfs_read(struct fuse_cred *cred, const char *path, char *buf,
+                     size_t size, off_t offset)
+{
+    int fd;
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    fd = virt_open(path, O_RDONLY, 0);
+    if(fd == -1)
+        return -errno;
+
+    res = virt_lseek(fd, offset, SEEK_SET);
+    if(res == -1)
+        res = -errno;
+    else {
+        res = virt_read(fd, buf, size);
+        if(res == -1)
+            res = -errno;
+    }
+    
+    virt_close(fd);
+    return res;
+}
+
+static int avfs_write(struct fuse_cred *cred, const char *path,
+                      const char *buf, size_t size, off_t offset)
+{
+    int fd;
+    int res;
+
+    res = check_cred(cred);
+    if(res)
+        return res;
+
+    fd = virt_open(path, O_WRONLY, 0);
+    if(fd == -1)
+        return -errno;
+
+    res = virt_lseek(fd, offset, SEEK_SET);
+    if(res == -1)
+        res = -errno;
+    else {
+        res = virt_write(fd, buf, size);
+        if(res == -1)
+            res = -errno;
+    }
+    
+    virt_close(fd);
+    return res;
+}
+
+static struct fuse_operations avfs_oper = {
+    getattr:	avfs_getattr,
+    readlink:	avfs_readlink,
+    getdir:     avfs_getdir,
+    mknod:	avfs_mknod,
+    mkdir:	avfs_mkdir,
+    symlink:	avfs_symlink,
+    unlink:	avfs_unlink,
+    rmdir:	avfs_rmdir,
+    rename:     avfs_rename,
+    link:	avfs_link,
+    chmod:	avfs_chmod,
+    chown:	avfs_chown,
+    truncate:	avfs_truncate,
+    utime:	avfs_utime,
+    open:	avfs_open,
+    read:	avfs_read,
+    write:	avfs_write,
+};
+
+
+void avfs_main(struct fuse *fuse)
+{
+    fuse_set_operations(fuse, &avfs_oper);
+    fuse_loop(fuse);
+}
+
+#if 0
+int main(int argc, char *argv[])
+{
+    int res;
+    const char *avfs_dir;
+    struct fuse *fuse;
+
+    if(argc != 2) {
+        fprintf(stderr, "usage: %s mount_dir\n", argv[0]);
+        exit(1);
+    }
+    
+    avfs_dir = argv[1];
+
+    fuse = fuse_new(0);
+    res = fuse_mount(fuse, avfs_dir);
+    if(res == -1)
+        exit(1);
+        
+    avfs_main(fuse);
+
+    return 0;
+}
+#endif
diff --git a/fusepro.c b/fusepro.c
index ff998bf..ce32000 100644
--- a/fusepro.c
+++ b/fusepro.c
@@ -15,7 +15,8 @@
 
 static struct fuse *pro_fuse;
 
-static int pro_getattr(const char *path, struct stat *stbuf)
+static int pro_getattr(struct fuse_cred *cred, const char *path,
+                       struct stat *stbuf)
 {
     int res;
 
@@ -26,7 +27,8 @@
     return 0;
 }
 
-static int pro_readlink(const char *path, char *buf, size_t size)
+static int pro_readlink(struct fuse_cred *cred, const char *path, char *buf,
+                        size_t size)
 {
     int res;
 
@@ -39,7 +41,8 @@
 }
 
 
-static int pro_getdir(const char *path, struct fuse_dh *h, dirfiller_t filler)
+static int pro_getdir(struct fuse_cred *cred, const char *path, fuse_dirh_t h,
+                      fuse_dirfil_t filler)
 {
     DIR *dp;
     struct dirent *de;
@@ -59,7 +62,8 @@
     return res;
 }
 
-static int pro_mknod(const char *path, mode_t mode, dev_t rdev)
+static int pro_mknod(struct fuse_cred *cred, const char *path, mode_t mode,
+                     dev_t rdev)
 {
     int res;
 
@@ -70,7 +74,7 @@
     return 0;
 }
 
-static int pro_mkdir(const char *path, mode_t mode)
+static int pro_mkdir(struct fuse_cred *cred, const char *path, mode_t mode)
 {
     int res;
 
@@ -81,7 +85,7 @@
     return 0;
 }
 
-static int pro_unlink(const char *path)
+static int pro_unlink(struct fuse_cred *cred, const char *path)
 {
     int res;
 
@@ -92,7 +96,7 @@
     return 0;
 }
 
-static int pro_rmdir(const char *path)
+static int pro_rmdir(struct fuse_cred *cred, const char *path)
 {
     int res;
 
@@ -103,7 +107,8 @@
     return 0;
 }
 
-static int pro_symlink(const char *from, const char *to)
+static int pro_symlink(struct fuse_cred *cred, const char *from,
+                       const char *to)
 {
     int res;
 
@@ -114,7 +119,7 @@
     return 0;
 }
 
-static int pro_rename(const char *from, const char *to)
+static int pro_rename(struct fuse_cred *cred, const char *from, const char *to)
 {
     int res;
 
@@ -125,7 +130,7 @@
     return 0;
 }
 
-static int pro_link(const char *from, const char *to)
+static int pro_link(struct fuse_cred *cred, const char *from, const char *to)
 {
     int res;
 
@@ -136,7 +141,7 @@
     return 0;
 }
 
-static int pro_chmod(const char *path, mode_t mode)
+static int pro_chmod(struct fuse_cred *cred, const char *path, mode_t mode)
 {
     int res;
 
@@ -147,18 +152,19 @@
     return 0;
 }
 
-static int pro_chown(const char *path, uid_t uid, gid_t gid)
+static int pro_chown(struct fuse_cred *cred, const char *path, uid_t uid,
+                     gid_t gid)
 {
     int res;
 
-    res = chown(path, uid, gid);
+    res = lchown(path, uid, gid);
     if(res == -1)
         return -errno;
 
     return 0;
 }
 
-static int pro_truncate(const char *path, off_t size)
+static int pro_truncate(struct fuse_cred *cred, const char *path, off_t size)
 {
     int res;
     
@@ -169,7 +175,8 @@
     return 0;
 }
 
-static int pro_utime(const char *path, struct utimbuf *buf)
+static int pro_utime(struct fuse_cred *cred, const char *path,
+                     struct utimbuf *buf)
 {
     int res;
     
@@ -181,7 +188,7 @@
 }
 
 
-static int pro_open(const char *path, int flags)
+static int pro_open(struct fuse_cred *cred, const char *path, int flags)
 {
     int res;
 
@@ -193,12 +200,13 @@
     return 0;
 }
 
-static int pro_pread(const char *path, char *buf, size_t size, off_t offset)
+static int pro_read(struct fuse_cred *cred, const char *path, char *buf,
+                    size_t size, off_t offset)
 {
     int fd;
     int res;
 
-    fd = open(path, 0);
+    fd = open(path, O_RDONLY);
     if(fd == -1)
         return -errno;
 
@@ -210,6 +218,24 @@
     return res;
 }
 
+static int pro_write(struct fuse_cred *cred, const char *path, const char *buf,
+                     size_t size, off_t offset)
+{
+    int fd;
+    int res;
+
+    fd = open(path, O_WRONLY);
+    if(fd == -1)
+        return -errno;
+
+    res = pwrite(fd, buf, size, offset);
+    if(res == -1)
+        res = -errno;
+    
+    close(fd);
+    return res;
+}
+
 static void exit_handler()
 {
     exit(0);
@@ -261,7 +287,8 @@
     truncate:	pro_truncate,
     utime:	pro_utime,
     open:	pro_open,
-    pread:	pro_pread,
+    read:	pro_read,
+    write:	pro_write,
 };
 
 int main(int argc, char *argv[])
@@ -275,7 +302,7 @@
     set_signal_handlers();
     atexit(cleanup);
 
-    pro_fuse = fuse_new();
+    pro_fuse = fuse_new(0);
     res = fuse_mount(pro_fuse, argv[1]);
     if(res == -1)
         exit(1);
diff --git a/include/fuse.h b/include/fuse.h
index bdc96a6..4edb0c7 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -13,30 +13,38 @@
 #include <utime.h>
 
 struct fuse;
-struct fuse_dh;
+typedef struct fuse_dirhandle *fuse_dirh_t;
 
-typedef int (*dirfiller_t) (struct fuse_dh *, const char *, int type);
+typedef int (*fuse_dirfil_t) (fuse_dirh_t, const char *, int type);
 
-struct fuse_operations {
-    int (*getattr)  (const char *path, struct stat *stbuf);
-    int (*readlink) (const char *path, char *buf, size_t size);
-    int (*getdir)   (const char *path, struct fuse_dh *h, dirfiller_t filler);
-    int (*mknod)    (const char *path, mode_t mode, dev_t rdev);
-    int (*mkdir)    (const char *path, mode_t mode);
-    int (*unlink)   (const char *path);
-    int (*rmdir)    (const char *path);
-    int (*symlink)  (const char *from, const char *to);
-    int (*rename)   (const char *from, const char *to);
-    int (*link)     (const char *from, const char *to);
-    int (*chmod)    (const char *path, mode_t mode);
-    int (*chown)    (const char *path, uid_t uid, gid_t gid);
-    int (*truncate) (const char *path, off_t size);
-    int (*utime)    (const char *path, struct utimbuf *buf);
-    int (*open)     (const char *path, int flags);
-    int (*pread)    (const char *path, char *buf, size_t size, off_t offset);
+struct fuse_cred {
+    uid_t uid;
+    gid_t gid;
 };
 
-struct fuse *fuse_new();
+struct fuse_operations {
+    int (*getattr)  (struct fuse_cred *, const char *, struct stat *);
+    int (*readlink) (struct fuse_cred *, const char *, char *, size_t);
+    int (*getdir)   (struct fuse_cred *, const char *, fuse_dirh_t, fuse_dirfil_t);
+    int (*mknod)    (struct fuse_cred *, const char *, mode_t, dev_t);
+    int (*mkdir)    (struct fuse_cred *, const char *, mode_t);
+    int (*unlink)   (struct fuse_cred *, const char *);
+    int (*rmdir)    (struct fuse_cred *, const char *);
+    int (*symlink)  (struct fuse_cred *, const char *, const char *);
+    int (*rename)   (struct fuse_cred *, const char *, const char *);
+    int (*link)     (struct fuse_cred *, const char *, const char *);
+    int (*chmod)    (struct fuse_cred *, const char *, mode_t);
+    int (*chown)    (struct fuse_cred *, const char *, uid_t, gid_t);
+    int (*truncate) (struct fuse_cred *, const char *, off_t);
+    int (*utime)    (struct fuse_cred *, const char *, struct utimbuf *);
+    int (*open)     (struct fuse_cred *, const char *, int);
+    int (*read)     (struct fuse_cred *, const char *, char *, size_t, off_t);
+    int (*write)    (struct fuse_cred *, const char *, const char *, size_t, off_t);
+};
+
+#define FUSE_MULTITHREAD (1 << 0)
+
+struct fuse *fuse_new(int flags);
 
 int fuse_mount(struct fuse *f, const char *dir);
 
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 5fe008c..a6e74df 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -54,6 +54,7 @@
 	FUSE_LINK,
 	FUSE_OPEN,
 	FUSE_READ,
+	FUSE_WRITE,
 };
 
 /* Conservative buffer size for the client */
@@ -64,6 +65,10 @@
 	struct fuse_attr attr;
 };
 
+struct fuse_forget_in {
+	int version;
+};
+
 struct fuse_getattr_out {
 	struct fuse_attr attr;
 };
@@ -104,6 +109,10 @@
 	unsigned int valid;
 };
 
+struct fuse_setattr_out {
+	unsigned long long newsize;
+};
+
 struct fuse_open_in {
 	unsigned int flags;
 };
@@ -113,10 +122,18 @@
 	unsigned int size;
 };
 
+struct fuse_write_in {
+	unsigned long long offset;
+	unsigned int size;
+	char buf[1];
+};
+
 struct fuse_in_header {
 	int unique;
 	enum fuse_opcode opcode;
 	unsigned long ino;
+	unsigned int uid;
+	unsigned int gid;
 };
 
 struct fuse_out_header {
diff --git a/kernel/dev.c b/kernel/dev.c
index 6a8d373..8fdb37f 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -128,7 +128,7 @@
 
 	spin_lock(&fuse_lock);
 	ret = -ENOTCONN;
-	if(fc->file == NULL)
+	if(!fc->file)
 		goto out_unlock_free;
 	
 	ih = (struct fuse_in_header *) req->in;
@@ -204,7 +204,7 @@
 	req = list_entry(fc->pending.next, struct fuse_req, list);
 	size = req->insize;
 	if(nbytes < size) {
-		printk("fuse_dev_read[%i]: buffer too small\n", fc->id);
+		printk("fuse_dev_read: buffer too small\n");
 		ret = -EIO;
 		goto err;
 	}
@@ -221,6 +221,7 @@
 	if(copy_to_user(buf, tmpbuf, size))
 		return -EFAULT;
 	
+	kfree(tmpbuf);
 	return size;
 
   err:
@@ -259,12 +260,12 @@
 
 	ret = -EIO;
 	if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) {
-		printk("fuse_dev_write[%i]: write is short or long\n", fc->id);
+		printk("fuse_dev_write: write is short or long\n");
 		goto out;
 	}
 	
 	ret = -ENOMEM;
-	tmpbuf = kmalloc(nbytes, GFP_KERNEL);
+	tmpbuf = kmalloc(nbytes, GFP_NOFS);
 	if(!tmpbuf)
 		goto out;
 	
@@ -320,7 +321,6 @@
 
 static struct fuse_conn *new_conn(void)
 {
-	static int connctr = 1;
 	struct fuse_conn *fc;
 
 	fc = kmalloc(sizeof(*fc), GFP_KERNEL);
@@ -331,12 +331,6 @@
 		INIT_LIST_HEAD(&fc->pending);
 		INIT_LIST_HEAD(&fc->processing);
 		fc->reqctr = 1;
-		fc->cleared = NULL;
-		fc->numcleared = 0;
-		
-		spin_lock(&fuse_lock);
-		fc->id = connctr ++;
-		spin_unlock(&fuse_lock);
 	}
 	return fc;
 }
@@ -376,9 +370,6 @@
 	fc->file = NULL;
 	end_requests(&fc->pending);
 	end_requests(&fc->processing);
-	kfree(fc->cleared);
-	fc->cleared = NULL;
-	fc->numcleared = 0;
 	fuse_release_conn(fc);
 	spin_unlock(&fuse_lock);
 	return 0;
diff --git a/kernel/dir.c b/kernel/dir.c
index 1dac0c8..f506816 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -16,7 +16,6 @@
 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;
 
@@ -24,6 +23,9 @@
 
 static void change_attributes(struct inode *inode, struct fuse_attr *attr)
 {
+	if(S_ISREG(inode->i_mode) && inode->i_size != attr->size)
+		invalidate_inode_pages(inode);
+
 	inode->i_mode    = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
 	inode->i_nlink   = attr->nlink;
 	inode->i_uid     = attr->uid;
@@ -36,9 +38,10 @@
 	inode->i_ctime   = attr->ctime;
 }
 
-static void fuse_init_inode(struct inode *inode, int mode, int rdev)
+static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
 {
-	inode->i_mode = mode & S_IFMT;
+	inode->i_mode = attr->mode & S_IFMT;
+	inode->i_size = attr->size;
 	if(S_ISREG(inode->i_mode)) {
 		inode->i_op = &fuse_file_inode_operations;
 		fuse_init_file_inode(inode);
@@ -51,23 +54,24 @@
 		inode->i_op = &fuse_symlink_inode_operations;
 	}
 	else {
-		inode->i_op = &fuse_special_inode_operations;
-		init_special_inode(inode, inode->i_mode, rdev);
+		inode->i_op = &fuse_file_inode_operations;
+		init_special_inode(inode, inode->i_mode, attr->rdev);
 	}
 	inode->u.generic_ip = inode;
 }
 
 struct inode *fuse_iget(struct super_block *sb, ino_t ino,
-			struct fuse_attr *attr)
+			struct fuse_attr *attr, int version)
 {
 	struct inode *inode;
 
 	inode = iget(sb, ino);
 	if(inode) {
 		if(!inode->u.generic_ip)
-			fuse_init_inode(inode, attr->mode, attr->rdev);
+			fuse_init_inode(inode, attr);
 		
 		change_attributes(inode, attr);
+		inode->i_version = version;
 	}
 
 	return inode;
@@ -75,12 +79,13 @@
 
 static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
 {
+	int ret;
 	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 arg;
 	struct inode *inode;
-	
+
 	in.h.opcode = FUSE_LOOKUP;
 	in.h.ino = dir->i_ino;
 	in.argsize = entry->d_name.len + 1;
@@ -91,16 +96,24 @@
 	
 	inode = NULL;
 	if(!out.h.error) {
-		inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr);
+		ret = -ENOMEM;
+		inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr, out.h.unique);
 		if(!inode) 
-			return ERR_PTR(-ENOMEM);
+			goto err;
 	}
-	else if(out.h.error != -ENOENT)
-		return ERR_PTR(out.h.error);
+	else if(out.h.error != -ENOENT) {
+		ret = out.h.error;
+		goto err;
+	}
 
+	entry->d_time = jiffies;
 	entry->d_op = &fuse_dentry_opertations;
 	d_add(entry, inode);
+
 	return NULL;
+
+  err:
+	return ERR_PTR(ret);
 }
 
 /* create needs to return a positive entry, so this also does a lookup */
@@ -136,12 +149,11 @@
 	if(out.h.error) 
 		return out.h.error;
 
-	inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr);
+	inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, out.h.unique);
 	if(!inode) 
 		return -ENOMEM;
 
 	d_instantiate(entry, inode);
-
 	return 0;
 }
 
@@ -293,17 +305,16 @@
 	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)
+static int fuse_revalidate(struct dentry *entry)
 {
-	struct inode *inode = dentry->d_inode;
+	struct inode *inode = entry->d_inode;
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_getattr_out arg;
 	
-	if(inode->i_ino != FUSE_ROOT_INO)
+	if(inode->i_ino != FUSE_ROOT_INO && 
+	   time_before_eq(jiffies, entry->d_time + HZ / 100))
 		return 0;
 
 	in.h.opcode = FUSE_GETATTR;
@@ -435,7 +446,7 @@
 	struct fuse_getdir_out outarg;
 
 	if(!(file->f_flags & O_DIRECTORY))
-		return -EISDIR;
+		return 0;
 	
 	in.h.opcode = FUSE_GETDIR;
 	in.h.ino = inode->i_ino;
@@ -465,7 +476,9 @@
 static int fuse_dir_release(struct inode *inode, struct file *file)
 {
 	struct file *cfile = file->private_data;
-	fput(cfile);
+
+	if(cfile)
+		fput(cfile);
 
 	return 0;
 }
@@ -502,16 +515,26 @@
 	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
-	struct fuse_setattr_in arg;
+	struct fuse_setattr_in inarg;
+	struct fuse_setattr_out outarg;
 
-	arg.valid = iattr_to_fattr(attr, &arg.attr);
+	inarg.valid = iattr_to_fattr(attr, &inarg.attr);
 	
 	in.h.opcode = FUSE_SETATTR;
 	in.h.ino = inode->i_ino;
-	in.argsize = sizeof(arg);
-	in.arg = &arg;
+	in.argsize = sizeof(inarg);
+	in.arg = &inarg;
+	out.argsize = sizeof(outarg);
+	out.arg = &outarg;
 	request_send(fc, &in, &out);
 
+	if(!out.h.error && (attr->ia_valid & ATTR_SIZE)) {
+		if(outarg.newsize > attr->ia_size)
+			outarg.newsize = attr->ia_size;
+			
+		vmtruncate(inode, outarg.newsize);
+	}
+
 	return out.h.error;
 }
 
@@ -552,12 +575,6 @@
 	revalidate:	fuse_revalidate,
 };
 
-static struct inode_operations fuse_special_inode_operations = {
-	setattr:	fuse_setattr,
-	permission:	fuse_permission,
-	revalidate:	fuse_revalidate,
-};
-
 static struct inode_operations fuse_symlink_inode_operations =
 {
 	setattr:	fuse_setattr,
diff --git a/kernel/file.c b/kernel/file.c
index df3a863..a677d4c 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/pagemap.h>
+#include <linux/slab.h>
 
 
 static int fuse_open(struct inode *inode, struct file *file)
@@ -25,10 +26,13 @@
 	in.argsize = sizeof(arg);
 	in.arg = &arg;
 	request_send(fc, &in, &out);
+	if(!out.h.error)
+		invalidate_inode_pages(inode);
 
 	return out.h.error;
 }
 
+
 static int fuse_readpage(struct file *file, struct page *page)
 {
 	struct inode *inode = page->mapping->host;
@@ -65,13 +69,96 @@
 	return out.h.error;
 }
 
+static int write_buffer(struct inode *inode, struct page *page,
+			unsigned offset, size_t count)
+{
+	struct fuse_conn *fc = INO_FC(inode);
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_write_in *arg;
+	size_t argsize;
+	char *buffer;
+
+	argsize = offsetof(struct fuse_write_in, buf) + count;
+	arg = kmalloc(argsize, GFP_KERNEL);
+	if(!arg)
+		return -ENOMEM;
+
+	arg->offset = (page->index << PAGE_CACHE_SHIFT) + offset;
+	arg->size = count;
+	buffer = kmap(page);
+	memcpy(arg->buf, buffer + offset, count);
+	kunmap(page);
+	
+	in.h.opcode = FUSE_WRITE;
+	in.h.ino = inode->i_ino;
+	in.argsize = argsize;
+	in.arg = arg;
+	request_send(fc, &in, &out);
+	kfree(arg);
+
+	return out.h.error;
+}
+
+
+static int fuse_writepage(struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	unsigned count;
+	unsigned long end_index;
+	int err;
+	
+	end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+	if(page->index < end_index)
+		count = PAGE_CACHE_SIZE;
+	else {
+		count = inode->i_size & (PAGE_CACHE_SIZE - 1);
+		err = -EIO;
+		if(page->index > end_index || count == 0)
+			goto out;
+
+	}
+	err = write_buffer(inode, page, 0, count);
+  out:
+	UnlockPage(page);
+	return 0;
+}
+
+
+static int fuse_prepare_write(struct file *file, struct page *page,
+			      unsigned offset, unsigned to)
+{
+	/* No op */
+	return 0;
+}
+
+static int fuse_commit_write(struct file *file, struct page *page,
+			     unsigned offset, unsigned to)
+{
+	int err;
+	struct inode *inode = page->mapping->host;
+
+	err = write_buffer(inode, page, offset, to - offset);
+	if(!err) {
+		loff_t pos = (page->index << PAGE_CACHE_SHIFT) + to;
+		if(pos > inode->i_size)
+			inode->i_size = pos;
+	}
+	return err;
+}
+
 static struct file_operations fuse_file_operations = {
 	open:		fuse_open,
 	read:		generic_file_read,
+	write:		generic_file_write,
+	mmap:		generic_file_mmap,
 };
 
 static struct address_space_operations fuse_file_aops  = {
 	readpage:	fuse_readpage,
+	writepage:	fuse_writepage,
+	prepare_write:	fuse_prepare_write,
+	commit_write:	fuse_commit_write,
 };
 
 void fuse_init_file_inode(struct inode *inode)
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index aaa395d..b738bed 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -10,6 +10,7 @@
 #include <linux/fs.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/rwsem.h>
 
 #define FUSE_VERSION "0.1"
 
@@ -38,15 +39,6 @@
 	/** The list of requests being processed */
 	struct list_head processing;
 
-	/** The number of cleared inodes */
-	unsigned int numcleared;
-	
-	/** The array of cleared inode numbers */
-	unsigned long *cleared;
-
-	/** Connnection number (for debuging) */
-	int id;
-
 	/** The request id */
 	int reqctr;
 };
@@ -70,8 +62,7 @@
 	/** The request input */
 	char *in;
 	
-	/** The maximum request output size, if zero, then the request is
-            asynchronous */
+	/** The maximum request output size */
 	unsigned int outsize;
 
 	/** The request output */
@@ -98,7 +89,7 @@
 	void *arg;
 };
 
-#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
+#define FUSE_IN_INIT { {0, 0, 0, current->fsuid, current->fsgid}, 0, 0 }
 #define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }
 
 
@@ -117,7 +108,7 @@
  * Get a filled in inode
  */
 struct inode *fuse_iget(struct super_block *sb, ino_t ino,
-			struct fuse_attr *attr);
+			struct fuse_attr *attr, int version);
 
 
 /**
diff --git a/kernel/inode.c b/kernel/inode.c
index ed4f760..77e8469 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -21,68 +21,20 @@
 	/* No op */
 }
 
-static void send_forget(struct fuse_conn *fc, unsigned long *forget,
-			unsigned int numforget)
-{
-	struct fuse_in in = FUSE_IN_INIT;
-	
-	in.h.opcode = FUSE_FORGET;
-	in.h.ino = 0;
-	in.argsize = numforget * sizeof(unsigned long);
-	in.arg = forget;
-	
-	request_send(fc, &in, NULL);
-}
-
-static int alloc_cleared(struct fuse_conn *fc)
-{
-	unsigned long *tmp;
-	
-	spin_unlock(&fuse_lock);
-	tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_NOFS);
-	spin_lock(&fuse_lock);
-
-	if(!fc->file || fc->cleared != NULL)
-		kfree(tmp);
-	else if(!tmp)
-		printk("fuse_clear_inode: Cannot allocate memory\n");
-	else
-		fc->cleared = tmp;
-
-	return fc->cleared != NULL;
-}
-
-static unsigned long *add_cleared(struct fuse_conn *fc, unsigned long ino)
-{
-	if(!fc->file || (!fc->cleared && !alloc_cleared(fc)))
-		return NULL;
-
-	fc->cleared[fc->numcleared] = ino;
-	fc->numcleared ++;
-	
-	if(fc->numcleared == MAX_CLEARED) {
-		unsigned long *tmp = fc->cleared;
-		fc->cleared = NULL;
-		fc->numcleared = 0;
-		return tmp;
-	}
-	
-	return NULL;
-}
-
 static void fuse_clear_inode(struct inode *inode)
 {
 	struct fuse_conn *fc = INO_FC(inode);
-	unsigned long *forget;
-
-	spin_lock(&fuse_lock);
-	forget = add_cleared(fc, inode->i_ino);
-	spin_unlock(&fuse_lock);
-
-	if(forget) {
-		send_forget(fc, forget, MAX_CLEARED);
-		kfree(forget);
-	}
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_forget_in arg;
+	
+	arg.version = inode->i_version;
+	
+	in.h.opcode = FUSE_FORGET;
+	in.h.ino = inode->i_ino;
+	in.argsize = sizeof(arg);
+	in.arg = &arg;
+	
+	request_send(fc, &in, NULL);
 }
 
 static void fuse_put_super(struct super_block *sb)
@@ -143,7 +95,7 @@
 	memset(&attr, 0, sizeof(attr));
 
 	attr.mode = S_IFDIR;
-	return fuse_iget(sb, 1, &attr);
+	return fuse_iget(sb, 1, &attr, 0);
 }
 
 static struct super_block *fuse_read_super(struct super_block *sb, 
@@ -169,8 +121,7 @@
 		goto err;
 
 	if(fc->sb != NULL) {
-		printk("fuse_read_super: connection %i already mounted\n",
-		       fc->id);
+		printk("fuse_read_super: connection already mounted\n");
 		goto err;
 	}
 
@@ -213,6 +164,5 @@
  * Local Variables:
  * indent-tabs-mode: t
  * c-basic-offset: 8
- * End:
- */
+ * End: */
 
diff --git a/kernel/redir_hack.c b/kernel/redir_hack.c
new file mode 100644
index 0000000..4c91a4f
--- /dev/null
+++ b/kernel/redir_hack.c
@@ -0,0 +1,787 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/unistd.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <linux/coda_psdev.h>
+#include <linux/version.h>
+
+#if 0
+#define DEB(X) printk X
+#else
+#define DEB(X)
+#endif
+
+#define REDIR_VERSION "0.3"
+
+extern void *sys_call_table[];
+
+typedef asmlinkage int (*chdir_func)    (const char *);
+typedef asmlinkage int (*stat_func)     (const char *, struct stat *);
+typedef asmlinkage int (*access_func)   (const char *, int);
+typedef asmlinkage int (*open_func)     (const char *, int, int);
+typedef asmlinkage int (*readlink_func) (const char *, char *, int);
+typedef asmlinkage int (*getcwd_func)   (char *, unsigned long);
+
+static chdir_func    orig_chdir;
+static stat_func     orig_stat;
+static stat_func     orig_lstat;
+static access_func   orig_access;
+static open_func     orig_open;
+static readlink_func orig_readlink;
+static getcwd_func   orig_getcwd;
+
+typedef asmlinkage long (*stat64_func)   (const char *, struct stat64 *, long);
+
+static stat64_func   orig_stat64;
+static stat64_func   orig_lstat64;
+
+#ifdef __i386__
+typedef asmlinkage int (*execve_func) (struct pt_regs);
+
+static execve_func orig_execve;
+#endif
+
+#define AVFS_MAGIC_CHAR '#'
+
+#define PF_AVFS 0x00008000
+
+#define OVERLAY_BASE "/mnt/tmp"
+
+#define path_ok(pwd) (pwd->d_parent == pwd || !list_empty(&pwd->d_hash))
+
+static char *path_pwd(char *page)
+{
+        return d_path(current->fs->pwd, current->fs->pwdmnt, page, PAGE_SIZE);
+}
+
+static int a_path_walk(const char *pathname, int flags, struct nameidata *nd)
+{
+        int error;
+
+        error = 0;
+        if (path_init(pathname, flags, nd))
+                error = path_walk(pathname, nd);
+
+        return error;
+}
+
+static void a_path_release(struct nameidata *nd)
+{
+        dput(nd->dentry);
+        mntput(nd->mnt);
+}
+
+static char *resolv_virt(const char *pathname, int must_exist, int flags)
+{
+        struct nameidata root;
+	struct nameidata nd;
+        struct dentry *origroot;
+        struct vfsmount *origrootmnt;
+	char *newpathname = NULL;
+	char *page = NULL;
+	char *path = NULL;
+	int pathlen = 0;
+        int error;
+        int newflags;
+        char overlay_dir[128];
+        unsigned overlay_dir_len;
+        
+        sprintf(overlay_dir, "%s/%u", OVERLAY_BASE, current->fsuid);
+        overlay_dir_len = strlen(overlay_dir);
+        
+	lock_kernel();
+
+	DEB((KERN_INFO "resolve_virt pathname: '%s'\n", 
+	       pathname ? pathname : "(null)"));
+
+        error = a_path_walk(overlay_dir, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &root);
+        if(error)
+            goto out;
+
+	origroot = current->fs->root;
+        origrootmnt = current->fs->rootmnt;
+
+	current->fs->root = root.dentry;
+        current->fs->rootmnt = root.mnt;
+	
+        newflags = flags;
+        if(must_exist)
+                newflags |= LOOKUP_POSITIVE;
+
+        error  = a_path_walk(pathname, newflags, &nd);
+	if(!error) {
+		if(path_ok(nd.dentry)) {
+			page = (char *) __get_free_page(GFP_USER);
+			if(page) {
+				path = d_path(nd.dentry, nd.mnt, page,
+                                              PAGE_SIZE);
+				DEB((KERN_INFO "resolve_virt path = '%s'\n",
+				     path));
+				pathlen = (unsigned int) page + PAGE_SIZE - 
+					(unsigned int) path;
+			}
+		}
+                a_path_release(&nd);
+	}
+
+	current->fs->root = origroot;
+        current->fs->rootmnt = origrootmnt;
+
+        a_path_release(&root);
+
+	if(path) {
+		int isvirtual;
+
+                error  = a_path_walk(path, flags, &nd);
+		if(!error) {
+			if(nd.dentry->d_inode)
+				isvirtual = 0;
+			else if(must_exist)
+				isvirtual = 1;
+			else if(strchr(path, AVFS_MAGIC_CHAR))
+				isvirtual = 1;
+			else 
+				isvirtual = 0;
+
+                        a_path_release(&nd);
+		}
+		else {
+			isvirtual = 1;
+		}
+
+		if(!isvirtual) {
+			newpathname = kmalloc(pathlen + 1, GFP_USER);
+			if(newpathname)
+				strncpy(newpathname, path, pathlen);
+		}
+		else {
+			newpathname = kmalloc(overlay_dir_len + pathlen + 1,
+					      GFP_USER);
+
+			if(newpathname) {
+				strcpy(newpathname, overlay_dir);
+				strncat(newpathname, path, pathlen);
+			}
+		}
+	}
+
+	if(page)
+		free_page((unsigned long) page);
+
+
+	DEB((KERN_INFO "resolve_virt newpathname: '%s'\n", 
+	     newpathname ? newpathname : "(null)"));
+
+  out:
+	unlock_kernel();
+	return newpathname;
+}
+
+
+#define FUSE_SUPER_MAGIC 0x65735546
+
+#define cwd_virtual() \
+	(current->fs->pwd->d_sb->s_magic == FUSE_SUPER_MAGIC)
+
+static char *get_abs_path(const char *filename)
+{
+	char *cwd;
+	int cwdlen, fnamelen;
+	char *abspath, *s;
+	char *page;
+        char overlay_dir[128];
+        unsigned overlay_dir_len;
+        
+        sprintf(overlay_dir, "/mnt/avfs/%010u", current->fsuid);
+        overlay_dir_len = strlen(overlay_dir);
+
+        if(!path_ok(current->fs->pwd))
+                return NULL;
+
+        page = (char *) __get_free_page(GFP_USER);
+	if(!page)
+		return NULL;
+
+        cwd = path_pwd(page);
+	cwdlen = (unsigned int) page + PAGE_SIZE - (unsigned int) cwd - 1;
+	if(cwd_virtual() && cwdlen > overlay_dir_len) {
+		cwd += overlay_dir_len;
+		cwdlen -= overlay_dir_len;
+	}
+		
+
+	fnamelen = strlen(filename);
+
+	abspath = kmalloc(cwdlen + 1 + fnamelen + 1, GFP_USER);
+	if(abspath) {
+		s = abspath;
+		strncpy(s, cwd, cwdlen);
+		s += cwdlen;
+		*s++ = '/';
+		strncpy(s, filename, fnamelen + 1);
+	}
+	free_page((unsigned long) page);
+	
+	return abspath;
+}
+
+static char *resolve_name(const char *kfilename, int must_exist, int flags)
+{
+	char *tmp;
+	char *newfilename;		
+
+	tmp = getname(kfilename);
+	if(IS_ERR(tmp))
+		return tmp;
+
+
+	if((tmp[0] != '/' && cwd_virtual()) || strchr(tmp, AVFS_MAGIC_CHAR)) {
+		DEB((KERN_INFO "resolve_name: %s (%i/%s)\n", tmp, 
+		     current->pid,
+		     (current->flags & PF_AVFS) ? "on" : "off"));
+
+		if(strcmp(tmp, "/#avfs-on") == 0) {
+			printk(KERN_INFO "AVFS ON  (pid: %i)\n",
+			       current->pid);
+			current->flags |= PF_AVFS;
+			newfilename = ERR_PTR(-EEXIST);
+		}
+		else if(!(current->flags & PF_AVFS))
+			newfilename = NULL;
+		else if(strcmp(tmp, "/#avfs-off") == 0) {
+			printk(KERN_INFO "AVFS OFF (pid: %i)\n",
+			       current->pid);
+			current->flags &= ~PF_AVFS;
+			newfilename = ERR_PTR(-EEXIST);
+		}
+		else {
+			if(tmp[0] == '/') {
+				newfilename = resolv_virt(tmp, must_exist, flags);
+			}
+			else {
+				char *abspath;
+
+				abspath = get_abs_path(tmp);
+				if(abspath) {
+					newfilename = resolv_virt(abspath, must_exist, flags);
+					kfree(abspath);
+				}
+				else
+					newfilename = NULL;
+			}
+		}
+	}
+	else 
+		newfilename = NULL;
+	
+	putname(tmp);
+	
+	return newfilename;
+}
+
+asmlinkage int virt_chdir(const char *filename)
+{
+	int ret;
+	mm_segment_t old_fs;
+	char *newfilename;
+	
+	if(!cwd_virtual()) {
+		ret = (*orig_chdir)(filename);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 1);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return (*orig_chdir)(filename);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+
+	DEB((KERN_INFO "CHDIR: trying '%s'\n", newfilename));
+		
+	old_fs = get_fs();
+	set_fs(get_ds());
+	ret =  (*orig_chdir)(newfilename);
+	set_fs(old_fs);
+	kfree(newfilename);
+
+	DEB((KERN_INFO "CHDIR: result %i\n", ret));
+	
+	return ret;
+}
+
+static int do_orig_stat(stat_func sfunc, const char *filename,
+			struct stat *statbuf)
+{
+	int ret;
+	mm_segment_t old_fs;
+	struct stat locbuf;
+
+	old_fs = get_fs();
+	set_fs(get_ds());
+	ret =  (*sfunc)(filename, &locbuf);
+	set_fs(old_fs);
+
+	if(ret == 0)
+		ret = (copy_to_user(statbuf, &locbuf, sizeof(locbuf)) ? 
+		       -EFAULT : 0);
+
+	return ret;
+}
+
+asmlinkage int virt_stat(const char *filename, struct stat *statbuf)
+{
+	int ret;
+	char *newfilename;
+
+	if(!cwd_virtual()) {
+		ret = (*orig_stat)(filename, statbuf);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 1);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return (*orig_stat)(filename, statbuf);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+	DEB((KERN_INFO "STAT: trying '%s'\n", newfilename));
+
+	ret = do_orig_stat(orig_stat, newfilename, statbuf);
+	kfree(newfilename);
+
+	DEB((KERN_INFO "STAT: result %i\n", ret));
+
+	return ret;
+}
+
+asmlinkage int virt_lstat(const char *filename, struct stat *statbuf)
+{
+	int ret;
+	char *newfilename;
+
+	if(!cwd_virtual()) {
+		ret = (*orig_lstat)(filename, statbuf);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 0);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return (*orig_lstat)(filename, statbuf);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+	DEB((KERN_INFO "LSTAT: trying '%s'\n", newfilename));
+
+	ret = do_orig_stat(orig_lstat, newfilename, statbuf);
+	kfree(newfilename);
+
+	DEB((KERN_INFO "LSTAT: result %i\n", ret));
+
+	return ret;
+}
+
+
+asmlinkage int virt_access(const char *filename, int mode)
+{
+	int ret;
+	mm_segment_t old_fs;
+	char *newfilename;
+	
+	if(!cwd_virtual()) {
+		ret = (*orig_access)(filename, mode);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 1);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return (*orig_access)(filename, mode);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+	DEB((KERN_INFO "ACCESS: trying '%s'\n", newfilename));
+		
+	old_fs = get_fs();
+	set_fs(get_ds());
+	ret = (*orig_access)(newfilename, mode);
+	set_fs(old_fs);
+	kfree(newfilename);
+
+	DEB((KERN_INFO "ACCESS: result %i\n", ret));
+	
+	return ret;
+}
+
+asmlinkage int virt_open(const char *filename, int flags, int mode)
+{
+	int ret;
+	mm_segment_t old_fs;
+	char *newfilename;
+	
+	if(!cwd_virtual()) {
+		ret = (*orig_open)(filename, flags, mode);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 1);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return (*orig_open)(filename, flags, mode);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+	DEB((KERN_INFO "OPEN: trying '%s'\n", newfilename));
+		
+	old_fs = get_fs();
+	set_fs(get_ds());
+	ret = (*orig_open)(newfilename, flags, mode);
+	set_fs(old_fs);
+	kfree(newfilename);
+
+	DEB((KERN_INFO "OPEN: result %i\n", ret));
+	
+	return ret;
+}
+
+asmlinkage int virt_readlink(const char *filename, char *buf, int bufsiz)
+{
+	int ret;
+	mm_segment_t old_fs;
+	char *newfilename;
+	char *locbuf;
+	int len;
+	
+	if(!cwd_virtual()) {
+		ret = (*orig_readlink)(filename, buf, bufsiz);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 0);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return (*orig_readlink)(filename, buf, bufsiz);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+	DEB((KERN_INFO "READLINK: trying '%s'\n", newfilename));
+
+	/* bufsiz is legal (already checked by sys_readlink) */
+	len = bufsiz;
+	if(bufsiz > PAGE_SIZE)
+		len = PAGE_SIZE;
+			
+	locbuf = (char *) __get_free_page(GFP_USER);
+			
+	ret = -ENOMEM;
+	if(locbuf) {
+		old_fs = get_fs();
+		set_fs(get_ds());
+		ret =  (*orig_readlink)(newfilename, locbuf, len);
+		set_fs(old_fs);
+
+		if(ret >= 0)
+			if(copy_to_user(buf, locbuf, len))
+				ret = -EFAULT;
+		free_page((unsigned long) locbuf);
+	}
+	kfree(newfilename);
+
+	DEB((KERN_INFO "READLINK: result %i\n", ret));
+	
+	return ret;
+}
+
+asmlinkage int virt_getcwd(char *buf, unsigned long size)
+{
+	int ret;
+	char *cwd;
+	unsigned long cwdlen;
+	char *page;
+	char *newcwd;
+	unsigned long newlen;
+        char overlay_dir[128];
+        unsigned overlay_dir_len;
+        	
+	ret = (*orig_getcwd)(buf, size);
+
+	if(!cwd_virtual() || ret < 0)
+		return ret;
+		
+	if(!path_ok(current->fs->pwd))
+		return -ENOENT;
+	
+	page = (char *) __get_free_page(GFP_USER);
+	if(!page)
+		return -ENOMEM;
+	
+        cwd = path_pwd(page);
+	cwdlen = PAGE_SIZE + (page - cwd) - 1;
+	
+        sprintf(overlay_dir, "/mnt/avfs/%010u", current->fsuid);
+        overlay_dir_len = strlen(overlay_dir);
+
+	if(cwdlen >= overlay_dir_len && 
+	   strncmp(cwd, overlay_dir, overlay_dir_len) == 0) {
+		if(cwdlen == overlay_dir_len) {
+			newcwd = "/";
+			newlen = 1;
+		}
+		else {
+			newcwd = cwd + overlay_dir_len;
+			newlen = cwdlen - overlay_dir_len;
+		}
+
+		ret = -ERANGE;
+		if(newlen + 1 <= size) {
+			ret = newlen + 1;
+			if(copy_to_user(buf, newcwd, newlen + 1))
+				ret = -EFAULT;
+		}
+	}
+	free_page((unsigned long) page);
+
+	return ret;
+}
+
+
+static long do_orig_stat64(stat64_func sfunc, const char *filename,
+			  struct stat64 * statbuf, long flags)
+{
+	long ret;
+	mm_segment_t old_fs;
+	struct stat64 locbuf;
+
+	old_fs = get_fs();
+	set_fs(get_ds());
+	ret =  (*sfunc)(filename, &locbuf, flags);
+	set_fs(old_fs);
+
+	if(ret == 0)
+		ret = (copy_to_user(statbuf, &locbuf, sizeof(locbuf)) ? 
+		       -EFAULT : 0);
+
+	return ret;
+}
+
+asmlinkage long virt_stat64(char * filename, struct stat64 * statbuf, long flags)
+{
+	long ret;
+	char *newfilename;
+
+	if(!cwd_virtual()) {
+		ret = (*orig_stat64)(filename, statbuf, flags);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 1);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return (*orig_stat64)(filename, statbuf, flags);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+	DEB((KERN_INFO "STAT64: trying '%s'\n", newfilename));
+
+	ret = do_orig_stat64(orig_stat64, newfilename, statbuf, flags);
+	kfree(newfilename);
+
+	DEB((KERN_INFO "STAT64: result %li\n", ret));
+
+	return ret;
+}
+
+asmlinkage long virt_lstat64(char * filename, struct stat64 * statbuf, long flags)
+{
+	long ret;
+	char *newfilename;
+
+	if(!cwd_virtual()) {
+		ret = (*orig_lstat64)(filename, statbuf, flags);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 0);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return (*orig_lstat64)(filename, statbuf, flags);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+	DEB((KERN_INFO "LSTAT64: trying '%s'\n", newfilename));
+
+	ret = do_orig_stat64(orig_lstat64, newfilename, statbuf, flags);
+	kfree(newfilename);
+
+	DEB((KERN_INFO "LSTAT64: result %li\n", ret));
+
+	return ret;
+}
+
+#ifdef __i386__
+
+asmlinkage int real_execve(struct pt_regs *regs)
+{
+	int error;
+	char * filename;
+
+	filename = getname((char *) regs->ebx);
+	error = PTR_ERR(filename);
+	if (IS_ERR(filename))
+		goto out;
+	error = do_execve(filename, (char **) regs->ecx, (char **) regs->edx, regs);
+	if (error == 0)
+		current->ptrace &= ~PT_DTRACE;
+	putname(filename);
+
+out:
+	return error;
+}
+
+asmlinkage int virt_execve(struct pt_regs regs)
+{
+	int ret;
+	char *newfilename;
+	char *filename = (char *) regs.ebx;
+
+	if(!cwd_virtual()) {
+		ret = real_execve(&regs);
+		if(ret != -ENOENT) 
+			return ret;
+	}
+	else 
+		ret = 0;
+
+	newfilename = resolve_name(filename, 1, 1);
+	if(!newfilename) {
+		if(ret)
+			return ret;
+		else
+			return real_execve(&regs);
+	}
+	if(IS_ERR(newfilename))
+			return PTR_ERR(newfilename);
+
+	DEB((KERN_INFO "EXECVE: trying '%s'\n", newfilename));
+
+	ret = do_execve(newfilename, (char **) regs.ecx, (char **) regs.edx,
+			&regs);
+	if (ret == 0)
+		current->ptrace &= ~PT_DTRACE;
+	kfree(newfilename);
+
+	DEB((KERN_INFO "EXECVE: result %i\n", ret));
+
+	return ret;
+}
+#endif /* __i386__ */
+
+void *replace_syscall(int index, void *new_syscall)
+{
+	void *orig_syscall = sys_call_table[index];
+
+	printk(KERN_INFO "replacing syscall nr. %3i [%p] with [%p]\n", 
+	       index, orig_syscall, new_syscall);
+	sys_call_table[index] = new_syscall;
+	
+	return orig_syscall;
+}
+
+int init_module(void)
+{
+    printk(KERN_INFO "redir init (version %s)\n", REDIR_VERSION);
+
+    orig_chdir    = replace_syscall(__NR_chdir,    virt_chdir);
+    orig_stat     = replace_syscall(__NR_stat,     virt_stat);
+    orig_lstat    = replace_syscall(__NR_lstat,    virt_lstat);
+    orig_access   = replace_syscall(__NR_access,   virt_access);
+    orig_open     = replace_syscall(__NR_open,     virt_open);
+    orig_readlink = replace_syscall(__NR_readlink, virt_readlink);
+    orig_getcwd   = replace_syscall(__NR_getcwd,   virt_getcwd);
+
+    orig_stat64   = replace_syscall(__NR_stat64,   virt_stat64);
+    orig_lstat64  = replace_syscall(__NR_lstat64,  virt_lstat64);
+
+#ifdef __i386__
+    orig_execve   = replace_syscall(__NR_execve,   virt_execve);
+#endif
+
+    return 0;
+}
+
+
+void cleanup_module(void)
+{
+    printk(KERN_INFO "redir cleanup\n");
+   
+    replace_syscall(__NR_chdir,    orig_chdir);
+    replace_syscall(__NR_stat,     orig_stat);
+    replace_syscall(__NR_lstat,    orig_lstat);
+    replace_syscall(__NR_access,   orig_access);
+    replace_syscall(__NR_open,     orig_open);
+    replace_syscall(__NR_readlink, orig_readlink);
+    replace_syscall(__NR_getcwd,   orig_getcwd);
+
+    replace_syscall(__NR_stat64,   orig_stat64);
+    replace_syscall(__NR_lstat64,  orig_lstat64);
+
+#ifdef __i386__
+    replace_syscall(__NR_execve,   orig_execve);
+#endif
+
+}
diff --git a/lib/fuse.c b/lib/fuse.c
index cd919ed..137b5ef 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -9,12 +9,23 @@
 #include "fuse_i.h"
 #include <linux/fuse.h>
 
+
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
 
 
+static inline struct node *get_node(fino_t ino)
+{
+    return (struct node *) ((ino << 3) + 0x8000000);
+}
+
+static inline fino_t get_ino(struct node *node)
+{
+    return (((fino_t) node) - 0x8000000) >> 3;
+}
+
 static guint name_hash(const struct node *node)
 {
     return g_str_hash(node->name) ^ node->parent;
@@ -27,15 +38,6 @@
         strcmp(node1->name, node2->name) == 0;
 }
 
-static struct node *new_node(fino_t parent, const char *name, int mode)
-{
-    struct node *node = g_new0(struct node, 1);
-    node->name = g_strdup(name);
-    node->parent = parent;
-    node->mode = mode;
-    return node;
-}
-
 static int free_node(struct node *node)
 {
     g_free(node->name);
@@ -43,16 +45,6 @@
     return 1;
 }
 
-static inline struct node *get_node(fino_t ino)
-{
-    return (struct node *) ((ino << 3) + 0x8000000);
-}
-
-static inline fino_t get_ino(struct node *node)
-{
-    return (((fino_t) node) - 0x8000000) >> 3;
-}
-
 static struct node *lookup_node(struct fuse *f, fino_t parent,
                                 const char *name)
 {
@@ -64,29 +56,51 @@
     return g_hash_table_lookup(f->nametab, &tmp);
 }
 
-static void unhash_node(struct fuse *f, struct node *node)
+static void hash_node(struct fuse *f, struct node *node, fino_t parent,
+                      const char *name)
 {
-    g_hash_table_remove(f->nametab, node);
-    g_free(node->name);
-    node->parent = 0;
-    node->name = NULL;
+    node->name = g_strdup(name);
+    node->parent = parent;
+    g_hash_table_insert(f->nametab, node, node);
 }
 
-static fino_t find_node(struct fuse *f, fino_t parent, char *name, int mode)
+static void unhash_node(struct fuse *f, struct node *node)
+{
+    if(node->name != NULL) {
+        g_hash_table_remove(f->nametab, node);
+        g_free(node->name);
+        node->parent = 0;
+        node->name = NULL;
+    }
+}
+
+static fino_t find_node(struct fuse *f, fino_t parent, char *name,
+                        struct fuse_attr *attr, int version)
 {
     struct node *node;
-    mode &= S_IFMT;
+    int mode = attr->mode & S_IFMT;
+    int rdev = 0;
+    
+    if(S_ISCHR(mode) || S_ISBLK(mode))
+        rdev = attr->rdev;
 
+    pthread_mutex_lock(&f->lock);
     node = lookup_node(f, parent, name);
     if(node != NULL) {
-        if(node->mode == mode)
-            return get_ino(node);
-
+        if(node->mode == mode && node->rdev == rdev)
+            goto out;
+        
         unhash_node(f, node);
     }
 
-    node = new_node(parent, name, mode);
-    g_hash_table_insert(f->nametab, node, node);
+    node = g_new0(struct node, 1);
+    node->mode = mode;
+    node->rdev = rdev;    
+    hash_node(f, node, parent, name);
+
+  out:
+    node->version = version;
+    pthread_mutex_unlock(&f->lock);
     return get_ino(node);
 }
 
@@ -94,14 +108,17 @@
 {
     struct node *node;
 
+    pthread_mutex_lock(&f->lock);
     node = lookup_node(f, parent, name);
+    pthread_mutex_unlock(&f->lock);
+
     if(node != NULL)
         return get_ino(node);
     else
         return (fino_t) -1;
 }
 
-static char *get_path(fino_t ino)
+static char *get_path(struct fuse *f, fino_t ino)
 {
     GString *s;
     char *ss;
@@ -111,15 +128,18 @@
         g_string_prepend_c(s, '/');
     else {
         struct node *node;
+        pthread_mutex_lock(&f->lock);
         for(; ino != FUSE_ROOT_INO; ino = node->parent) {
             node = get_node(ino);
             if(node->name == NULL) {
+                pthread_mutex_unlock(&f->lock);
                 g_string_free(s, TRUE);
                 return NULL;
             }
             g_string_prepend(s, node->name);
             g_string_prepend_c(s, '/');
         }
+        pthread_mutex_unlock(&f->lock);
     }
 
     ss = s->str;
@@ -128,49 +148,66 @@
     return ss;
 }
 
-static char *get_path_name(fino_t ino, const char *name)
+static char *get_path_name(struct fuse *f, fino_t ino, const char *name)
 {
-    char *path;
     char *path2;
+    char *path;
     
-    path = get_path(ino);
+    if(ino == FUSE_ROOT_INO)
+        return g_strconcat("/", name, NULL);
+
+    path = get_path(f, ino);
     if(path == NULL)
         return NULL;
-
+    
     path2 = g_strconcat(path, "/", name, NULL);
     g_free(path);
+
     return path2;
 }
 
-static void destroy_node(struct fuse *f, fino_t ino)
+static void destroy_node(struct fuse *f, fino_t ino, int version)
 {
-    struct node *node = get_node(ino);
-    unhash_node(f, node);
-    free_node(node);
+    struct node *node;
+
+    pthread_mutex_lock(&f->lock);
+    node = get_node(ino);
+    if(node->version == version) {
+        unhash_node(f, node);
+        free_node(node);
+    }
+    pthread_mutex_unlock(&f->lock);
+
 }
 
 static void remove_node(struct fuse *f, fino_t dir, const char *name)
 {
-    struct node *node = lookup_node(f, dir, name);
+    struct node *node;
+
+    pthread_mutex_lock(&f->lock);
+    node = lookup_node(f, dir, name);
     assert(node != NULL);
     unhash_node(f, node);
+    pthread_mutex_unlock(&f->lock);
 }
 
 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);
+    struct node *node;
+    struct node *newnode;
+    
+    pthread_mutex_lock(&f->lock);
+    node  = lookup_node(f, olddir, oldname);
+    newnode  = lookup_node(f, newdir, newname);
     assert(node != NULL);
 
     if(newnode != NULL)
         unhash_node(f, newnode);
         
-    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);
+    unhash_node(f, node);
+    hash_node(f, node, newdir, newname);
+    pthread_mutex_unlock(&f->lock);
 }
 
 
@@ -189,7 +226,7 @@
     attr->ctime   = stbuf->st_ctime;
 }
 
-static int fill_dir(struct fuse_dh *dh, char *name, int type)
+static int fill_dir(struct fuse_dirhandle *dh, char *name, int type)
 {
     struct fuse_dirent dirent;
     size_t reclen;
@@ -234,6 +271,7 @@
 
     printf("   unique: %i, error: %i (%s), outsize: %i\n", out->unique,
            out->error, strerror(-out->error), outsize);
+    fflush(stdout);
                 
     res = write(f->fd, outbuf, outsize);
     if(res == -1)
@@ -242,34 +280,40 @@
     g_free(outbuf);
 }
 
+static void fill_cred(struct fuse_in_header *in, struct fuse_cred *cred)
+{
+    cred->uid = in->uid;
+    cred->gid = in->gid;
+}
+
 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;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path_name(in->ino, name);
+    path = get_path_name(f, in->ino, name);
     if(path != NULL) {
         res = -ENOSYS;
         if(f->op.getattr)
-            res = f->op.getattr(path, &buf);
+            res = f->op.getattr(&cred, path, &buf);
         g_free(path);
     }
     if(res == 0) {
         convert_stat(&buf, &arg.attr);
-        arg.ino = find_node(f, in->ino, name, arg.attr.mode);
+        arg.ino = find_node(f, in->ino, name, &arg.attr, in->unique);
     }
     send_reply(f, in, res, &arg, sizeof(arg));
 }
 
-static void do_forget(struct fuse *f, unsigned long *inos, size_t num)
+static void do_forget(struct fuse *f, struct fuse_in_header *in,
+                      struct fuse_forget_in *arg)
 {
-    size_t i;
-
-    for(i = 0; i < num; i++)
-        destroy_node(f, inos[i]);
+    destroy_node(f, in->ino, arg->version);
 }
 
 static void do_getattr(struct fuse *f, struct fuse_in_header *in)
@@ -278,13 +322,15 @@
     char *path;
     struct stat buf;
     struct fuse_getattr_out arg;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path(in->ino);
+    path = get_path(f, in->ino);
     if(path != NULL) {
         res = -ENOSYS;
         if(f->op.getattr)
-            res = f->op.getattr(path, &buf);
+            res = f->op.getattr(&cred, path, &buf);
         g_free(path);
     }
     if(res == 0) 
@@ -300,15 +346,18 @@
     char *path;
     int valid = arg->valid;
     struct fuse_attr *attr = &arg->attr;
+    struct fuse_setattr_out outarg;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path(in->ino);
+    path = get_path(f, in->ino);
     if(path != NULL) {
         res = 0;
         if(!res && (valid & FATTR_MODE)) {
             res = -ENOSYS;
             if(f->op.chmod)
-                res = f->op.chmod(path, attr->mode);
+                res = f->op.chmod(&cred, path, attr->mode);
         }        
         if(!res && (valid & (FATTR_UID | FATTR_GID))) {
             uid_t uid = (valid & FATTR_UID) ? attr->uid : (uid_t) -1;
@@ -316,12 +365,18 @@
             
             res = -ENOSYS;
             if(f->op.chown)
-                res = f->op.chown(path, uid, gid);
+                res = f->op.chown(&cred, path, uid, gid);
         }
         if(!res && (valid & FATTR_SIZE)) {
             res = -ENOSYS;
-            if(f->op.truncate)
-                res = f->op.truncate(path, attr->size);
+            if(f->op.truncate && f->op.getattr) {
+                res = f->op.truncate(&cred, path, attr->size);
+                if(!res) {
+                    struct stat buf;
+                    res = f->op.getattr(&cred, path, &buf);
+                    outarg.newsize = buf.st_size;
+                }
+            }
         }
         if(!res && (valid & FATTR_UTIME)) {
             struct utimbuf buf;
@@ -329,11 +384,11 @@
             buf.modtime = attr->mtime;
             res = -ENOSYS;
             if(f->op.utime)
-                res = f->op.utime(path, &buf);
+                res = f->op.utime(&cred, path, &buf);
         }
         g_free(path);
     }
-    send_reply(f, in, res, NULL, 0);
+    send_reply(f, in, res, &outarg, sizeof(outarg));
 }
 
 static void do_readlink(struct fuse *f, struct fuse_in_header *in)
@@ -341,13 +396,15 @@
     int res;
     char link[PATH_MAX + 1];
     char *path;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path(in->ino);
+    path = get_path(f, in->ino);
     if(path != NULL) {
         res = -ENOSYS;
         if(f->op.readlink)
-            res = f->op.readlink(path, link, sizeof(link));
+            res = f->op.readlink(&cred, path, link, sizeof(link));
         g_free(path);
     }
     send_reply(f, in, res, link, !res ? strlen(link) : 0);
@@ -357,19 +414,20 @@
 {
     int res;
     struct fuse_getdir_out arg;
-    struct fuse_dh dh;
+    struct fuse_dirhandle dh;
     char *path;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     dh.fuse = f;
     dh.fp = tmpfile();
     dh.dir = in->ino;
-
     res = -ENOENT;
-    path = get_path(in->ino);
+    path = get_path(f, in->ino);
     if(path != NULL) {
         res = -ENOSYS;
         if(f->op.getdir)
-            res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
+            res = f->op.getdir(&cred, path, &dh, (fuse_dirfil_t) fill_dir);
         g_free(path);
     }
     fflush(dh.fp);
@@ -385,21 +443,24 @@
     char *path;
     struct fuse_mknod_out outarg;
     struct stat buf;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path_name(in->ino, inarg->name);
+    path = get_path_name(f, in->ino, inarg->name);
     if(path != NULL) {
         res = -ENOSYS;
         if(f->op.mknod && f->op.getattr) {
-            res = f->op.mknod(path, inarg->mode, inarg->rdev);
+            res = f->op.mknod(&cred, path, inarg->mode, inarg->rdev);
             if(res == 0)
-                res = f->op.getattr(path, &buf);
+                res = f->op.getattr(&cred, path, &buf);
         }
         g_free(path);
     }
     if(res == 0) {
         convert_stat(&buf, &outarg.attr);
-        outarg.ino = find_node(f, in->ino, inarg->name, outarg.attr.mode);
+        outarg.ino = find_node(f, in->ino, inarg->name, &outarg.attr,
+                               in->unique);
     }
 
     send_reply(f, in, res, &outarg, sizeof(outarg));
@@ -410,13 +471,15 @@
 {
     int res;
     char *path;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path_name(in->ino, inarg->name);
+    path = get_path_name(f, in->ino, inarg->name);
     if(path != NULL) {
         res = -ENOSYS;
         if(f->op.mkdir)
-            res = f->op.mkdir(path, inarg->mode);
+            res = f->op.mkdir(&cred, path, inarg->mode);
         g_free(path);
     }
     send_reply(f, in, res, NULL, 0);
@@ -426,18 +489,20 @@
 {
     int res;
     char *path;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path_name(in->ino, name);
+    path = get_path_name(f, in->ino, name);
     if(path != NULL) {
         res = -ENOSYS;
         if(in->opcode == FUSE_UNLINK) {
             if(f->op.unlink)
-                res = f->op.unlink(path);
+                res = f->op.unlink(&cred, path);
         }
         else {
             if(f->op.rmdir)
-                res = f->op.rmdir(path);
+                res = f->op.rmdir(&cred, path);
         }
         g_free(path);
     }
@@ -451,13 +516,15 @@
 {
     int res;
     char *path;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path_name(in->ino, name);
+    path = get_path_name(f, in->ino, name);
     if(path != NULL) {
         res = -ENOSYS;
         if(f->op.symlink)
-            res = f->op.symlink(link, path);
+            res = f->op.symlink(&cred, link, path);
         g_free(path);
     }
     send_reply(f, in, res, NULL, 0);
@@ -473,15 +540,17 @@
     char *newname = inarg->names + strlen(oldname) + 1;
     char *oldpath;
     char *newpath;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    oldpath = get_path_name(olddir, oldname);
+    oldpath = get_path_name(f, olddir, oldname);
     if(oldpath != NULL) {
-        newpath = get_path_name(newdir, newname);
+        newpath = get_path_name(f, newdir, newname);
         if(newpath != NULL) {
             res = -ENOSYS;
             if(f->op.rename)
-                res = f->op.rename(oldpath, newpath);
+                res = f->op.rename(&cred, oldpath, newpath);
             if(res == 0)
                 rename_node(f, olddir, oldname, newdir, newname);
             g_free(newpath);
@@ -497,15 +566,17 @@
     int res;
     char *oldpath;
     char *newpath;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    oldpath = get_path(in->ino);
+    oldpath = get_path(f, in->ino);
     if(oldpath != NULL) {
-        newpath =  get_path_name(arg->newdir, arg->name);
+        newpath =  get_path_name(f, arg->newdir, arg->name);
         if(newpath != NULL) {
             res = -ENOSYS;
             if(f->op.link)
-                res = f->op.link(oldpath, newpath);
+                res = f->op.link(&cred, oldpath, newpath);
             g_free(newpath);
         }
         g_free(oldpath);
@@ -518,13 +589,15 @@
 {
     int res;
     char *path;
+    struct fuse_cred cred;
 
+    fill_cred(in, &cred);
     res = -ENOENT;
-    path = get_path(in->ino);
+    path = get_path(f, in->ino);
     if(path != NULL) {
         res = -ENOSYS;
         if(f->op.open)
-            res = f->op.open(path, arg->flags);
+            res = f->op.open(&cred, path, arg->flags);
         g_free(path);
     }
     send_reply(f, in, res, NULL, 0);
@@ -537,12 +610,14 @@
     char *path;
     char *buf = g_malloc(arg->size);
     size_t size;
+    struct fuse_cred cred;
 
-    path = get_path(in->ino);
+    fill_cred(in, &cred);
+    path = get_path(f, in->ino);
     if(path != NULL) {
         res = -ENOSYS;
-        if(f->op.pread)
-            res = f->op.pread(path, buf, arg->size, arg->offset);
+        if(f->op.read)
+            res = f->op.read(&cred, path, buf, arg->size, arg->offset);
         g_free(path);
     }
     
@@ -556,110 +631,183 @@
     g_free(buf);
 }
 
+static void do_write(struct fuse *f, struct fuse_in_header *in,
+                     struct fuse_write_in *arg)
+{
+    int res;
+    char *path;
+    struct fuse_cred cred;
+
+    fill_cred(in, &cred);
+    path = get_path(f, in->ino);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.write)
+            res = f->op.write(&cred, path, arg->buf, arg->size, arg->offset);
+        g_free(path);
+    }
+    
+    if(res > 0) {
+        if((size_t) res != arg->size) {
+            fprintf(stderr, "short write: %u (should be %u)\n", res,
+                    arg->size);
+            res = -EIO;
+        }
+        else 
+            res = 0;
+    }
+
+    send_reply(f, in, res, NULL, 0);
+}
+
+struct cmd {
+    struct fuse *f;
+    char *buf;
+    size_t buflen;
+};
+
+static void *do_command(void *data)
+{
+    struct cmd *cmd = (struct cmd *) data;
+    struct fuse_in_header *in = (struct fuse_in_header *) cmd->buf;
+    void *inarg = cmd->buf + sizeof(struct fuse_in_header);
+    size_t argsize;
+    struct fuse *f = cmd->f;
+
+    printf("unique: %i, opcode: %i, ino: %li, insize: %i\n", in->unique,
+           in->opcode, in->ino, cmd->buflen);
+    fflush(stdout);
+    
+    argsize = cmd->buflen - sizeof(struct fuse_in_header);
+        
+    switch(in->opcode) {
+    case FUSE_LOOKUP:
+        do_lookup(f, in, (char *) inarg);
+        break;
+
+    case FUSE_FORGET:
+        do_forget(f, in, (struct fuse_forget_in *) inarg);
+        break;
+
+    case FUSE_GETATTR:
+        do_getattr(f, in);
+        break;
+
+    case FUSE_SETATTR:
+        do_setattr(f, in, (struct fuse_setattr_in *) inarg);
+        break;
+
+    case FUSE_READLINK:
+        do_readlink(f, in);
+        break;
+
+    case FUSE_GETDIR:
+        do_getdir(f, in);
+        break;
+
+    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;
+
+    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;
+
+    case FUSE_OPEN:
+        do_open(f, in, (struct fuse_open_in *) inarg);
+        break;
+
+    case FUSE_READ:
+        do_read(f, in, (struct fuse_read_in *) inarg);
+        break;
+
+    case FUSE_WRITE:
+        do_write(f, in, (struct fuse_write_in *) inarg);
+        break;
+
+    default:
+        fprintf(stderr, "Operation %i not implemented\n", in->opcode);
+        /* No need to send reply to async requests */
+        if(in->unique != 0)
+            send_reply(f, in, -ENOSYS, NULL, 0);
+    }
+
+    g_free(cmd->buf);
+    g_free(cmd);
+
+    return NULL;
+}
+
 void fuse_loop(struct fuse *f)
 {
     int res;
     char inbuf[FUSE_MAX_IN];
-    struct fuse_in_header *in = (struct fuse_in_header *) inbuf;
-    void *inarg = inbuf + sizeof(struct fuse_in_header);
-    size_t insize;
-    size_t argsize;
+    pthread_attr_t attr;
+    pthread_t thrid;
+
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     
     while(1) {
+        struct cmd *cmd;
+
         res = read(f->fd, inbuf, sizeof(inbuf));
         if(res == -1) {
             perror("reading fuse device");
             continue;
         }
-        insize = res;
-        
-        if(insize < sizeof(struct fuse_in_header)) {
+        if((size_t) res < sizeof(struct fuse_in_header)) {
             fprintf(stderr, "short read on fuse device\n");
             continue;
         }
-        printf("unique: %i, opcode: %i, ino: %li, insize: %i (%i)\n",
-               in->unique, in->opcode, in->ino, insize, 
-               g_hash_table_size(f->nametab));
 
-        argsize = insize - sizeof(struct fuse_in_header);
-
-        switch(in->opcode) {
-        case FUSE_LOOKUP:
-            do_lookup(f, in, (char *) inarg);
-            break;
-
-        case FUSE_FORGET:
-            do_forget(f, (unsigned long *) inarg,
-                      argsize / sizeof(unsigned long));
-            break;
-
-        case FUSE_GETATTR:
-            do_getattr(f, in);
-            break;
-
-        case FUSE_SETATTR:
-            do_setattr(f, in, (struct fuse_setattr_in *) inarg);
-            break;
-
-        case FUSE_READLINK:
-            do_readlink(f, in);
-            break;
-
-        case FUSE_GETDIR:
-            do_getdir(f, in);
-            break;
-
-        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;
-
-        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;
-
-        case FUSE_OPEN:
-            do_open(f, in, (struct fuse_open_in *) inarg);
-            break;
-
-        case FUSE_READ:
-            do_read(f, in, (struct fuse_read_in *) inarg);
-            break;
-
-        default:
-            fprintf(stderr, "Operation %i not implemented\n", in->opcode);
-            /* No need to send reply to async requests */
-            if(in->unique != 0)
-                send_reply(f, in, -ENOSYS, NULL, 0);
+        cmd = g_new0(struct cmd, 1);
+        cmd->f = f;
+        cmd->buflen = res;
+        cmd->buf = g_malloc(cmd->buflen);
+        memcpy(cmd->buf, inbuf, cmd->buflen);
+        
+        if(f->flags & FUSE_MULTITHREAD) {
+            res = pthread_create(&thrid, &attr, do_command, cmd);
+            if(res != 0) {
+                fprintf(stderr, "Error creating thread: %s\n", 
+                        strerror(errno));
+                exit(1);
+            }
         }
+        else
+            do_command(cmd);
     }
 }
 
-struct fuse *fuse_new()
+struct fuse *fuse_new(int flags)
 {
     struct fuse *f = g_new0(struct fuse, 1);
 
+    f->flags = flags;
     f->fd = -1;
     f->dir = NULL;
     f->nametab = g_hash_table_new((GHashFunc) name_hash,
                                   (GCompareFunc) name_compare);
+    pthread_mutex_init(&f->lock, NULL);
 
     return f;
 }
@@ -671,8 +819,9 @@
 
 void fuse_destroy(struct fuse *f)
 {
-    fuse_unmount(f);
+    close(f->fd);
     g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL);
     g_hash_table_destroy(f->nametab);
+    pthread_mutex_destroy(&f->lock);
     g_free(f);
 }
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 2d030ae..c28e2e8 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -9,6 +9,7 @@
 #include "fuse.h"
 #include <glib.h>
 #include <stdio.h>
+#include <pthread.h>
 
 #define FUSE_DEV "/proc/fs/fuse/dev"
 
@@ -18,16 +19,20 @@
     char *name;
     fino_t parent;
     int mode;
+    int rdev;
+    int version;
 };
 
 struct fuse {
+    int flags;
     char *dir;
     int fd;
     struct fuse_operations op;
     GHashTable *nametab;
+    pthread_mutex_t lock;
 };
 
-struct fuse_dh {
+struct fuse_dirhandle {
     struct fuse *fuse;
     fino_t dir;
     FILE *fp;
diff --git a/usermux.c b/usermux.c
new file mode 100644
index 0000000..4caacb9
--- /dev/null
+++ b/usermux.c
@@ -0,0 +1,252 @@
+#include <fuse.h>
+
+#include <pthread.h>
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <wait.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define MOUNTDIR "/mnt/avfs"
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+static struct fuse *um_fuse;
+static const char *um_dir;
+
+#define MAX_USERS 100
+static uid_t users[MAX_USERS];
+static size_t numusers = 0;
+
+void avfs_main(struct fuse *fuse);
+
+static void reset_signal_handlers()
+{
+    struct sigaction sa;
+	
+    sa.sa_handler = SIG_DFL;
+    sigemptyset(&(sa.sa_mask));
+    sa.sa_flags = 0;
+	
+    sigaction(SIGCHLD, &sa, NULL);
+}
+
+
+static void start_avfs(uid_t uid)
+{
+    int res;
+    char *userdir;
+    struct fuse *user_fuse;
+    struct passwd pw_buf;
+    struct passwd *pw;
+    char buf[1024];
+
+    res = getpwuid_r(uid, &pw_buf, buf, sizeof(buf), &pw);
+    if(pw == NULL)
+        return;
+
+    user_fuse = fuse_new(0);
+    
+    userdir = g_strdup_printf("%s/%010u", MOUNTDIR, uid);
+    mkdir(userdir, 0755);
+    chown(userdir, pw->pw_uid, pw->pw_gid);
+    res = fuse_mount(user_fuse, userdir);
+    g_free(userdir);
+
+    if(res == -1)
+        return;
+
+    res = fork();
+    if(res == 0) {
+        reset_signal_handlers();
+
+        initgroups(pw->pw_name, pw->pw_gid);
+        setgid(pw->pw_gid);
+        setuid(pw->pw_uid);
+        
+        avfs_main(user_fuse);
+        exit(0);
+    }
+
+    fuse_destroy(user_fuse);
+}
+
+
+static int find_user(const char *userstr, uid_t *uid)
+{
+    size_t i;
+    char *end;
+    
+    *uid = strtol(userstr, &end, 10);
+    if(*end != '\0')
+        return 0;
+
+    pthread_mutex_lock(&lock);
+    for(i = 0; i < numusers; i++) {
+        if(users[i] == *uid) {
+            pthread_mutex_unlock(&lock);
+            return 1;
+        }
+    }
+    if(numusers == MAX_USERS) {
+        memmove(users, users + sizeof(users[0]),
+                (MAX_USERS - 1) * sizeof(users[0]));
+        numusers --;
+    }
+
+    users[numusers++] = *uid;
+    pthread_mutex_unlock(&lock);
+
+    start_avfs(*uid);
+
+    return 1;
+}
+
+static void root_attr(struct stat *stbuf)
+{
+    stbuf->st_mode = S_IFDIR | 0555;
+    stbuf->st_nlink = 2 + numusers;
+    stbuf->st_size = MAX_USERS;
+    stbuf->st_blksize = 1024;
+}
+
+static int um_getattr(struct fuse_cred *cred, const char *path,
+                      struct stat *stbuf)
+{
+    uid_t uid;
+    memset(stbuf, 0, sizeof(*stbuf));
+
+    if(strcmp(path, "/") == 0) {
+        root_attr(stbuf);
+        return 0;
+    }
+
+    if(!find_user(path+1, &uid))
+        return -ENOENT;
+
+    stbuf->st_mode = S_IFLNK | 0777;
+    stbuf->st_nlink = 1;
+    stbuf->st_size = strlen(MOUNTDIR) + 1 + 10;
+    stbuf->st_blksize = 1024;
+    stbuf->st_uid = uid;
+
+    return 0;
+}
+
+static int um_readlink(struct fuse_cred *cred, const char *path, char *buf,
+                       size_t size)
+{
+    uid_t uid;
+
+    if(!find_user(path+1, &uid))
+        return -ENOENT;
+
+    snprintf(buf, size, "%s/%010u", MOUNTDIR, uid);
+    return 0;
+}
+
+static int um_getdir(struct fuse_cred *cred, const char *path, fuse_dirh_t h,
+                     fuse_dirfil_t filler)
+{
+    size_t i;
+
+    if(strcmp(path, "/") != 0)
+        return 0;
+    
+    filler(h, ".", 0);
+    filler(h, "..", 0);
+
+    pthread_mutex_lock(&lock);
+    for(i = 0; i < numusers; i++) {
+        char buf[32];
+
+        sprintf(buf, "%u", users[i]);
+        filler(h, buf, 0);
+    }
+    pthread_mutex_unlock(&lock);
+
+    return 0;
+}
+
+
+static void exit_handler()
+{
+    exit(0);
+}
+
+static void child_handler()
+{
+    int status;
+    wait(&status);
+}
+
+static void set_signal_handlers()
+{
+    struct sigaction sa;
+
+    sa.sa_handler = exit_handler;
+    sigemptyset(&(sa.sa_mask));
+    sa.sa_flags = 0;
+
+    if (sigaction(SIGHUP, &sa, NULL) == -1 || 
+	sigaction(SIGINT, &sa, NULL) == -1 || 
+	sigaction(SIGTERM, &sa, NULL) == -1) {
+	
+	perror("Cannot set exit signal handlers");
+        exit(1);
+    }
+
+    sa.sa_handler = SIG_IGN;
+    
+    if(sigaction(SIGPIPE, &sa, NULL) == -1) {
+	perror("Cannot set ignored signals");
+        exit(1);
+    }
+
+    sa.sa_handler = child_handler;
+    if(sigaction(SIGCHLD, &sa, NULL) == -1) {
+	perror("Cannot set child signal handler");
+        exit(1);
+    }
+}
+
+static void cleanup()
+{
+    fuse_unmount(um_fuse);
+    fuse_destroy(um_fuse);
+}
+
+static struct fuse_operations um_oper = {
+    getattr:	um_getattr,
+    getdir:     um_getdir,
+    readlink:	um_readlink,
+};
+
+int main(int argc, char *argv[])
+{
+    int res;
+    if(argc != 2) {
+        fprintf(stderr, "usage: %s mount_dir\n", argv[0]);
+        exit(1);
+    }
+    
+    um_dir = argv[1];
+
+    set_signal_handlers();
+    atexit(cleanup);
+
+    um_fuse = fuse_new(0);
+    res = fuse_mount(um_fuse, um_dir);
+    if(res == -1)
+        exit(1);
+        
+    fuse_set_operations(um_fuse, &um_oper);
+    fuse_loop(um_fuse);
+
+    return 0;
+}