x
diff --git a/fusepro.c b/fusepro.c
index 4f593e7..ff998bf 100644
--- a/fusepro.c
+++ b/fusepro.c
@@ -1,11 +1,17 @@
-#include <fuse.h>
+#ifdef linux
+/* For pread()/pwrite() */
+#define _XOPEN_SOURCE 500
+#endif
 
+#include <fuse.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <dirent.h>
 #include <errno.h>
 #include <signal.h>
+#include <utime.h>
+#include <fcntl.h>
 
 static struct fuse *pro_fuse;
 
@@ -53,7 +59,7 @@
     return res;
 }
 
-static int pro_mknod(const char *path, int mode, int rdev)
+static int pro_mknod(const char *path, mode_t mode, dev_t rdev)
 {
     int res;
 
@@ -64,18 +70,7 @@
     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)
+static int pro_mkdir(const char *path, mode_t mode)
 {
     int res;
 
@@ -108,6 +103,17 @@
     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_rename(const char *from, const char *to)
 {
     int res;
@@ -130,6 +136,80 @@
     return 0;
 }
 
+static int pro_chmod(const char *path, mode_t mode)
+{
+    int res;
+
+    res = chmod(path, mode);
+    if(res == -1)
+        return -errno;
+    
+    return 0;
+}
+
+static int pro_chown(const char *path, uid_t uid, gid_t gid)
+{
+    int res;
+
+    res = chown(path, uid, gid);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int pro_truncate(const char *path, off_t size)
+{
+    int res;
+    
+    res = truncate(path, size);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int pro_utime(const char *path, struct utimbuf *buf)
+{
+    int res;
+    
+    res = utime(path, buf);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+
+static int pro_open(const char *path, int flags)
+{
+    int res;
+
+    res = open(path, flags);
+    if(res == -1) 
+        return -errno;
+
+    close(res);
+    return 0;
+}
+
+static int pro_pread(const char *path, char *buf, size_t size, off_t offset)
+{
+    int fd;
+    int res;
+
+    fd = open(path, 0);
+    if(fd == -1)
+        return -errno;
+
+    res = pread(fd, buf, size, offset);
+    if(res == -1)
+        res = -errno;
+    
+    close(fd);
+    return res;
+}
+
 static void exit_handler()
 {
     exit(0);
@@ -176,10 +256,17 @@
     rmdir:	pro_rmdir,
     rename:     pro_rename,
     link:	pro_link,
+    chmod:	pro_chmod,
+    chown:	pro_chown,
+    truncate:	pro_truncate,
+    utime:	pro_utime,
+    open:	pro_open,
+    pread:	pro_pread,
 };
 
 int main(int argc, char *argv[])
 {
+    int res;
     if(argc != 2) {
         fprintf(stderr, "usage: %s mount_dir\n", argv[0]);
         exit(1);
@@ -189,7 +276,10 @@
     atexit(cleanup);
 
     pro_fuse = fuse_new();
-    fuse_mount(pro_fuse, argv[1]);
+    res = fuse_mount(pro_fuse, argv[1]);
+    if(res == -1)
+        exit(1);
+        
     fuse_set_operations(pro_fuse, &pro_oper);
     fuse_loop(pro_fuse);
 
diff --git a/include/fuse.h b/include/fuse.h
index e18f606..bdc96a6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -10,24 +10,30 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <utime.h>
 
 struct fuse;
 struct fuse_dh;
 
 typedef int (*dirfiller_t) (struct fuse_dh *, const char *, int type);
 
-
 struct fuse_operations {
-    int (*getattr) (const char *path, struct stat *stbuf);
+    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, int mode, int rdev);
-    int (*mkdir) (const char *path, int mode);
-    int (*unlink) (const char *path);
-    int (*rmdir) (const char *path);
-    int (*rename) (const char *from, const char *to);
-    int (*symlink) (const char *from, const char *to);
-    int (*link) (const char *from, const char *to);
+    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 *fuse_new();
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 64a4af3..5fe008c 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -19,11 +19,11 @@
 #define FUSE_ROOT_INO 1
 
 struct fuse_attr {
-	unsigned short      mode;
-	unsigned short      nlink;
-	unsigned short      uid;
-	unsigned short      gid;
-	unsigned short      rdev;
+	unsigned int        mode;
+	unsigned int        nlink;
+	unsigned int        uid;
+	unsigned int        gid;
+	unsigned int        rdev;
 	unsigned long long  size;
 	unsigned long       blksize;
 	unsigned long       blocks;
@@ -32,19 +32,28 @@
 	unsigned long       ctime;
 };
 
+#define FATTR_MODE	(1 << 0)
+#define FATTR_UID	(1 << 1)
+#define FATTR_GID	(1 << 2)
+#define FATTR_SIZE	(1 << 3)
+#define FATTR_UTIME	(1 << 4)
+
 enum fuse_opcode {
 	FUSE_LOOKUP     = 1,
 	FUSE_FORGET,
 	FUSE_GETATTR,
+	FUSE_SETATTR,
 	FUSE_READLINK,
+	FUSE_SYMLINK,
 	FUSE_GETDIR,
 	FUSE_MKNOD,
 	FUSE_MKDIR,
-	FUSE_SYMLINK,
 	FUSE_UNLINK,
 	FUSE_RMDIR,
 	FUSE_RENAME,
 	FUSE_LINK,
+	FUSE_OPEN,
+	FUSE_READ,
 };
 
 /* Conservative buffer size for the client */
@@ -90,6 +99,20 @@
 	char name[1];
 };
 
+struct fuse_setattr_in {
+	struct fuse_attr attr;
+	unsigned int valid;
+};
+
+struct fuse_open_in {
+	unsigned int flags;
+};
+
+struct fuse_read_in {
+	unsigned long long offset;
+	unsigned int size;
+};
+
 struct fuse_in_header {
 	int unique;
 	enum fuse_opcode opcode;
diff --git a/kernel/Makefile b/kernel/Makefile
index f7769d5..4646162 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -5,7 +5,7 @@
 
 all: fuse.o
 
-fuse_objs = dev.o inode.o dir.o util.o
+fuse_objs = dev.o inode.o dir.o file.o util.o
 
 fuse.o: $(fuse_objs)
 	ld -r -o fuse.o $(fuse_objs)
diff --git a/kernel/dev.c b/kernel/dev.c
index d32589d..6a8d373 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -87,7 +87,7 @@
 {
 	struct fuse_req *req;
 	
-	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	req = kmalloc(sizeof(*req), GFP_NOFS);
 	if(!req)
 		return NULL;
 
@@ -98,7 +98,7 @@
 	req->out = NULL;
 
 	req->insize = IHSIZE + inp->argsize;
-	req->in = kmalloc(req->insize, GFP_KERNEL);
+	req->in = kmalloc(req->insize, GFP_NOFS);
 	if(!req->in) {
 		request_free(req);
 		return NULL;
@@ -188,7 +188,7 @@
 			     loff_t *off)
 {
 	int ret;
-	struct fuse_conn *fc = file->private_data;
+	struct fuse_conn *fc = DEV_FC(file);
 	struct fuse_req *req;
 	char *tmpbuf;
 	unsigned int size;
@@ -249,7 +249,7 @@
 			      size_t nbytes, loff_t *off)
 {
 	ssize_t ret;
-	struct fuse_conn *fc = file->private_data;
+	struct fuse_conn *fc = DEV_FC(file);
 	struct fuse_req *req;
 	char *tmpbuf;
 	struct fuse_out_header *oh;
@@ -302,7 +302,7 @@
 
 static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
 {
-	struct fuse_conn *fc = file->private_data;
+	struct fuse_conn *fc = DEV_FC(file);
 	unsigned int mask = POLLOUT | POLLWRNORM;
 
 	if(!fc->sb)
@@ -370,7 +370,7 @@
 
 static int fuse_dev_release(struct inode *inode, struct file *file)
 {
-	struct fuse_conn *fc = file->private_data;
+	struct fuse_conn *fc = DEV_FC(file);
 
 	spin_lock(&fuse_lock);
 	fc->file = NULL;
diff --git a/kernel/dir.c b/kernel/dir.c
index b23def7..1dac0c8 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -10,9 +10,8 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/file.h>
 #include <linux/slab.h>
+#include <linux/file.h>
 
 static struct inode_operations fuse_dir_inode_operations;
 static struct inode_operations fuse_file_inode_operations;
@@ -20,13 +19,12 @@
 static struct inode_operations fuse_special_inode_operations;
 
 static struct file_operations fuse_dir_operations;
-static struct file_operations fuse_file_operations;
 
 static struct dentry_operations fuse_dentry_opertations;
 
 static void change_attributes(struct inode *inode, struct fuse_attr *attr)
 {
-	inode->i_mode    = attr->mode;
+	inode->i_mode    = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
 	inode->i_nlink   = attr->nlink;
 	inode->i_uid     = attr->uid;
 	inode->i_gid     = attr->gid;
@@ -38,29 +36,46 @@
 	inode->i_ctime   = attr->ctime;
 }
 
-void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
+static void fuse_init_inode(struct inode *inode, int mode, int rdev)
 {
-	change_attributes(inode, attr);
-	
+	inode->i_mode = mode & S_IFMT;
 	if(S_ISREG(inode->i_mode)) {
 		inode->i_op = &fuse_file_inode_operations;
-		inode->i_fop = &fuse_file_operations;
+		fuse_init_file_inode(inode);
 	}
 	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))
+	else if(S_ISLNK(inode->i_mode)) {
 		inode->i_op = &fuse_symlink_inode_operations;
+	}
 	else {
 		inode->i_op = &fuse_special_inode_operations;
-		init_special_inode(inode, inode->i_mode, attr->rdev);
+		init_special_inode(inode, inode->i_mode, rdev);
 	}
+	inode->u.generic_ip = inode;
+}
+
+struct inode *fuse_iget(struct super_block *sb, ino_t ino,
+			struct fuse_attr *attr)
+{
+	struct inode *inode;
+
+	inode = iget(sb, ino);
+	if(inode) {
+		if(!inode->u.generic_ip)
+			fuse_init_inode(inode, attr->mode, attr->rdev);
+		
+		change_attributes(inode, attr);
+	}
+
+	return inode;
 }
 
 static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
 {
-	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+	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;
@@ -76,11 +91,9 @@
 	
 	inode = NULL;
 	if(!out.h.error) {
-		inode = iget(dir->i_sb, arg.ino);
+		inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr);
 		if(!inode) 
 			return ERR_PTR(-ENOMEM);
-		
-		fuse_init_inode(inode, &arg.attr);
 	}
 	else if(out.h.error != -ENOENT)
 		return ERR_PTR(out.h.error);
@@ -94,7 +107,7 @@
 static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
 		      int rdev)
 {
-	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+	struct fuse_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_mknod_in *inarg;
@@ -123,11 +136,10 @@
 	if(out.h.error) 
 		return out.h.error;
 
-	inode = iget(dir->i_sb, outarg.ino);
+	inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr);
 	if(!inode) 
 		return -ENOMEM;
-	
-	fuse_init_inode(inode, &outarg.attr);
+
 	d_instantiate(entry, inode);
 
 	return 0;
@@ -141,7 +153,7 @@
 
 static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
 {
-	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+	struct fuse_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_mkdir_in *inarg;
@@ -168,7 +180,7 @@
 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_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	char *inarg;
@@ -195,7 +207,7 @@
 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_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 
@@ -220,7 +232,7 @@
 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_conn *fc = INO_FC(olddir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_rename_in *inarg;
@@ -252,7 +264,7 @@
 		     struct dentry *newent)
 {
 	struct inode *inode = entry->d_inode;
-	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_link_in *inarg;
@@ -286,7 +298,7 @@
 static int fuse_revalidate(struct dentry *dentry)
 {
 	struct inode *inode = dentry->d_inode;
-	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+	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;
@@ -357,7 +369,7 @@
 static int read_link(struct dentry *dentry, char **bufp)
 {
 	struct inode *inode = dentry->d_inode;
-	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	unsigned long page;
@@ -417,7 +429,7 @@
 
 static int fuse_dir_open(struct inode *inode, struct file *file)
 {
-	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+	struct fuse_conn *fc = INO_FC(inode);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_getdir_out outarg;
@@ -453,15 +465,56 @@
 static int fuse_dir_release(struct inode *inode, struct file *file)
 {
 	struct file *cfile = file->private_data;
-
-	if(!cfile)
-		BUG();
-	
 	fput(cfile);
 
 	return 0;
 }
 
+static unsigned int iattr_to_fattr(struct iattr *iattr,
+				   struct fuse_attr *fattr)
+{
+	unsigned int ivalid = iattr->ia_valid;
+	unsigned int fvalid = 0;
+	
+	memset(fattr, 0, sizeof(*fattr));
+	
+	if(ivalid & ATTR_MODE)
+		fvalid |= FATTR_MODE,   fattr->mode = iattr->ia_mode;
+	if(ivalid & ATTR_UID)
+		fvalid |= FATTR_UID,    fattr->uid = iattr->ia_uid;
+	if(ivalid & ATTR_GID)
+		fvalid |= FATTR_GID,    fattr->gid = iattr->ia_gid;
+	if(ivalid & ATTR_SIZE)
+		fvalid |= FATTR_SIZE,   fattr->size = iattr->ia_size;
+	/* You can only _set_ these together (they may change by themselves) */
+	if((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) {
+		fvalid |= FATTR_UTIME;
+		fattr->atime = iattr->ia_atime;
+		fattr->mtime = iattr->ia_mtime;
+	}
+
+	return fvalid;
+}
+
+static int fuse_setattr(struct dentry *entry, struct iattr *attr)
+{
+	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_setattr_in arg;
+
+	arg.valid = iattr_to_fattr(attr, &arg.attr);
+	
+	in.h.opcode = FUSE_SETATTR;
+	in.h.ino = inode->i_ino;
+	in.argsize = sizeof(arg);
+	in.arg = &arg;
+	request_send(fc, &in, &out);
+
+	return out.h.error;
+}
+
 static int fuse_dentry_revalidate(struct dentry *entry, int flags)
 {
 	if(!entry->d_inode || !(flags & LOOKUP_CONTINUE))
@@ -481,9 +534,7 @@
 	rmdir:          fuse_rmdir,
 	rename:         fuse_rename,
 	link:           fuse_link,
-#if 0
 	setattr:	fuse_setattr,
-#endif
 	permission:	fuse_permission,
 	revalidate:	fuse_revalidate,
 };
@@ -496,20 +547,20 @@
 };
 
 static struct inode_operations fuse_file_inode_operations = {
+	setattr:	fuse_setattr,
 	permission:	fuse_permission,
 	revalidate:	fuse_revalidate,
 };
 
 static struct inode_operations fuse_special_inode_operations = {
+	setattr:	fuse_setattr,
 	permission:	fuse_permission,
 	revalidate:	fuse_revalidate,
 };
 
-static struct file_operations fuse_file_operations = {
-};
-
 static struct inode_operations fuse_symlink_inode_operations =
 {
+	setattr:	fuse_setattr,
 	readlink:	fuse_readlink,
 	follow_link:	fuse_follow_link,
 	revalidate:	fuse_revalidate,
diff --git a/kernel/file.c b/kernel/file.c
new file mode 100644
index 0000000..df3a863
--- /dev/null
+++ b/kernel/file.c
@@ -0,0 +1,88 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001  Miklos Szeredi (mszeredi@inf.bme.hu)
+
+    This program can be distributed under the terms of the GNU GPL.
+    See the file COPYING.
+*/
+#include "fuse_i.h"
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pagemap.h>
+
+
+static int fuse_open(struct inode *inode, struct file *file)
+{
+	struct fuse_conn *fc = INO_FC(inode);
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_open_in arg;
+
+	arg.flags = file->f_flags & ~O_EXCL;
+	in.h.opcode = FUSE_OPEN;
+	in.h.ino = inode->i_ino;
+	in.argsize = sizeof(arg);
+	in.arg = &arg;
+	request_send(fc, &in, &out);
+
+	return out.h.error;
+}
+
+static int fuse_readpage(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	struct fuse_conn *fc = INO_FC(inode);
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_read_in arg;
+	char *buffer;
+
+	buffer = kmap(page);
+
+	arg.offset = page->index << PAGE_CACHE_SHIFT;
+	arg.size = PAGE_CACHE_SIZE;
+
+	in.h.opcode = FUSE_READ;
+	in.h.ino = inode->i_ino;
+	in.argsize = sizeof(arg);
+	in.arg = &arg;
+	out.argsize = PAGE_CACHE_SIZE;
+	out.argvar = 1;
+	out.arg = buffer;
+
+	request_send(fc, &in, &out);
+	if(!out.h.error) {
+		if(out.argsize < PAGE_CACHE_SIZE) 
+			memset(buffer + out.argsize, 0,
+			       PAGE_CACHE_SIZE - out.argsize);
+		SetPageUptodate(page);
+	}
+
+	kunmap(page);
+	UnlockPage(page);
+
+	return out.h.error;
+}
+
+static struct file_operations fuse_file_operations = {
+	open:		fuse_open,
+	read:		generic_file_read,
+};
+
+static struct address_space_operations fuse_file_aops  = {
+	readpage:	fuse_readpage,
+};
+
+void fuse_init_file_inode(struct inode *inode)
+{
+	inode->i_fop = &fuse_file_operations;
+	inode->i_data.a_ops = &fuse_file_aops;
+}
+
+/* 
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index de0a9e6..aaa395d 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -82,6 +82,9 @@
 };
 
 
+#define INO_FC(inode) ((struct fuse_conn *) (inode)->i_sb->u.generic_sbp)
+#define DEV_FC(file) ((struct fuse_conn *) (file)->private_data)
+
 struct fuse_in {
 	struct fuse_in_header h;
 	unsigned int argsize;
@@ -111,9 +114,16 @@
 
 
 /**
- * Initialize inode
+ * Get a filled in inode
  */
-void fuse_init_inode(struct inode *inode, struct fuse_attr *attr);
+struct inode *fuse_iget(struct super_block *sb, ino_t ino,
+			struct fuse_attr *attr);
+
+
+/**
+ * Initialise operations on regular file
+ */
+void fuse_init_file_inode(struct inode *inode);
 
 /**
  * Check if the connection can be released, and if yes, then free the
diff --git a/kernel/inode.c b/kernel/inode.c
index 702ef57..ed4f760 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -39,7 +39,7 @@
 	unsigned long *tmp;
 	
 	spin_unlock(&fuse_lock);
-	tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_KERNEL);
+	tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_NOFS);
 	spin_lock(&fuse_lock);
 
 	if(!fc->file || fc->cleared != NULL)
@@ -72,7 +72,7 @@
 
 static void fuse_clear_inode(struct inode *inode)
 {
-	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+	struct fuse_conn *fc = INO_FC(inode);
 	unsigned long *forget;
 
 	spin_lock(&fuse_lock);
@@ -139,17 +139,11 @@
 
 static struct inode *get_root_inode(struct super_block *sb)
 {
-	struct inode *root;
+	struct fuse_attr attr;
+	memset(&attr, 0, sizeof(attr));
 
-	root = iget(sb, 1);
-	if(root) {
-		struct fuse_attr attr;
-		memset(&attr, 0, sizeof(attr));
-		attr.mode = S_IFDIR;
-		fuse_init_inode(root, &attr);
-	}
-
-	return root;
+	attr.mode = S_IFDIR;
+	return fuse_iget(sb, 1, &attr);
 }
 
 static struct super_block *fuse_read_super(struct super_block *sb, 
diff --git a/lib/fuse.c b/lib/fuse.c
index f033e04..cd919ed 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -27,11 +27,12 @@
         strcmp(node1->name, node2->name) == 0;
 }
 
-static struct node *new_node(fino_t parent, const char *name)
+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;
 }
 
@@ -63,20 +64,41 @@
     return g_hash_table_lookup(f->nametab, &tmp);
 }
 
-static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
+static void unhash_node(struct fuse *f, struct node *node)
+{
+    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, int mode)
+{
+    struct node *node;
+    mode &= S_IFMT;
+
+    node = lookup_node(f, parent, name);
+    if(node != NULL) {
+        if(node->mode == mode)
+            return get_ino(node);
+
+        unhash_node(f, node);
+    }
+
+    node = new_node(parent, name, mode);
+    g_hash_table_insert(f->nametab, node, node);
+    return get_ino(node);
+}
+
+static fino_t find_node_dir(struct fuse *f, fino_t parent, char *name)
 {
     struct node *node;
 
     node = lookup_node(f, parent, name);
     if(node != NULL)
         return get_ino(node);
-
-    if(!create)
+    else
         return (fino_t) -1;
-
-    node = new_node(parent, name);
-    g_hash_table_insert(f->nametab, node, node);
-    return get_ino(node);
 }
 
 static char *get_path(fino_t ino)
@@ -91,6 +113,10 @@
         struct node *node;
         for(; ino != FUSE_ROOT_INO; ino = node->parent) {
             node = get_node(ino);
+            if(node->name == NULL) {
+                g_string_free(s, TRUE);
+                return NULL;
+            }
             g_string_prepend(s, node->name);
             g_string_prepend_c(s, '/');
         }
@@ -104,32 +130,42 @@
 
 static char *get_path_name(fino_t ino, const char *name)
 {
-    char *path = get_path(ino);
-    char *path2 = g_strconcat(path, "/", name, NULL);
+    char *path;
+    char *path2;
+    
+    path = get_path(ino);
+    if(path == NULL)
+        return NULL;
+
+    path2 = g_strconcat(path, "/", name, NULL);
     g_free(path);
     return path2;
 }
 
-static void remove_node(struct fuse *f, fino_t ino)
+static void destroy_node(struct fuse *f, fino_t ino)
 {
     struct node *node = get_node(ino);
-    g_hash_table_remove(f->nametab, node);
+    unhash_node(f, node);
     free_node(node);
 }
 
+static void remove_node(struct fuse *f, fino_t dir, const char *name)
+{
+    struct node *node = lookup_node(f, dir, name);
+    assert(node != NULL);
+    unhash_node(f, node);
+}
+
 static void rename_node(struct fuse *f, fino_t olddir, const char *oldname,
                         fino_t newdir, const char *newname)
 {
     struct node *node = lookup_node(f, olddir, oldname);
     struct node *newnode = lookup_node(f, newdir, newname);
-        
     assert(node != NULL);
 
-    /* The overwritten node is left to dangle until deleted */
     if(newnode != NULL)
-        g_hash_table_remove(f->nametab, newnode);
+        unhash_node(f, newnode);
         
-    /* The renamed node is not freed, since it's pointer is the key */
     g_hash_table_remove(f->nametab, node);
     g_free(node->name);
     node->name = g_strdup(newname);
@@ -159,7 +195,7 @@
     size_t reclen;
     size_t res;
 
-    dirent.ino = find_node(dh->fuse, dh->dir, name, 0);
+    dirent.ino = find_node_dir(dh->fuse, dh->dir, name);
     dirent.namelen = strlen(name);
     strncpy(dirent.name, name, sizeof(dirent.name));
     dirent.type = type;
@@ -213,14 +249,17 @@
     struct stat buf;
     struct fuse_lookup_out arg;
 
+    res = -ENOENT;
     path = get_path_name(in->ino, name);
-    res = -ENOSYS;
-    if(f->op.getattr)
-        res = f->op.getattr(path, &buf);
-    g_free(path);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.getattr)
+            res = f->op.getattr(path, &buf);
+        g_free(path);
+    }
     if(res == 0) {
         convert_stat(&buf, &arg.attr);
-        arg.ino = find_node(f, in->ino, name, 1);
+        arg.ino = find_node(f, in->ino, name, arg.attr.mode);
     }
     send_reply(f, in, res, &arg, sizeof(arg));
 }
@@ -230,7 +269,7 @@
     size_t i;
 
     for(i = 0; i < num; i++)
-        remove_node(f, inos[i]);
+        destroy_node(f, inos[i]);
 }
 
 static void do_getattr(struct fuse *f, struct fuse_in_header *in)
@@ -240,30 +279,78 @@
     struct stat buf;
     struct fuse_getattr_out arg;
 
+    res = -ENOENT;
     path = get_path(in->ino);
-    res = -ENOSYS;
-    if(f->op.getattr)
-        res = f->op.getattr(path, &buf);
-    g_free(path);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.getattr)
+            res = f->op.getattr(path, &buf);
+        g_free(path);
+    }
     if(res == 0) 
         convert_stat(&buf, &arg.attr);
 
     send_reply(f, in, res, &arg, sizeof(arg));
 }
 
+static void do_setattr(struct fuse *f, struct fuse_in_header *in,
+                       struct fuse_setattr_in *arg)
+{
+    int res;
+    char *path;
+    int valid = arg->valid;
+    struct fuse_attr *attr = &arg->attr;
+
+    res = -ENOENT;
+    path = get_path(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);
+        }        
+        if(!res && (valid & (FATTR_UID | FATTR_GID))) {
+            uid_t uid = (valid & FATTR_UID) ? attr->uid : (uid_t) -1;
+            gid_t gid = (valid & FATTR_GID) ? attr->gid : (gid_t) -1;
+            
+            res = -ENOSYS;
+            if(f->op.chown)
+                res = f->op.chown(path, uid, gid);
+        }
+        if(!res && (valid & FATTR_SIZE)) {
+            res = -ENOSYS;
+            if(f->op.truncate)
+                res = f->op.truncate(path, attr->size);
+        }
+        if(!res && (valid & FATTR_UTIME)) {
+            struct utimbuf buf;
+            buf.actime = attr->atime;
+            buf.modtime = attr->mtime;
+            res = -ENOSYS;
+            if(f->op.utime)
+                res = f->op.utime(path, &buf);
+        }
+        g_free(path);
+    }
+    send_reply(f, in, res, NULL, 0);
+}
+
 static void do_readlink(struct fuse *f, struct fuse_in_header *in)
 {
     int res;
     char link[PATH_MAX + 1];
     char *path;
 
-    path = get_path(in->ino);    
-    res = -ENOSYS;
-    if(f->op.readlink)
-        res = f->op.readlink(path, link, sizeof(link));
-    g_free(path);
-
-    send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
+    res = -ENOENT;
+    path = get_path(in->ino);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.readlink)
+            res = f->op.readlink(path, link, sizeof(link));
+        g_free(path);
+    }
+    send_reply(f, in, res, link, !res ? strlen(link) : 0);
 }
 
 static void do_getdir(struct fuse *f, struct fuse_in_header *in)
@@ -277,12 +364,14 @@
     dh.fp = tmpfile();
     dh.dir = in->ino;
 
+    res = -ENOENT;
     path = get_path(in->ino);
-    res = -ENOSYS;
-    if(f->op.getdir)
-        res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
-    g_free(path);
-
+    if(path != NULL) {
+        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));
@@ -297,17 +386,20 @@
     struct fuse_mknod_out outarg;
     struct stat buf;
 
+    res = -ENOENT;
     path = get_path_name(in->ino, inarg->name);
-    res = -ENOSYS;
-    if(f->op.mknod && f->op.getattr) {
-        res = f->op.mknod(path, inarg->mode, inarg->rdev);
-        if(res == 0)
-            res = f->op.getattr(path, &buf);
+    if(path != NULL) {
+        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);
     }
-    g_free(path);
     if(res == 0) {
         convert_stat(&buf, &outarg.attr);
-        outarg.ino = find_node(f, in->ino, inarg->name, 1);
+        outarg.ino = find_node(f, in->ino, inarg->name, outarg.attr.mode);
     }
 
     send_reply(f, in, res, &outarg, sizeof(outarg));
@@ -319,11 +411,14 @@
     int res;
     char *path;
 
+    res = -ENOENT;
     path = get_path_name(in->ino, inarg->name);
-    res = -ENOSYS;
-    if(f->op.mkdir)
-        res = f->op.mkdir(path, inarg->mode);
-    g_free(path);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.mkdir)
+            res = f->op.mkdir(path, inarg->mode);
+        g_free(path);
+    }
     send_reply(f, in, res, NULL, 0);
 }
 
