x
diff --git a/Makefile b/Makefile
index f264b78..9f6ff4b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,34 +1,25 @@
 CC = gcc
-
-KCFLAGS = -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -pipe
-KCPPFLAGS = -I /lib/modules/`uname -r`/build/include/ -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES
-
 CFLAGS = -Wall -W -g `glib-config --cflags`
 LDFLAGS = `glib-config --libs`
-CPPFLAGS = 
+CPPFLAGS = -Iinclude
 
-all: fuse.o fusemount
 
-dev.o: dev.c
-	$(CC) $(KCFLAGS) $(KCPPFLAGS) -c dev.c
 
-inode.o: inode.c
-	$(CC) $(KCFLAGS) $(KCPPFLAGS) -c inode.c
+all: kernel/fuse.o fusepro
 
-dir.o: dir.c
-	$(CC) $(KCFLAGS) $(KCPPFLAGS) -c dir.c
+kernel/fuse.o: FORCE
+	make -C kernel fuse.o
 
-util.o: util.c
-	$(CC) $(KCFLAGS) $(KCPPFLAGS) -c util.c
+lib/libfuse.a: FORCE
+	make -C lib libfuse.a
 
-fuse_objs = dev.o inode.o dir.o util.o
-
-fuse.o: $(fuse_objs)
-	ld -r -o fuse.o $(fuse_objs)
-
-fusemount: fusemount.o
+fusepro: fusepro.o lib/libfuse.a
 
 clean:
+	make -C kernel clean
+	make -C lib clean
 	rm -f *.o
-	rm -f fusemount
+	rm -f fusepro
 	rm -f *~
+
+FORCE:
diff --git a/dev.c b/dev.c
deleted file mode 100644
index 16d8ad1..0000000
--- a/dev.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
-    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/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/file.h>
-
-#define ICSIZE sizeof(struct fuse_in_common)
-#define OCSIZE sizeof(struct fuse_out_common)
-
-static struct proc_dir_entry *proc_fs_fuse;
-struct proc_dir_entry *proc_fuse_dev;
-
-static int request_wait_answer(struct fuse_req *req)
-{
-	int ret = 0;
-	DECLARE_WAITQUEUE(wait, current);
-
-	add_wait_queue(&req->waitq, &wait);
-	while(!req->done) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		if(signal_pending(current)) {
-			ret = -EINTR;
-			break;
-		}
-		spin_unlock(&fuse_lock);
-		schedule();
-		spin_lock(&fuse_lock);
-	}
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&req->waitq, &wait);
-
-	return ret;
-}
-
-void request_send(struct fuse_conn *fc, struct fuse_in *in,
-		  struct fuse_out *out)
-{
-	int ret;
-	struct fuse_req *req;
-
-	req = kmalloc(sizeof(*req), GFP_KERNEL);
-	if(req == NULL) {
-		out->result = -ENOMEM;
-		return;
-	}
-	
-	req->in = in;
-	req->out = out;
-	req->done = 0;
-	init_waitqueue_head(&req->waitq);
-	
-	spin_lock(&fuse_lock);
-	if(fc->file == NULL) {
-		ret = -ENOTCONN;
-		goto out;
-	}
-
-	req->in->h.unique = fc->reqctr ++;
-	list_add_tail(&req->list, &fc->pending);
-	fc->outstanding ++;
-	/* FIXME: Wait until the number of outstanding requests drops
-           below a certain level */
-	wake_up(&fc->waitq);
-	ret = request_wait_answer(req);
-	fc->outstanding --;
-	list_del(&req->list);
-
-  out:
-	kfree(req);
-	spin_unlock(&fuse_lock);
-
-	if(ret)
-		out->result = ret;
-	else if (out->result <= -512 || out->result > 0) {
-		printk("Bad result from client: %i\n", out->result);
-		out->result = -EPROTO;
-	}
-}
-
-static int request_wait(struct fuse_conn *fc)
-{
-	int ret = 0;
-	DECLARE_WAITQUEUE(wait, current);
-
-	add_wait_queue(&fc->waitq, &wait);
-	while(list_empty(&fc->pending)) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		if(signal_pending(current)) {
-			ret = -ERESTARTSYS;
-			break;
-		}
-		spin_unlock(&fuse_lock);
-		schedule();
-		spin_lock(&fuse_lock);
-	}
-	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&fc->waitq, &wait);
-
-	return ret;
-}
-
-
-static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
-			     loff_t *off)
-{
-	ssize_t ret;
-	struct fuse_conn *fc = file->private_data;
-	struct fuse_req *req;
-	size_t size;
-
-	spin_lock(&fuse_lock);
-	ret = request_wait(fc);
-	if(ret)
-		goto err;
-	
-	req = list_entry(fc->pending.next, struct fuse_req, list);
-	size = ICSIZE + req->in->argsize;
-
-	ret = -EPROTO;
-	if(nbytes < size) {
-		printk("fuse_dev_read: buffer too small (%i)\n", size);
-		goto err;
-	}
-	
-	list_del(&req->list);
-	list_add_tail(&req->list, &fc->processing);
-	spin_unlock(&fuse_lock);
-
-	if(copy_to_user(buf, &req->in->c, ICSIZE) ||
-	   copy_to_user(buf + ICSIZE, req->in->arg, req->in->argsize))
-		return -EFAULT;
-	
-	return size;
-
-  err:
-	spin_unlock(&fuse_lock);
-	return ret;
-}
-
-static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
-{
-	struct list_head *entry;
-	struct fuse_req *req = NULL;
-
-	list_for_each(entry, &fc->processing) {
-		struct fuse_req *tmp;
-		tmp = list_entry(entry, struct fuse_req, list);
-		if(tmp->in->c.unique == unique) {
-			req = tmp;
-			break;
-		}
-	}
-
-	return req;
-}
-
-static ssize_t fuse_dev_write(struct file *file, const char *buf,
-			      size_t nbytes, loff_t *off)
-{
-	struct fuse_conn *fc = file->private_data;
-	struct fuse_req *req;
-	struct fuse_out_common oc;
-	char *tmpbuf;
-
-	if(nbytes > 2 * PAGE_SIZE) {
-		printk("fuse_dev_write: write is too long\n");
-		return -EPROTO;
-	}
-	
-	tmpbuf = (char *)  __get_free_pages(GFP_KERNEL, 1);
-	if(!tmpbuf)
-		return -ENOMEM;
-
-	if(copy_from_user(tmpbuf, buf, nbytes))
-		return -EFAULT;
-	
-	spin_lock(&fuse_lock);
-	req = request_find(fc, oc.unique);
-	
-	if(req == NULL)
-		printk("fuse_dev_write[%i]: unknown request: %i", fc->id,
-		       oc.unique);
-
-		
-
-	else {
-		struct fuse_outparam *out = &param.u.o;
-		if(req->param.u.i.opcode == FUSE_OPEN && out->result == 0)
-			out->u.open_internal.file = fget(out->u.open.fd);
-
-		req->param = param;
-		req->done = 1;
-		wake_up(&req->waitq);
-	}
-	spin_unlock(&fuse_lock);
-
-	return nbytes;
-}
-
-
-static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
-{
-	struct fuse_conn *fc = file->private_data;
-	unsigned int mask = POLLOUT | POLLWRNORM;
-
-	poll_wait(file, &fc->waitq, wait);
-
-	spin_lock(&fuse_lock);
-	if (!list_empty(&fc->pending))
-                mask |= POLLIN | POLLRDNORM;
-	spin_unlock(&fuse_lock);
-
-	return mask;
-}
-
-static struct fuse_conn *new_conn(void)
-{
-	static int connctr = 1;
-	struct fuse_conn *fc;
-
-	fc = kmalloc(sizeof(*fc), GFP_KERNEL);
-	if(fc != NULL) {
-		fc->sb = NULL;
-		fc->file = NULL;
-		init_waitqueue_head(&fc->waitq);
-		INIT_LIST_HEAD(&fc->pending);
-		INIT_LIST_HEAD(&fc->processing);
-		fc->outstanding = 0;
-		fc->reqctr = 0;
-		
-		spin_lock(&fuse_lock);
-		fc->id = connctr ++;
-		spin_unlock(&fuse_lock);
-	}
-	return fc;
-}
-
-static int fuse_dev_open(struct inode *inode, struct file *file)
-{
-	struct fuse_conn *fc;
-
-	fc = new_conn();
-	if(!fc)
-		return -ENOMEM;
-
-	fc->file = file;
-	file->private_data = fc;
-
-	return 0;
-}
-
-static void end_requests(struct list_head *head)
-{
-	while(!list_empty(head)) {
-		struct fuse_req *req;
-		req = list_entry(head->next, struct fuse_req, list);
-		list_del_init(&req->list);
-		req->done = 1;
-		req->param.u.o.result = -ECONNABORTED;
-		wake_up(&req->waitq);
-	}
-}
-
-static int fuse_dev_release(struct inode *inode, struct file *file)
-{
-	struct fuse_conn *fc = file->private_data;
-
-	spin_lock(&fuse_lock);
-	fc->file = NULL;
-	end_requests(&fc->pending);
-	end_requests(&fc->processing);
-	fuse_release_conn(fc);
-	spin_unlock(&fuse_lock);
-	return 0;
-}
-
-static struct file_operations fuse_dev_operations = {
-	owner:		THIS_MODULE,
-	read:		fuse_dev_read,
-	write:		fuse_dev_write,
-	poll:		fuse_dev_poll,
-	open:		fuse_dev_open,
-	release:	fuse_dev_release,
-};
-
-int fuse_dev_init()
-{
-	int ret;
-
-	proc_fs_fuse = NULL;
-	proc_fuse_dev = NULL;
-
-	ret = -EIO;
-	proc_fs_fuse = proc_mkdir("fuse", proc_root_fs);
-	if(!proc_fs_fuse) {
-		printk("fuse: failed to create directory in /proc/fs\n");
-		goto err;
-	}
-
-	proc_fs_fuse->owner = THIS_MODULE;
-	proc_fuse_dev = create_proc_entry("dev", S_IFSOCK | S_IRUGO | S_IWUGO,
-					  proc_fs_fuse);
-	if(!proc_fuse_dev) {
-		printk("fuse: failed to create entry in /proc/fs/fuse\n");
-		goto err;
-	}
-
-	proc_fuse_dev->proc_fops = &fuse_dev_operations;
-
-	return 0;
-
-  err:
-	fuse_dev_cleanup();
-	return ret;
-}
-
-void fuse_dev_cleanup()
-{
-	if(proc_fs_fuse) {
-		remove_proc_entry("dev", proc_fs_fuse);
-		remove_proc_entry("fuse", proc_root_fs);
-	}
-}
-
-/* 
- * Local Variables:
- * indent-tabs-mode: t
- * c-basic-offset: 8
- * End:
- */
diff --git a/dir.c b/dir.c
deleted file mode 100644
index 7a3ccc2..0000000
--- a/dir.c
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
-    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/sched.h>
-#include <linux/file.h>
-#include <linux/slab.h>
-
-static void change_attributes(struct inode *inode, struct fuse_attr *attr)
-{
-	inode->i_mode    = attr->mode;
-	inode->i_nlink   = attr->nlink;
-	inode->i_uid     = attr->uid;
-	inode->i_gid     = attr->gid;
-	inode->i_size    = attr->size;
-	inode->i_blksize = attr->blksize;
-	inode->i_blocks  = attr->blocks;
-	inode->i_atime   = attr->atime;
-	inode->i_mtime   = attr->mtime;
-	inode->i_ctime   = attr->ctime;
-}
-
-static void init_inode(struct inode *inode, struct fuse_attr *attr)
-{
-	change_attributes(inode, attr);
-	
-	if(S_ISREG(inode->i_mode))
-		fuse_file_init(inode);
-	else if(S_ISDIR(inode->i_mode))
-		fuse_dir_init(inode);
-	else if(S_ISLNK(inode->i_mode))
-		fuse_symlink_init(inode);
-	else
-		init_special_inode(inode, inode->i_mode, attr->rdev);
-}
-
-static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
-{
-	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
-	struct fuse_inparam in;
-	struct fuse_outparam out;
-	struct inode *inode;
-	
-	in.opcode = FUSE_LOOKUP;
-	in.ino = dir->i_ino;
-	strcpy(in.u.lookup.name, entry->d_name.name);
-
-	request_send(fc, &in, &out);
-	
-	if(out.result)
-		return ERR_PTR(out.result);
-
-	inode = iget(dir->i_sb, out.u.lookup.ino);
-	if(!inode) 
-		return ERR_PTR(-ENOMEM);
-
-	init_inode(inode, &out.u.lookup.attr);
-
-	d_add(entry, inode);
-	return NULL;
-}
-
-static int fuse_permission(struct inode *inode, int mask)
-{
-
-	return 0;
-}
-
-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_inparam in;
-	struct fuse_outparam out;
-	
-	in.opcode = FUSE_GETATTR;
-	in.ino = inode->i_ino;
-
-	request_send(fc, &in, &out);
-	
-	if(out.result == 0)
-		change_attributes(inode, &out.u.getattr.attr);
-
-	return out.result;
-}
-
-
-#define DIR_BUFSIZE 2048
-
-static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
-{
-	struct file *cfile = file->private_data;
-	char *buf;
-	char *p;
-	int ret;
-	size_t nbytes;
-	
-	buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL);
-	if(!buf)
-		return -ENOMEM;
-
-	ret = kernel_read(cfile, file->f_pos, buf, DIR_BUFSIZE);
-	if(ret < 0) {
-		printk("fuse_readdir: failed to read container file\n");
-		goto out;
-	}
-	nbytes = ret;
-	p = buf;
-	ret = 0;
-	while(nbytes >= FUSE_NAME_OFFSET) {
-		struct fuse_dirent *dirent = (struct fuse_dirent *) p;
-		size_t reclen = FUSE_DIRENT_SIZE(dirent);
-		int err;
-		if(dirent->namelen > NAME_MAX) {
-			printk("fuse_readdir: name too long\n");
-			ret = -EPROTO;
-			goto out;
-		}
-		if(reclen > nbytes)
-			break;
-
-		err = filldir(dstbuf, dirent->name, dirent->namelen,
-			      file->f_pos, dirent->ino, dirent->type);
-		if(err) {
-			ret = err;
-			break;
-		}
-		p += reclen;
-		file->f_pos += reclen;
-		nbytes -= reclen;
-		ret ++;
-	}
-
-  out:
-	kfree(buf);	
-	return ret;
-}
-
-
-
-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_in in;
-	struct fuse_out out;
-	unsigned long page;
-
-	page = __get_free_page(GFP_KERNEL);
-	if(!page)
-		return -ENOMEM;
-
-	in.c.opcode = FUSE_READLINK;
-	in.c.ino = inode->i_ino;
-	in.argsize = 0;
-	out.arg = (void *) page;
-	out.argsize = PAGE_SIZE;
-	
-	request_send(fc, &in, &out);
-	if(out.c.result) {
-		__free_page(page);
-		return out.c.result;
-	}
-
-	*bufp = (char *) page;
-	(*bufp)[PAGE_SIZE - 1] = 0;
-	return 0;
-}
-
-static void free_link(char *link)
-{
-	__free_page((unsigned long) link);
-}
-
-static int fuse_readlink(struct dentry *dentry, char *buffer, int buflen)
-{
-	int ret;
-	char *link;
-
-	ret = read_link(dentry, &link);
-	if(ret)
-		return ret;
-
-	ret = vfs_readlink(dentry, buffer, buflen, link);
-	free_link(link);
-	return ret;
-}
-
-static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-	int ret;
-	char *link;
-
-	ret = read_link(dentry, &link);
-	if(ret)
-		return ret;
-
-	ret = vfs_follow_link(nd, link);
-	free_link(link);
-	return ret;
-}
-
-static int fuse_open(struct inode *inode, struct file *file)
-{
-	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
-	struct fuse_inparam in;
-	struct fuse_outparam out;
-	struct file *cfile = NULL;
-	
-	in.opcode = FUSE_OPEN;
-	in.ino = inode->i_ino;
-	in.u.open.flags = file->f_flags & ~O_EXCL;
-
-	request_send(fc, &in, &out);
-	
-	if(out.result == 0) {
-		struct inode *inode;
-		cfile = out.u.open_internal.file;
-		if(!cfile) {
-			printk("fuse_open: invalid container file\n");
-			return -EPROTO;
-		}
-		inode = cfile->f_dentry->d_inode;
-		if(!S_ISREG(inode->i_mode)) {
-			printk("fuse_open: container is not a regular file\n");
-			fput(cfile);
-			return -EPROTO;
-		}
-
-		file->private_data = cfile;
-	}
-
-	return out.result;
-}
-
-static int fuse_release(struct inode *inode, struct file *file)
-{
-	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
-	struct file *cfile = file->private_data;
-	struct fuse_inparam in;
-	struct fuse_outparam out;
-
-	if(cfile)
-		fput(cfile);
-
-	in.opcode = FUSE_RELEASE;
-	request_send(fc, &in, &out);
-
-	return out.result;
-}
-
-static struct inode_operations fuse_dir_inode_operations =
-{
-	lookup:		fuse_lookup,
-	permission:	fuse_permission,
-        revalidate:	fuse_revalidate,
-};
-
-static struct file_operations fuse_dir_operations = {
-	read:		generic_read_dir,
-	readdir:	fuse_readdir,
-	open:		fuse_open,
-	release:	fuse_release,
-};
-
-static struct inode_operations fuse_file_inode_operations =
-{
-	permission:	fuse_permission,
-        revalidate:	fuse_revalidate,
-};
-
-static struct file_operations fuse_file_operations = {
-};
-
-static struct inode_operations fuse_symlink_inode_operations =
-{
-	readlink:	fuse_readlink,
-	follow_link:	fuse_follow_link,
-        revalidate:	fuse_revalidate,
-};
-
-void fuse_dir_init(struct inode *inode)
-{
-	inode->i_op = &fuse_dir_inode_operations;
-	inode->i_fop = &fuse_dir_operations;
-}
-
-void fuse_file_init(struct inode *inode)
-{
-	inode->i_op = &fuse_file_inode_operations;
-	inode->i_fop = &fuse_file_operations;
-}
-
-void fuse_symlink_init(struct inode *inode)
-{
-	inode->i_op = &fuse_symlink_inode_operations;
-}
-
-/* 
- * Local Variables:
- * indent-tabs-mode: t
- * c-basic-offset: 8
- * End:
- */
diff --git a/fuse.h b/fuse.h
deleted file mode 100644
index 04faf9d..0000000
--- a/fuse.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-    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 <linux/limits.h>
-
-#define FUSE_MOUNT_VERSION 1
-
-struct fuse_mount_data {
-	int version;
-	int fd;
-};
-
-#define FUSE_ROOT_INO 1
-
-struct fuse_attr {
-	unsigned short mode;
-	unsigned short nlink;
-	unsigned short uid;
-	unsigned short gid;
-	unsigned short rdev;
-	unsigned long  size;
-	unsigned long  blksize;
-	unsigned long  blocks;
-	unsigned long  atime;
-	unsigned long  mtime;
-	unsigned long  ctime;
-};
-
-enum fuse_opcode {
-	FUSE_LOOKUP,
-	FUSE_GETATTR,
-	FUSE_READLINK,
-	FUSE_OPEN,
-	FUSE_RELEASE,
-};
-
-/* Conservative buffer size for the client */
-#define FUSE_MAX_IN 8192
-
-struct fuse_in_open {
-	int flag;
-};
-
-struct fuse_out_open {
-	int fd;
-};
-
-struct fuse_in_lookup {
-	char name[NAME_MAX + 1];
-};
-
-struct fuse_out_lookup {
-	unsigned long ino;
-	struct fuse_attr attr;
-};
-
-struct fuse_out_getattr {
-	struct fuse_attr attr;
-};
-
-struct fuse_out_readlink {
-	char link[PATH_MAX + 1];
-};
-
-struct fuse_in_common {
-	int unique;
-	enum fuse_opcode opcode;
-	unsigned long ino;	
-};
-
-struct fuse_out_common {
-	int unique;
-	int result;
-};
-
-struct fuse_in {
-	struct fuse_in_common c;
-	size_t argsize;
-	void *arg;
-};
-
-struct fuse_out {
-	struct fuse_out_common c;
-	size_t argsize;
-	void *arg;
-};
-
-struct fuse_dirent {
-	unsigned long ino;
-	unsigned short namelen;
-	unsigned char type;
-	char name[NAME_MAX + 1];
-};
-
-#define FUSE_NAME_OFFSET ((size_t) ((struct fuse_dirent *) 0)->name)
-#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
-#define FUSE_DIRENT_SIZE(d) \
-	FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
-
-/* 
- * Local Variables:
- * indent-tabs-mode: t
- * c-basic-offset: 8
- * End:
- */
diff --git a/fusemount.c b/fusemount.c
deleted file mode 100644
index 574a3fd..0000000
--- a/fusemount.c
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
-    AVFS: A Virtual File System Library
-    Copyright (C) 1998-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.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <dirent.h>
-#include <assert.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-#include <mntent.h>
-#include <glib.h>
-
-static char *mount_dir;
-
-const char *basedir = "/tmp/pro";
-
-struct node {
-    char *name;
-    unsigned long ino;
-};
-
-static GNode *root;
-static GHashTable *nodetab;
-static unsigned long inoctr = FUSE_ROOT_INO;
-
-static GNode *new_node(const char *name, unsigned long ino)
-{
-    struct node *node = g_new0(struct node, 1);
-    GNode *gn = g_node_new(node);
-    
-    node->name = g_strdup(name);
-    node->ino = ino;
-
-    return gn;
-}
-
-static unsigned long find_node(unsigned long ino, const char *name)
-{
-    GNode *cn;
-    GNode *pn = g_hash_table_lookup(nodetab, (gpointer) ino);
-    if(pn == NULL) {
-        fprintf(stderr, "Can't find parent node %li\n", ino);
-        return 0;
-    }
-    
-    for(cn = pn->children; cn != NULL; cn = cn->next) {
-        struct node *node = (struct node *) cn->data;
-        if(strcmp(node->name, name) == 0)
-            return node->ino;
-    }
-
-    do inoctr++;
-    while(!inoctr && g_hash_table_lookup(nodetab, (gpointer) ino) != NULL);
-    
-    cn = new_node(name, inoctr);
-    g_node_insert(pn, -1, cn);
-    g_hash_table_insert(nodetab, (gpointer) inoctr, cn);
-    
-    return inoctr;
-}
-
-static char *real_path(unsigned long ino)
-{
-    GString *s;
-    char *ss;
-    GNode *gn = g_hash_table_lookup(nodetab, (gpointer) ino);
-
-    if(gn == NULL) {
-        fprintf(stderr, "Can't find node %li\n", ino);
-        return NULL;
-    }
-    
-    s = g_string_new("");
-    for(; gn != NULL; gn = gn->parent) {
-        g_string_prepend(s, ((struct node *) gn->data)->name);
-        g_string_prepend_c(s, '/');
-    }
-    g_string_prepend(s, basedir);
-    ss = s->str;
-    g_string_free(s, FALSE);
-    
-    return ss;
-}
-
-static int get_dir(unsigned long dir)
-{
-    int dirfd;
-    struct fuse_dirent dirent;
-    DIR *dp;
-    struct dirent *de;
-    size_t reclen;
-    char *path;
-    
-    path = real_path(dir);
-    if(path == NULL)
-        return -ENOENT;
-
-    dp = opendir(path);
-    g_free(path);
-    if(dp == NULL) {
-        perror(path);
-        return -errno;
-    }
-    dirfd = open("/tmp/dirtmp", O_RDWR | O_TRUNC | O_CREAT, 0600);
-    if(dirfd == -1) {
-        perror("/tmp/dirtmp");
-        exit(1);
-    }
-    while((de = readdir(dp)) != NULL) {
-        unsigned long ino = find_node(dir, de->d_name);
-        assert(ino != 0);
-
-        dirent.ino = ino;
-        dirent.namelen = strlen(de->d_name);
-        assert(dirent.namelen <= NAME_MAX);
-        strcpy(dirent.name, de->d_name);
-        dirent.type = de->d_type;
-
-        reclen = FUSE_DIRENT_SIZE(&dirent);
-        write(dirfd, &dirent, reclen);
-    }
-    closedir(dp);
-
-    return dirfd;
-}
-
-static int get_attributes(unsigned long ino, struct fuse_attr *attr)
-{
-    char *path;
-    struct stat buf;
-    int res;
-    
-    path = real_path(ino);
-    if(path == NULL)
-        return -ENOENT;
-
-    res = stat(path, &buf);
-    g_free(path);
-    if(res == -1)
-        return -errno;
-
-    attr->mode    = buf.st_mode;
-    attr->nlink   = buf.st_nlink;
-    attr->uid     = buf.st_uid;
-    attr->gid     = buf.st_gid;
-    attr->rdev    = buf.st_rdev;
-    attr->size    = buf.st_size;
-    attr->blksize = buf.st_blksize;
-    attr->blocks  = buf.st_blocks;
-    attr->atime   = buf.st_atime;
-    attr->mtime   = buf.st_mtime;
-    attr->ctime   = buf.st_ctime;
-    
-    return 0;
-}
-
-static void loop(int devfd)
-{
-    int res;
-    struct fuse_param param;
-    struct fuse_outparam out;
-    struct fuse_inparam in;
-    int dirfd;
-    
-    while(1) {
-        res = read(devfd, &param, sizeof(param));
-        if(res == -1) {
-            perror("read");
-            exit(1);
-        }
-
-        printf("unique: %i, opcode: %i\n", param.unique, param.u.i.opcode);
-
-        dirfd = -1;
-        in = param.u.i;
-        switch(in.opcode) {
-        case FUSE_LOOKUP:
-            out.u.lookup.ino = find_node(in.ino, in.u.lookup.name);
-            if(out.u.lookup.ino == 0)
-                out.result = -ENOENT;
-            else
-                out.result = get_attributes(out.u.lookup.ino,
-                                            &out.u.lookup.attr);
-            break;
-
-        case FUSE_GETATTR:
-            out.result = get_attributes(in.ino, &out.u.getattr.attr);
-            break;
-
-        case FUSE_OPEN:
-            dirfd = get_dir(in.ino);
-            if(dirfd >= 0) {
-                out.u.open.fd = dirfd;
-                out.result = 0;
-            }
-            else
-                out.result = dirfd;
-            break;
-
-        case FUSE_RELEASE:
-            out.result = 0;
-            break;
-
-        default:
-            out.result = -EOPNOTSUPP;
-        }
-        param.u.o = out;
-                
-        res = write(devfd, &param, sizeof(param));
-        if(res == -1) {
-            perror("write");
-            exit(1);
-        }
-        if(dirfd != -1) {
-            close(dirfd);
-            unlink("/tmp/dirtmp");
-        }
-    }
-}
-
-static int mount_fuse(const char *dev, const char *dir, int devfd)
-{
-    int res;
-    const char *type;
-    FILE *fd;
-    struct mntent ent;
-    struct fuse_mount_data data;
-    
-    data.version = FUSE_MOUNT_VERSION;
-    data.fd = devfd;
-
-    type = "fuse";
-    res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
-    
-    if(res == -1) {
-        fprintf(stderr, "mount failed: %s\n", strerror(errno));
-	return -1;
-    }
-    
-    fd = setmntent("/etc/mtab", "a");
-    if(fd == NULL) {
-	fprintf(stderr, "setmntent(\"/etc/mtab\") failed: %s\n",
-		strerror(errno));
-	return -1;
-    }
-    
-    ent.mnt_fsname = (char *) dev;
-    ent.mnt_dir = (char *) dir;
-    ent.mnt_type = (char *) type;
-    ent.mnt_opts = "rw,nosuid,nodev";
-    ent.mnt_freq = 0;
-    ent.mnt_passno = 0;
-    res = addmntent(fd, & ent);
-    if(res != 0)
-	fprintf(stderr, "addmntent() failed: %s\n", strerror(errno));
-    
-    endmntent(fd);
-    
-    return 0;
-}
-
-int unmount_fuse(const char *dir)
-{
-    int res;
-    FILE *fdold, *fdnew;
-    struct mntent *entp;
-    
-    res = umount(dir);
-    
-    if(res == -1) {
-        fprintf(stderr, "umount failed: %s\n", strerror(errno));
-	return -1;
-    }
-    
-    fdold = setmntent("/etc/mtab", "r");
-    if(fdold == NULL) {
-	fprintf(stderr, "setmntent(\"/etc/mtab\") failed: %s\n",
-		strerror(errno));
-	return -1;
-    }
-
-    fdnew = setmntent("/etc/mtab~", "w");
-    if(fdnew == NULL) {
-	fprintf(stderr, "setmntent(\"/etc/mtab~\") failed: %s\n",
-		strerror(errno));
-	return -1;
-    }
-
-    do {
-	entp = getmntent(fdold);
-	if(entp != NULL) {
-	    if(strcmp(entp->mnt_dir, dir) != 0) {
-		res = addmntent(fdnew, entp);
-		if(res != 0) {
-		    fprintf(stderr, "addmntent() failed: %s\n",
-			    strerror(errno));
-		}
-	    }
-	}
-    } while(entp != NULL);
-
-    endmntent(fdold);
-    endmntent(fdnew);
-
-    res = rename("/etc/mtab~", "/etc/mtab");
-    if(res == -1) {
-	fprintf(stderr, "rename(\"/etc/mtab~\", \"/etc/mtab\") failed: %s\n", 
-		strerror(errno));
-	return -1;
-    }
-    
-    return 0;
-}
-
-void cleanup()
-{
-    unmount_fuse(mount_dir);
-}
-
-
-void exit_handler()
-{
-    exit(0);
-}
-
-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);
-    }
-}
-
-
-int main(int argc, char *argv[])
-{
-    const char *dev;
-    int devfd;
-
-    if(argc < 3) {
-        fprintf(stderr, "usage: %s dev dir\n", argv[0]);
-        exit(1);
-    }
-
-    dev = argv[1];
-    mount_dir = argv[2];
-
-    devfd = open(dev, O_RDWR);
-    if(devfd == -1) {
-        fprintf(stderr, "failed to open %s: %s\n", dev, strerror(errno));
-        exit(1);
-    }
-
-    mount_fuse(dev, mount_dir, devfd);
-
-    set_signal_handlers();
-    atexit(cleanup);
-
-    root = new_node("/", FUSE_ROOT_INO);
-    nodetab = g_hash_table_new(NULL, NULL);
-    g_hash_table_insert(nodetab, (gpointer) FUSE_ROOT_INO, root);
-
-    loop(devfd);
-    
-    return 0;
-}
-
-
-
diff --git a/fusepro.c b/fusepro.c
new file mode 100644
index 0000000..4c0582c
--- /dev/null
+++ b/fusepro.c
@@ -0,0 +1,125 @@
+#include <fuse.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <signal.h>
+
+static struct fuse *pro_fuse;
+
+static int pro_getattr(const char *path, struct stat *stbuf)
+{
+    int res;
+
+    res = lstat(path, stbuf);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static int pro_readlink(const char *path, char *buf, size_t size)
+{
+    int res;
+
+    res = readlink(path, buf, size - 1);
+    if(res == -1)
+        return -errno;
+
+    buf[res] = '\0';
+    return 0;
+}
+
+
+static int pro_getdir(const char *path, struct fuse_dh *h, dirfiller_t filler)
+{
+    DIR *dp;
+    struct dirent *de;
+    int res;
+
+    dp = opendir(path);
+    if(dp == NULL)
+        return -errno;
+
+    while((de = readdir(dp)) != NULL) {
+        res = filler(h, de->d_name, de->d_type);
+        if(res != 0)
+            break;
+    }
+
+    closedir(dp);
+    return res;
+}
+
+static int pro_mknod(const char *path, int mode, int rdev)
+{
+    int res;
+
+    res = mknod(path, mode, rdev);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
+static void exit_handler()
+{
+    exit(0);
+}
+
+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);
+    }
+}
+
+static void cleanup()
+{
+    fuse_unmount(pro_fuse);
+    fuse_destroy(pro_fuse);
+}
+
+static struct fuse_operations pro_oper = {
+    getattr:	pro_getattr,
+    readlink:	pro_readlink,
+    getdir:     pro_getdir,
+    mknod:	pro_mknod,
+};
+
+int main(int argc, char *argv[])
+{
+    if(argc != 2) {
+        fprintf(stderr, "usage: %s mount_dir\n", argv[0]);
+        exit(1);
+    }
+
+    set_signal_handlers();
+    atexit(cleanup);
+
+    pro_fuse = fuse_new();
+    fuse_mount(pro_fuse, argv[1]);
+    fuse_set_operations(pro_fuse, &pro_oper);
+    fuse_loop(pro_fuse);
+
+    return 0;
+}
diff --git a/include/fuse.h b/include/fuse.h
new file mode 100644
index 0000000..6b1a151
--- /dev/null
+++ b/include/fuse.h
@@ -0,0 +1,37 @@
+/*
+    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.
+*/
+
+/* This file defines the library interface of FUSE */
+
+#include <sys/types.h>
+#include <sys/stat.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 (*readlink) (const char *path, char *buf, size_t size);
+    int (*mknod) (const char *path, int mode, int rdev);
+    int (*getdir) (const char *path, struct fuse_dh *h, dirfiller_t filler);
+};
+
+struct fuse *fuse_new();
+
+int fuse_mount(struct fuse *f, const char *dir);
+
+void fuse_set_operations(struct fuse *f, const struct fuse_operations *op);
+
+void fuse_loop(struct fuse *f);
+
+int fuse_unmount(struct fuse *f);
+
+void fuse_destroy(struct fuse *f);
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
new file mode 100644
index 0000000..fb3aca0
--- /dev/null
+++ b/include/linux/fuse.h
@@ -0,0 +1,100 @@
+/*
+    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.
+*/
+
+/* This file defines the kernel interface of FUSE */
+
+
+#define FUSE_MOUNT_VERSION 1
+
+struct fuse_mount_data {
+	int version;
+	int fd;
+};
+
+#define FUSE_ROOT_INO 1
+
+struct fuse_attr {
+	unsigned short      mode;
+	unsigned short      nlink;
+	unsigned short      uid;
+	unsigned short      gid;
+	unsigned short      rdev;
+	unsigned long long  size;
+	unsigned long       blksize;
+	unsigned long       blocks;
+	unsigned long       atime;
+	unsigned long       mtime;
+	unsigned long       ctime;
+};
+
+enum fuse_opcode {
+	FUSE_LOOKUP     = 1,
+	FUSE_FORGET,
+	FUSE_GETATTR,
+	FUSE_READLINK,
+	FUSE_GETDIR,
+	FUSE_MKNOD,
+};
+
+/* Conservative buffer size for the client */
+#define FUSE_MAX_IN 8192
+
+struct fuse_lookup_out {
+	unsigned long ino;
+	struct fuse_attr attr;
+};
+
+struct fuse_getattr_out {
+	struct fuse_attr attr;
+};
+
+struct fuse_getdir_out {
+	int fd;
+	void *file; /* Used by kernel only */
+};
+
+struct fuse_mknod_in {
+	unsigned short mode;
+	unsigned short rdev;
+	char name[1];
+};
+
+struct fuse_mknod_out {
+	unsigned long ino;
+	struct fuse_attr attr;
+};
+
+struct fuse_in_header {
+	int unique;
+	enum fuse_opcode opcode;
+	unsigned long ino;	
+};
+
+struct fuse_out_header {
+	int unique;
+	int result;
+};
+
+struct fuse_dirent {
+	unsigned long ino;
+	unsigned short namelen;
+	unsigned char type;
+	char name[256];
+};
+
+#define FUSE_NAME_OFFSET ((unsigned int) ((struct fuse_dirent *) 0)->name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+#define FUSE_DIRENT_SIZE(d) \
+	FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
+
+/* 
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/kernel/Makefile b/kernel/Makefile
new file mode 100644
index 0000000..f7769d5
--- /dev/null
+++ b/kernel/Makefile
@@ -0,0 +1,16 @@
+CC = gcc
+CFLAGS = -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -pipe
+CPPFLAGS = -I /lib/modules/`uname -r`/build/include/ -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES -I../include
+
+
+all: fuse.o
+
+fuse_objs = dev.o inode.o dir.o util.o
+
+fuse.o: $(fuse_objs)
+	ld -r -o fuse.o $(fuse_objs)
+
+
+clean:
+	rm -f *.o
+	rm -f *~
diff --git a/kernel/dev.c b/kernel/dev.c
new file mode 100644
index 0000000..a91a176
--- /dev/null
+++ b/kernel/dev.c
@@ -0,0 +1,440 @@
+/*
+    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/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/file.h>
+
+#define IHSIZE sizeof(struct fuse_in_header)
+#define OHSIZE sizeof(struct fuse_out_header)
+
+static struct proc_dir_entry *proc_fs_fuse;
+struct proc_dir_entry *proc_fuse_dev;
+
+static int request_wait_answer(struct fuse_req *req)
+{
+	int ret = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&req->waitq, &wait);
+	while(!list_empty(&req->list)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if(signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+		spin_unlock(&fuse_lock);
+		schedule();
+		spin_lock(&fuse_lock);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&req->waitq, &wait);
+
+	return ret;
+}
+
+static int request_check(struct fuse_req *req, struct fuse_out *outp)
+{
+	struct fuse_out_header *oh;
+	unsigned int size;
+
+	if(!req->out)
+		return -ECONNABORTED;
+
+	oh = (struct fuse_out_header *) req->out;
+	size = req->outsize - OHSIZE;
+	
+	if (oh->result <= -512 || oh->result > 0) {
+		printk("fuse: bad result\n");
+		return -EPROTO;
+	}
+
+	if(size > outp->argsize || 
+	   (oh->result == 0 && !outp->argvar && size != outp->argsize) ||
+	   (oh->result != 0 && size != 0)) {
+		printk("fuse: invalid argument length: %i (%i)\n", size,
+		       req->opcode);
+		return -EPROTO;
+	}
+	
+	memcpy(&outp->h, oh, OHSIZE);
+	outp->argsize = size;
+	if(size)
+		memcpy(outp->arg, req->out + OHSIZE, size);
+	
+	return oh->result;
+}
+
+static void request_free(struct fuse_req *req)
+{
+	kfree(req->in);
+	kfree(req->out);
+	kfree(req);
+}
+
+
+static struct fuse_req *request_new(struct fuse_conn *fc, struct fuse_in *inp,
+				    struct fuse_out *outp)
+{
+	struct fuse_req *req;
+	
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	if(!req)
+		return NULL;
+
+	if(outp)
+		req->outsize = OHSIZE + outp->argsize;
+	else
+		req->outsize = 0;
+	req->out = NULL;
+
+	req->insize = IHSIZE + inp->argsize;
+	req->in = kmalloc(req->insize, GFP_KERNEL);
+	if(!req->in) {
+		request_free(req);
+		return NULL;
+	}
+	memcpy(req->in, &inp->h, IHSIZE);
+	if(inp->argsize)
+		memcpy(req->in + IHSIZE, inp->arg, inp->argsize);
+
+	req->opcode = inp->h.opcode;
+	init_waitqueue_head(&req->waitq);
+
+	return req;
+}
+
+/* If 'outp' is NULL then the request this is asynchronous */
+void request_send(struct fuse_conn *fc, struct fuse_in *inp,
+		  struct fuse_out *outp)
+{
+	int ret;
+	struct fuse_in_header *ih;
+	struct fuse_req *req;
+
+	ret = -ENOMEM;
+	req = request_new(fc, inp, outp);
+	if(!req)
+		goto out;
+
+	spin_lock(&fuse_lock);
+	ret = -ENOTCONN;
+	if(fc->file == NULL)
+		goto out_unlock_free;
+	
+	ih = (struct fuse_in_header *) req->in;
+	if(outp) {
+		do fc->reqctr++;
+		while(!fc->reqctr);
+		ih->unique = req->unique = fc->reqctr;
+	}
+	else
+		ih->unique = req->unique = 0;
+
+	list_add_tail(&req->list, &fc->pending);
+	wake_up(&fc->waitq);
+
+	/* Async reqests are freed in fuse_dev_read() */
+	if(!outp) 
+		goto out_unlock; 
+	
+	ret = request_wait_answer(req);
+	list_del(&req->list);
+	if(!ret)
+		ret = request_check(req, outp);
+
+  out_unlock_free:
+	request_free(req);
+  out_unlock:
+	spin_unlock(&fuse_lock);
+  out:
+	if(outp)
+		outp->h.result = ret;
+}
+
+static int request_wait(struct fuse_conn *fc)
+{
+	int ret = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&fc->waitq, &wait);
+	while(list_empty(&fc->pending)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if(signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		spin_unlock(&fuse_lock);
+		schedule();
+		spin_lock(&fuse_lock);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&fc->waitq, &wait);
+
+	return ret;
+}
+
+
+static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
+			     loff_t *off)
+{
+	int ret;
+	struct fuse_conn *fc = file->private_data;
+	struct fuse_req *req;
+	char *tmpbuf;
+	unsigned int size;
+
+	if(fc->sb == NULL)
+		return -EPERM;
+	
+	spin_lock(&fuse_lock);
+	ret = request_wait(fc);
+	if(ret)
+		goto err;
+	
+	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);
+		ret = -EIO;
+		goto err;
+	}
+	tmpbuf = req->in;
+	req->in = NULL;
+
+	list_del(&req->list);
+	if(req->outsize)
+		list_add_tail(&req->list, &fc->processing);
+	else
+		request_free(req);
+	spin_unlock(&fuse_lock);
+
+	if(copy_to_user(buf, tmpbuf, size))
+		return -EFAULT;
+	
+	return size;
+
+  err:
+	spin_unlock(&fuse_lock);
+	return ret;
+}
+
+static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
+{
+	struct list_head *entry;
+	struct fuse_req *req = NULL;
+
+	list_for_each(entry, &fc->processing) {
+		struct fuse_req *tmp;
+		tmp = list_entry(entry, struct fuse_req, list);
+		if(tmp->unique == unique) {
+			req = tmp;
+			break;
+		}
+	}
+
+	return req;
+}
+
+static ssize_t fuse_dev_write(struct file *file, const char *buf,
+			      size_t nbytes, loff_t *off)
+{
+	ssize_t ret;
+	struct fuse_conn *fc = file->private_data;
+	struct fuse_req *req;
+	char *tmpbuf;
+	struct fuse_out_header *oh;
+
+	if(!fc->sb)
+		return -EPERM;
+
+	ret = -EIO;
+	if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) {
+		printk("fuse_dev_write[%i]: write is short or long\n", fc->id);
+		goto out;
+	}
+	
+	ret = -ENOMEM;
+	tmpbuf = kmalloc(nbytes, GFP_KERNEL);
+	if(!tmpbuf)
+		goto out;
+	
+	ret = -EFAULT;
+	if(copy_from_user(tmpbuf, buf, nbytes))
+		goto out_free;
+	
+	spin_lock(&fuse_lock);
+	oh =  (struct fuse_out_header *) tmpbuf;
+	req = request_find(fc, oh->unique);
+	if(req == NULL) {
+		ret = -ENOENT;
+		goto out_free_unlock;
+	}
+	list_del_init(&req->list);
+	if(req->opcode == FUSE_GETDIR) {
+		/* fget() needs to be done in this context */
+		struct fuse_getdir_out *arg;
+		arg = (struct fuse_getdir_out *) (tmpbuf + OHSIZE);
+		arg->file = fget(arg->fd);
+	}
+	req->out = tmpbuf;
+	req->outsize = nbytes;
+	tmpbuf = NULL;
+	ret = nbytes;
+	wake_up(&req->waitq);
+  out_free_unlock:
+	spin_unlock(&fuse_lock);
+  out_free:
+	kfree(tmpbuf);
+  out:
+	return ret;
+}
+
+
+static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
+{
+	struct fuse_conn *fc = file->private_data;
+	unsigned int mask = POLLOUT | POLLWRNORM;
+
+	if(!fc->sb)
+		return -EPERM;
+
+	poll_wait(file, &fc->waitq, wait);
+
+	spin_lock(&fuse_lock);
+	if (!list_empty(&fc->pending))
+                mask |= POLLIN | POLLRDNORM;
+	spin_unlock(&fuse_lock);
+
+	return mask;
+}
+
+static struct fuse_conn *new_conn(void)
+{
+	static int connctr = 1;
+	struct fuse_conn *fc;
+
+	fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+	if(fc != NULL) {
+		fc->sb = NULL;
+		fc->file = NULL;
+		init_waitqueue_head(&fc->waitq);
+		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;
+}
+
+static int fuse_dev_open(struct inode *inode, struct file *file)
+{
+	struct fuse_conn *fc;
+
+	fc = new_conn();
+	if(!fc)
+		return -ENOMEM;
+
+	fc->file = file;
+	file->private_data = fc;
+
+	return 0;
+}
+
+static void end_requests(struct list_head *head)
+{
+	while(!list_empty(head)) {
+		struct fuse_req *req;
+		req = list_entry(head->next, struct fuse_req, list);
+		list_del_init(&req->list);
+		if(req->outsize)
+			wake_up(&req->waitq);
+		else
+			request_free(req);
+	}
+}
+
+static int fuse_dev_release(struct inode *inode, struct file *file)
+{
+	struct fuse_conn *fc = file->private_data;
+
+	spin_lock(&fuse_lock);
+	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;
+}
+
+static struct file_operations fuse_dev_operations = {
+	owner:		THIS_MODULE,
+	read:		fuse_dev_read,
+	write:		fuse_dev_write,
+	poll:		fuse_dev_poll,
+	open:		fuse_dev_open,
+	release:	fuse_dev_release,
+};
+
+int fuse_dev_init()
+{
+	int ret;
+
+	proc_fs_fuse = NULL;
+	proc_fuse_dev = NULL;
+
+	ret = -EIO;
+	proc_fs_fuse = proc_mkdir("fuse", proc_root_fs);
+	if(!proc_fs_fuse) {
+		printk("fuse: failed to create directory in /proc/fs\n");
+		goto err;
+	}
+
+	proc_fs_fuse->owner = THIS_MODULE;
+	proc_fuse_dev = create_proc_entry("dev", S_IFSOCK | S_IRUGO | S_IWUGO,
+					  proc_fs_fuse);
+	if(!proc_fuse_dev) {
+		printk("fuse: failed to create entry in /proc/fs/fuse\n");
+		goto err;
+	}
+
+	proc_fuse_dev->proc_fops = &fuse_dev_operations;
+
+	return 0;
+
+  err:
+	fuse_dev_cleanup();
+	return ret;
+}
+
+void fuse_dev_cleanup()
+{
+	if(proc_fs_fuse) {
+		remove_proc_entry("dev", proc_fs_fuse);
+		remove_proc_entry("fuse", proc_root_fs);
+	}
+}
+
+/* 
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/kernel/dir.c b/kernel/dir.c
new file mode 100644
index 0000000..92ddc55
--- /dev/null
+++ b/kernel/dir.c
@@ -0,0 +1,384 @@
+/*
+    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/sched.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+
+static void change_attributes(struct inode *inode, struct fuse_attr *attr)
+{
+	inode->i_mode    = attr->mode;
+	inode->i_nlink   = attr->nlink;
+	inode->i_uid     = attr->uid;
+	inode->i_gid     = attr->gid;
+	inode->i_size    = attr->size;
+	inode->i_blksize = attr->blksize;
+	inode->i_blocks  = attr->blocks;
+	inode->i_atime   = attr->atime;
+	inode->i_mtime   = attr->mtime;
+	inode->i_ctime   = attr->ctime;
+}
+
+static void init_inode(struct inode *inode, struct fuse_attr *attr)
+{
+	change_attributes(inode, attr);
+	
+	if(S_ISREG(inode->i_mode))
+		fuse_file_init(inode);
+	else if(S_ISDIR(inode->i_mode))
+		fuse_dir_init(inode);
+	else if(S_ISLNK(inode->i_mode))
+		fuse_symlink_init(inode);
+	else {
+		fuse_special_init(inode);
+		init_special_inode(inode, inode->i_mode, attr->rdev);
+	}
+}
+
+
+static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
+{
+	struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_lookup_out arg;
+	struct inode *inode;
+	
+	in.h.opcode = FUSE_LOOKUP;
+	in.h.ino = dir->i_ino;
+	in.argsize = entry->d_name.len + 1;
+	in.arg = entry->d_name.name;
+	out.argsize = sizeof(arg);
+	out.arg = &arg;
+	request_send(fc, &in, &out);
+	
+	if(out.h.result) {
+		/* Negative dentries are not hashed */
+		if(out.h.result == -ENOENT)
+			return NULL;
+		else
+			return ERR_PTR(out.h.result);
+	}
+
+	inode = iget(dir->i_sb, arg.ino);
+	if(!inode) 
+		return ERR_PTR(-ENOMEM);
+
+	init_inode(inode, &arg.attr);
+	d_add(entry, inode);
+	return NULL;
+}
+
+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_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_mknod_in *inarg;
+	unsigned int insize;
+	struct fuse_mknod_out outarg;
+	struct inode *inode;
+	
+	insize = offsetof(struct fuse_mknod_in, name) + entry->d_name.len + 1;
+	inarg = kmalloc(insize, GFP_KERNEL);
+	if(!inarg)
+		return -ENOMEM;
+	
+	inarg->mode = mode;
+	inarg->rdev = rdev;
+	strcpy(inarg->name, entry->d_name.name);
+
+	in.h.opcode = FUSE_MKNOD;
+	in.h.ino = dir->i_ino;
+	in.argsize = insize;
+	in.arg = inarg;
+	out.argsize = sizeof(outarg);
+	out.arg = &outarg;
+	request_send(fc, &in, &out);
+	kfree(inarg);
+	if(out.h.result)
+		return out.h.result;
+
+	inode = iget(dir->i_sb, outarg.ino);
+	if(!inode) 
+		return -ENOMEM;
+
+	init_inode(inode, &outarg.attr);
+	d_add(entry, inode);
+	return 0;
+}
+
+static int fuse_create(struct inode *dir, struct dentry *entry, int mode)
+{
+	return fuse_mknod(dir, entry, mode, 0);
+}
+
+static int fuse_permission(struct inode *inode, int mask)
+{
+
+	return 0;
+}
+
+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_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_getattr_out arg;
+	
+	in.h.opcode = FUSE_GETATTR;
+	in.h.ino = inode->i_ino;
+	out.argsize = sizeof(arg);
+	out.arg = &arg;
+	request_send(fc, &in, &out);
+	
+	if(out.h.result == 0)
+		change_attributes(inode, &arg.attr);
+
+	return out.h.result;
+}
+
+
+#define DIR_BUFSIZE 2048
+
+static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
+{
+	struct file *cfile = file->private_data;
+	char *buf;
+	char *p;
+	int ret;
+	size_t nbytes;
+	
+	buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL);
+	if(!buf)
+		return -ENOMEM;
+
+	ret = kernel_read(cfile, file->f_pos, buf, DIR_BUFSIZE);
+	if(ret < 0) {
+		printk("fuse_readdir: failed to read container file\n");
+		goto out;
+	}
+	nbytes = ret;
+	p = buf;
+	ret = 0;
+	while(nbytes >= FUSE_NAME_OFFSET) {
+		struct fuse_dirent *dirent = (struct fuse_dirent *) p;
+		size_t reclen = FUSE_DIRENT_SIZE(dirent);
+		int over;
+		if(dirent->namelen > NAME_MAX) {
+			printk("fuse_readdir: name too long\n");
+			ret = -EPROTO;
+			goto out;
+		}
+		if(reclen > nbytes)
+			break;
+
+		over = filldir(dstbuf, dirent->name, dirent->namelen,
+			      file->f_pos, dirent->ino, dirent->type);
+		if(over)
+			break;
+
+		p += reclen;
+		file->f_pos += reclen;
+		nbytes -= reclen;
+	}
+
+  out:
+	kfree(buf);	
+	return ret;
+}
+
+
+
+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_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	unsigned long page;
+
+	page = __get_free_page(GFP_KERNEL);
+	if(!page)
+		return -ENOMEM;
+
+	in.h.opcode = FUSE_READLINK;
+	in.h.ino = inode->i_ino;
+	out.arg = (void *) page;
+	out.argsize = PAGE_SIZE - 1;
+	out.argvar = 1;
+	request_send(fc, &in, &out);
+	if(out.h.result) {
+		free_page(page);
+		return out.h.result;
+	}
+
+	*bufp = (char *) page;
+	(*bufp)[out.argsize] = '\0';
+	return 0;
+}
+
+static void free_link(char *link)
+{
+	free_page((unsigned long) link);
+}
+
+static int fuse_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	int ret;
+	char *link;
+
+	ret = read_link(dentry, &link);
+	if(ret)
+		return ret;
+
+	ret = vfs_readlink(dentry, buffer, buflen, link);
+	free_link(link);
+	return ret;
+}
+
+static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	int ret;
+	char *link;
+
+	ret = read_link(dentry, &link);
+	if(ret)
+		return ret;
+
+	ret = vfs_follow_link(nd, link);
+	free_link(link);
+	return ret;
+}
+
+static int fuse_dir_open(struct inode *inode, struct file *file)
+{
+	struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_getdir_out outarg;
+
+	if(!(file->f_flags & O_DIRECTORY))
+		return -EISDIR;
+	
+	in.h.opcode = FUSE_GETDIR;
+	in.h.ino = inode->i_ino;
+	out.argsize = sizeof(outarg);
+	out.arg = &outarg;
+	request_send(fc, &in, &out);
+	if(out.h.result == 0) {
+		struct file *cfile = outarg.file;
+		struct inode *inode;
+		if(!cfile) {
+			printk("fuse_getdir: invalid file\n");
+			return -EPROTO;
+		}
+		inode = cfile->f_dentry->d_inode;
+		if(!S_ISREG(inode->i_mode)) {
+			printk("fuse_getdir: not a regular file\n");
+			fput(cfile);
+			return -EPROTO;
+		}
+
+		file->private_data = cfile;
+	}
+
+	return out.h.result;
+}
+
+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 struct inode_operations fuse_dir_inode_operations =
+{
+	lookup:		fuse_lookup,
+	create:         fuse_create,
+	mknod:          fuse_mknod,
+#if 0
+
+	link:           fuse_link,
+	unlink:         fuse_unlink,
+	symlink:        fuse_symlink,
+	mkdir:          fuse_mkdir,
+	rmdir:          fuse_rmdir,
+	rename:         fuse_rename,
+#endif
+	permission:	fuse_permission,
+        revalidate:	fuse_revalidate,
+};
+
+static struct file_operations fuse_dir_operations = {
+	read:		generic_read_dir,
+	readdir:	fuse_readdir,
+	open:		fuse_dir_open,
+	release:	fuse_dir_release,
+};
+
+static struct inode_operations fuse_file_inode_operations = {
+	permission:	fuse_permission,
+        revalidate:	fuse_revalidate,
+};
+
+static struct inode_operations fuse_special_inode_operations = {
+	permission:	fuse_permission,
+        revalidate:	fuse_revalidate,
+};
+
+static struct file_operations fuse_file_operations = {
+};
+
+static struct inode_operations fuse_symlink_inode_operations =
+{
+	readlink:	fuse_readlink,
+	follow_link:	fuse_follow_link,
+        revalidate:	fuse_revalidate,
+};
+
+void fuse_dir_init(struct inode *inode)
+{
+	inode->i_op = &fuse_dir_inode_operations;
+	inode->i_fop = &fuse_dir_operations;
+}
+
+void fuse_file_init(struct inode *inode)
+{
+	inode->i_op = &fuse_file_inode_operations;
+	inode->i_fop = &fuse_file_operations;
+}
+
+void fuse_symlink_init(struct inode *inode)
+{
+	inode->i_op = &fuse_symlink_inode_operations;
+}
+
+void fuse_special_init(struct inode *inode)
+{
+	inode->i_op = &fuse_special_inode_operations;
+}
+
+/* 
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/fuse_i.h b/kernel/fuse_i.h
similarity index 70%
rename from fuse_i.h
rename to kernel/fuse_i.h
index 5f80620..1eb0bb4 100644
--- a/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -6,14 +6,15 @@
     See the file COPYING.
 */
 