@@ -332,17 +427,22 @@
     int res;
     char *path;
 
+    res = -ENOENT;
     path = get_path_name(in->ino, name);
-    res = -ENOSYS;
-    if(in->opcode == FUSE_UNLINK) {
-        if(f->op.unlink)
-            res = f->op.unlink(path);
+    if(path != NULL) {
+        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);
     }
-    else {
-        if(f->op.rmdir)
-            res = f->op.rmdir(path);
-    }
-    g_free(path);
+    if(res == 0)
+        remove_node(f, in->ino, name);
     send_reply(f, in, res, NULL, 0);
 }
 
@@ -352,11 +452,14 @@
     int res;
     char *path;
 
+    res = -ENOENT;
     path = get_path_name(in->ino, name);
-    res = -ENOSYS;
-    if(f->op.symlink)
-        res = f->op.symlink(link, path);
-    g_free(path);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.symlink)
+            res = f->op.symlink(link, path);
+        g_free(path);
+    }
     send_reply(f, in, res, NULL, 0);
 }
 
@@ -368,31 +471,90 @@
     fino_t newdir = inarg->newdir;
     char *oldname = inarg->names;
     char *newname = inarg->names + strlen(oldname) + 1;
-    char *oldpath = get_path_name(olddir, oldname);
-    char *newpath = get_path_name(newdir, newname);
+    char *oldpath;
+    char *newpath;
 