-#include "fuse.h"
-
+#include <linux/fuse.h>
 #include <linux/fs.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
 
 #define FUSE_VERSION "0.1"
 
+#define MAX_CLEARED 256
+
 /**
  * A Fuse connection.
  *
@@ -37,8 +38,11 @@
 	/** The list of requests being processed */
 	struct list_head processing;
 
-	/** The number of outstanding requests */
-	int outstanding;
+	/** The number of cleared inodes */
+	unsigned int numcleared;
+	
+	/** The array of cleared inode numbers */
+	unsigned long *cleared;
 
 	/** Connnection number (for debuging) */
 	int id;
@@ -54,26 +58,46 @@
 	/** The request list */
 	struct list_head list;
 
-	/** The request input parameters */
-	struct fuse_in *in;
+	/** The request ID */
+	int unique;
 
-	/** The request result */
-	struct fuse_out *out;
+	/** The opcode */
+	enum fuse_opcode opcode;
+	
+	/** The request input size */
+	unsigned int insize;
 
-	/** The file returned by open */
-	struct file *file;
+	/** The request input */
+	char *in;
+	
+	/** The maximum request output size, if zero, then the request is
+            asynchronous */
+	unsigned int outsize;
+
+	/** The request output */
+	char *out;
 
 	/** The request wait queue */
 	wait_queue_head_t waitq;