-    res = -ENOSYS;
-    if(f->op.rename)
-        res = f->op.rename(oldpath, newpath);
-    if(res == 0)
-        rename_node(f, olddir, oldname, newdir, newname);
+    res = -ENOENT;
+    oldpath = get_path_name(olddir, oldname);
+    if(oldpath != NULL) {
+        newpath = get_path_name(newdir, newname);
+        if(newpath != NULL) {
+            res = -ENOSYS;
+            if(f->op.rename)
+                res = f->op.rename(oldpath, newpath);
+            if(res == 0)
+                rename_node(f, olddir, oldname, newdir, newname);
+            g_free(newpath);
+        }
+        g_free(oldpath);
+    }
     send_reply(f, in, res, NULL, 0);   
 }
 
 static void do_link(struct fuse *f, struct fuse_in_header *in,
-                    struct fuse_link_in *inarg)
+                    struct fuse_link_in *arg)
 {
     int res;
-    char *oldpath = get_path(in->ino);
-    char *newpath = get_path_name(inarg->newdir, inarg->name);
+    char *oldpath;
+    char *newpath;
 
-    res = -ENOSYS;
-    if(f->op.link)
-        res = f->op.link(oldpath, newpath);
-
+    res = -ENOENT;
+    oldpath = get_path(in->ino);
+    if(oldpath != NULL) {
+        newpath =  get_path_name(arg->newdir, arg->name);
+        if(newpath != NULL) {
+            res = -ENOSYS;
+            if(f->op.link)
+                res = f->op.link(oldpath, newpath);
+            g_free(newpath);
+        }
+        g_free(oldpath);
+    }
     send_reply(f, in, res, NULL, 0);   
 }
 
+static void do_open(struct fuse *f, struct fuse_in_header *in,
+                    struct fuse_open_in *arg)
+{
+    int res;
+    char *path;
+
+    res = -ENOENT;
+    path = get_path(in->ino);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.open)
+            res = f->op.open(path, arg->flags);
+        g_free(path);
+    }
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_read(struct fuse *f, struct fuse_in_header *in,
+                    struct fuse_read_in *arg)
+{
+    int res;
+    char *path;
+    char *buf = g_malloc(arg->size);
+    size_t size;
+
+    path = get_path(in->ino);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.pread)
+            res = f->op.pread(path, buf, arg->size, arg->offset);
+        g_free(path);
+    }
+    
+    size = 0;
+    if(res > 0) {
+        size = res;
+        res = 0;
+    }
+
+    send_reply(f, in, res, buf, size);
+    g_free(buf);
+}
 
 void fuse_loop(struct fuse *f)
 {
@@ -435,6 +597,10 @@
             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;
@@ -469,6 +635,14 @@
             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 */
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index b3b907b..2d030ae 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -17,6 +17,7 @@
 struct node {
     char *name;
     fino_t parent;
+    int mode;
 };
 
 struct fuse {
diff --git a/lib/mount.c b/lib/mount.c
index ace3c65..48d9d45 100644
--- a/lib/mount.c
+++ b/lib/mount.c
@@ -103,7 +103,6 @@
     if(f->dir != NULL)
         return 0;
 
-    f->dir = g_strdup(dir);
     f->fd = open(dev, O_RDWR);
     if(f->fd == -1) {
         perror(dev);
@@ -115,6 +114,7 @@
         return -1;
 
     add_mntent(dev, dir, type);
+    f->dir = g_strdup(dir);
     
     return 0;
 }