-
-	/** True if the request is finished */
-	int done;
 };
 
-struct fuse_out_open_internal {
-	file *file;
+
+struct fuse_in {
+	struct fuse_in_header h;
+	unsigned int argsize;
+	const void *arg;
 };
 
+struct fuse_out {
+	struct fuse_out_header h;
+	unsigned int argsize;
+	unsigned int argvar;
+	void *arg;
+};
+
+#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
+#define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }
+
 
 /**
  * The proc entry for the client device ("/proc/fs/fuse/dev")
@@ -101,6 +125,11 @@
 void fuse_symlink_init(struct inode *inode);
 
 /**
+ * Fill in the special inode operaions
+ */
+void fuse_special_init(struct inode *inode);
+
+/**
  * Check if the connection can be released, and if yes, then free the
  * connection structure
  */
diff --git a/inode.c b/kernel/inode.c
similarity index 67%
rename from inode.c
rename to kernel/inode.c
index 5c2b743..ac2d6be 100644
--- a/inode.c
+++ b/kernel/inode.c
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/slab.h>
 #include <linux/file.h>
 
 #define FUSE_SUPER_MAGIC 0x65735546
@@ -20,6 +21,70 @@
 	/* 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_KERNEL);
+	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 = inode->i_sb->u.generic_sbp;
+	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);
+	}
+}
+
 static void fuse_put_super(struct super_block *sb)
 {
 	struct fuse_conn *fc = sb->u.generic_sbp;
@@ -33,6 +98,7 @@
 
 static struct super_operations fuse_super_operations = {
 	read_inode:	fuse_read_inode,
+	clear_inode:	fuse_clear_inode,
 	put_super:	fuse_put_super,
 };
 
diff --git a/util.c b/kernel/util.c
similarity index 100%
rename from util.c
rename to kernel/util.c
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 0000000..e72a39d
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,16 @@
+CC = gcc
+CFLAGS = -Wall -W -g `glib-config --cflags`
+LDFLAGS = `glib-config --libs`
+CPPFLAGS = -I../include
+
+
+all: libfuse.a
+
+libfuse_objs = mount.o fuse.o
+
+libfuse.a: $(libfuse_objs)
+	ar cr libfuse.a $(libfuse_objs)
+
+clean:
+	rm -f *.o *.a
+	rm -f *~
diff --git a/lib/fuse.c b/lib/fuse.c
new file mode 100644
index 0000000..f846f74
--- /dev/null
+++ b/lib/fuse.c
@@ -0,0 +1,393 @@
+/*
+    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/fuse.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+static guint name_hash(const struct node *node)
+{
+    return g_str_hash(node->name) ^ node->parent;
+}
+
+static gint name_compare(const struct node *node1, const struct node *node2)
+{
+    return
+        node1->parent == node2->parent &&
+        strcmp(node1->name, node2->name) == 0;
+}
+
+static struct node *new_node(fino_t parent, const char *name)
+{
+    struct node *node = g_new0(struct node, 1);
+    node->name = strdup(name);
+    node->parent = parent;
+    return node;
+}
+
+static int free_node(struct node *node)
+{
+    g_free(node->name);
+    g_free(node);
+    return 1;
+}
+
+static inline struct node *get_node(fino_t ino)
+{
+    return (struct node *) ino;
+}
+
+static inline fino_t get_ino(struct node *node)
+{
+    return (fino_t) node;
+}
+
+
+static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
+{
+    struct node *node;
+    struct node tmp;
+    
+    tmp.name = name;
+    tmp.parent = parent;
+
+    node = g_hash_table_lookup(f->nametab, &tmp);
+    if(node != NULL)
+        return get_ino(node);
+
+    if(!create)
+        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)
+{
+    GString *s;
+    char *ss;
+
+    s = g_string_new("");
+    if(ino == FUSE_ROOT_INO)
+        g_string_prepend_c(s, '/');
+    else {
+        struct node *node;
+        for(; ino != FUSE_ROOT_INO; ino = node->parent) {
+            node = get_node(ino);
+            g_string_prepend(s, node->name);
+            g_string_prepend_c(s, '/');
+        }
+    }
+
+    ss = s->str;
+    g_string_free(s, FALSE);
+    
+    return ss;
+}
+
+static void remove_node(struct fuse *f, fino_t ino)
+{
+    struct node *node = get_node(ino);
+    g_hash_table_remove(f->nametab, node);
+    free_node(node);
+}
+
+
+static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
+{
+    attr->mode    = stbuf->st_mode;
+    attr->nlink   = stbuf->st_nlink;
+    attr->uid     = stbuf->st_uid;
+    attr->gid     = stbuf->st_gid;
+    attr->rdev    = stbuf->st_rdev;
+    attr->size    = stbuf->st_size;
+    attr->blksize = stbuf->st_blksize;
+    attr->blocks  = stbuf->st_blocks;
+    attr->atime   = stbuf->st_atime;
+    attr->mtime   = stbuf->st_mtime;
+    attr->ctime   = stbuf->st_ctime;
+}
+
+static int get_attributes(struct fuse *f, fino_t ino, struct fuse_attr *attr)
+{
+    char *path;
+    struct stat buf;
+    int res;
+
+    if(f->op.getattr == NULL)
+        return -ENOSYS;
+    
+    path = get_path(ino);
+    res = f->op.getattr(path, &buf);
+    g_free(path);
+    if(res == 0) 
+        convert_stat(&buf, attr);
+    
+    return res;
+}
+
+static int read_link(struct fuse *f, fino_t ino, char *buf, size_t size)
+{
+    char *path;
+    int res;
+    
+    if(f->op.readlink == NULL)
+        return -ENOSYS;
+
+    path = get_path(ino);
+    res = f->op.readlink(path, buf, size);
+    g_free(path);
+
+    return res;
+}
+
+static int fill_dir(struct fuse_dh *dh, char *name, int type)
+{
+    struct fuse_dirent dirent;
+    size_t reclen;
+    size_t res;
+
+    dirent.ino = find_node(dh->fuse, dh->dir, name, 0);
+    dirent.namelen = strlen(name);
+    strncpy(dirent.name, name, sizeof(dirent.name));
+    dirent.type = type;
+    reclen = FUSE_DIRENT_SIZE(&dirent);
+    res = fwrite(&dirent, reclen, 1, dh->fp);
+    if(res == 0) {
+        perror("writing directory file");
+        return -EIO;
+    }
+    return 0;
+}
+
+static int get_dir(struct fuse *f, fino_t ino, FILE *fp)
+{
+    char *path;
+    int res;
+    struct fuse_dh dh;
+    
+    if(f->op.getdir == NULL)
+        return -ENOSYS;
+
+    dh.fuse = f;
+    dh.fp = fp;
+    dh.dir = ino;
+        
+    path = get_path(ino);
+    res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
+    g_free(path);
+
+    return res;
+}
+
+
+static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
+                       void *arg, size_t argsize)
+{
+    int res;
+    char *outbuf;
+    size_t outsize;
+    struct fuse_out_header *out;
+
+    if(result > 0) {
+        fprintf(stderr, "positive result to operation %i : %i\n", in->opcode,
+                result);
+        result = -ERANGE;
+    }
+
+    if(result != 0)
+        argsize = 0;
+
+    outsize = sizeof(struct fuse_out_header) + argsize;
+    outbuf = (char *) g_malloc(outsize);
+    out = (struct fuse_out_header *) outbuf;
+    out->unique = in->unique;
+    out->result = result;
+    if(argsize != 0)
+        memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
+
+    printf("   unique: %i, result: %i (%s), outsize: %i\n", out->unique,
+           out->result, strerror(-out->result), outsize);
+                
+    res = write(f->fd, outbuf, outsize);
+    if(res == -1)
+        perror("writing fuse device");
+
+    g_free(outbuf);
+}
+
+static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
+{
+    int res;
+    struct fuse_lookup_out arg;
+
+    arg.ino = find_node(f, in->ino, name, 1);
+    res = get_attributes(f, arg.ino, &arg.attr);
+
+    send_reply(f, in, res, &arg, sizeof(arg));
+}
+
+
+static void do_forget(struct fuse *f, unsigned long *inos, size_t num)
+{
+    size_t i;
+
+    for(i = 0; i < num; i++)
+        remove_node(f, inos[i]);
+}
+
+static void do_getattr(struct fuse *f, struct fuse_in_header *in)
+{
+    int res;
+    struct fuse_getattr_out arg;
+
+    res = get_attributes(f, in->ino, &arg.attr);
+    send_reply(f, in, res, &arg, sizeof(arg));
+}
+
+static void do_readlink(struct fuse *f, struct fuse_in_header *in)
+{
+    int res;
+    char link[PATH_MAX + 1];
+
+    res = read_link(f, in->ino, link, PATH_MAX + 1);
+    send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
+}
+
+static void do_mknod(struct fuse *f, struct fuse_in_header *in,
+                     struct fuse_mknod_in *inarg)
+{
+    int res;
+    struct fuse_mknod_out outarg;
+    
+    res = -ENOSYS;
+    if(f->op.mknod != NULL && f->op.getattr != NULL) {
+        char *path;
+        struct stat buf;
+
+        outarg.ino = find_node(f, in->ino, inarg->name, 1);
+        path = get_path(outarg.ino);
+        res = f->op.mknod(path, inarg->mode, inarg->rdev);
+        if(res == 0)
+            res = f->op.getattr(path, &buf);
+        g_free(path);
+
+        if(res == 0)
+            convert_stat(&buf, &outarg.attr);
+        else
+            remove_node(f, outarg.ino);
+    }
+    send_reply(f, in, res, &outarg, sizeof(outarg));
+}
+
+static void do_getdir(struct fuse *f, struct fuse_in_header *in)
+{
+    int res;
+    struct fuse_getdir_out arg;
+    FILE *fp = tmpfile();
+
+    res = get_dir(f, in->ino, fp);
+    fflush(fp);
+    arg.fd = fileno(fp);
+    send_reply(f, in, res, &arg, sizeof(arg));
+    fclose(fp);
+}
+
+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;
+    
+    while(1) {
+        res = read(f->fd, inbuf, sizeof(inbuf));
+        if(res == -1) {
+            perror("reading fuse device");
+            continue;
+        }
+        insize = res;
+        
+        if(insize < 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_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;
+
+        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);
+        }
+    }
+}
+
+struct fuse *fuse_new()
+{
+    struct fuse *f = g_new0(struct fuse, 1);
+
+    f->fd = -1;
+    f->dir = NULL;
+    f->nametab = g_hash_table_new((GHashFunc) name_hash,
+                                  (GCompareFunc) name_compare);
+
+    return f;
+}
+
+
+void fuse_set_operations(struct fuse *f, const struct fuse_operations *op)
+{
+    f->op = *op;
+}
+
+void fuse_destroy(struct fuse *f)
+{
+    fuse_unmount(f);
+    g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL);
+    g_hash_table_destroy(f->nametab);
+    g_free(f);
+}
+
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
new file mode 100644
index 0000000..b3b907b
--- /dev/null
+++ b/lib/fuse_i.h
@@ -0,0 +1,33 @@
+/*
+    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.h"
+#include <glib.h>
+#include <stdio.h>
+
+#define FUSE_DEV "/proc/fs/fuse/dev"
+
+typedef unsigned long fino_t;
+
+struct node {
+    char *name;
+    fino_t parent;
+};
+
+struct fuse {
+    char *dir;
+    int fd;
+    struct fuse_operations op;
+    GHashTable *nametab;
+};
+
+struct fuse_dh {
+    struct fuse *fuse;
+    fino_t dir;
+    FILE *fp;
+};
diff --git a/lib/mount.c b/lib/mount.c
new file mode 100644
index 0000000..ace3c65
--- /dev/null
+++ b/lib/mount.c
@@ -0,0 +1,142 @@
+/*
+    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/fuse.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <mntent.h>
+
+static int do_mount(const char *dev, const char *dir, const char *type, int fd)
+{
+    int res;
+    struct fuse_mount_data data;
+    
+    data.version = FUSE_MOUNT_VERSION;
+    data.fd = fd;
+
+    res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
+    if(res == -1) {
+        perror("mount failed");
+	return -1;
+    }
+    
+    return 0;
+}
+
+static void add_mntent(const char *dev, const char *dir, const char *type)
+{
+    int res;
+    FILE *fp;
+    struct mntent ent;
+    
+    fp = setmntent("/etc/mtab", "a");
+    if(fp == NULL) {
+        perror("setmntent");
+	return;
+    }
+    
+    ent.mnt_fsname = (char *) dev;
+    ent.mnt_dir = (char *) dir;
+    ent.mnt_type = (char *) type;
+    ent.mnt_opts = "rw,nosuid,nodev";
+    ent.mnt_freq = 0;
+    ent.mnt_passno = 0;
+    res = addmntent(fp, & ent);
+    if(res != 0)
+	perror("addmntent");
+    
+    endmntent(fp);
+
+}
+
+static void remove_mntent(const char *dir)
+{
+    int res;
+    FILE *fdold, *fdnew;
+    struct mntent *entp;
+        
+    fdold = setmntent("/etc/mtab", "r");
+    if(fdold == NULL) {
+        perror("/etc/mtab");
+	return;
+    }
+
+    fdnew = setmntent("/etc/mtab~", "w");
+    if(fdnew == NULL) {
+        perror("/etc/mtab~");
+	return;
+    }
+
+    do {
+	entp = getmntent(fdold);
+	if(entp != NULL && strcmp(entp->mnt_dir, dir) != 0) {
+            res = addmntent(fdnew, entp);
+            if(res != 0)
+                perror("addmntent");
+        }
+    } while(entp != NULL);
+
+    endmntent(fdold);
+    endmntent(fdnew);
+
+    res = rename("/etc/mtab~", "/etc/mtab");
+    if(res == -1)
+        perror("renameing /etc/mtab~ to /etc/mtab");
+}
+
+int fuse_mount(struct fuse *f, const char *dir)
+{
+    int res;
+    const char *dev = FUSE_DEV;
+    const char *type = "fuse";
+
+    if(f->dir != NULL)
+        return 0;
+
+    f->dir = g_strdup(dir);
+    f->fd = open(dev, O_RDWR);
+    if(f->fd == -1) {
+        perror(dev);
+        return -1;
+    }
+    
+    res = do_mount(dev, dir, type, f->fd);
+    if(res == -1)
+        return -1;
+
+    add_mntent(dev, dir, type);
+    
+    return 0;
+}
+
+int fuse_unmount(struct fuse *f)
+{
+    int res;
+
+    if(f->dir == NULL)
+        return 0;
+
+    close(f->fd);
+    f->fd = -1;
+
+    res = umount(f->dir);
+    if(res == -1)
+        perror("umount failed");
+    else
+        remove_mntent(f->dir);
+
+    g_free(f->dir);
+    f->dir = NULL;
+
+    return res;
+}