# This is the start of the FUSE server support, which will export the UML
# filesystem to the host as a FUSE filesystem.
Index: linux-2.6.17/arch/um/Kconfig
===================================================================
--- linux-2.6.17.orig/arch/um/Kconfig	2007-11-19 10:56:56.000000000 -0500
+++ linux-2.6.17/arch/um/Kconfig	2007-11-19 11:32:36.000000000 -0500
@@ -257,6 +257,13 @@ config KERNEL_STACK_ORDER
 	  be 1 << order pages.  The default is OK unless you're running Valgrind
 	  on UML, in which case, set this to 3.
 
+config FUSE_SERVER
+	bool "FUSE server"
+	default n
+	help
+	This enables the UML FUSE (http://fuse.sourceforge.net/) server, which
+	allows you to mount a UML filesystem on the host.
+
 endmenu
 
 source "init/Kconfig"
Index: linux-2.6.17/arch/um/Makefile
===================================================================
--- linux-2.6.17.orig/arch/um/Makefile	2007-11-19 11:12:16.000000000 -0500
+++ linux-2.6.17/arch/um/Makefile	2007-11-19 11:32:36.000000000 -0500
@@ -136,6 +136,10 @@ ifeq ($(CONFIG_X11_FB),y)
 UML_LIBS += -L/usr/X11R6/lib -lX11 -lXext
 endif
 
+ifeq ($(CONFIG_FUSE_SERVER),y)
+UML_LIBS += -L/usr/local/lib -lfuse
+endif
+
 CFLAGS_vmlinux := $(LINK-y) $(LINK_WRAPS)
 define cmd_vmlinux__
 	$(CC) $(CFLAGS_vmlinux) -o $@ \
Index: linux-2.6.17/arch/um/os-Linux/Makefile
===================================================================
--- linux-2.6.17.orig/arch/um/os-Linux/Makefile	2007-10-24 10:04:50.000000000 -0400
+++ linux-2.6.17/arch/um/os-Linux/Makefile	2007-11-19 11:32:36.000000000 -0500
@@ -10,6 +10,10 @@ obj-y = aio.o elf_aux.o execvp.o file.o 
 obj-$(CONFIG_TTY_LOG) += tty_log.o
 user-objs-$(CONFIG_TTY_LOG) += tty_log.o
 
+obj-$(CONFIG_FUSE_SERVER) += fuse.o
+user-objs-$(CONFIG_FUSE_SERVER) += fuse.o
+CFLAGS_fuse.o += -I/usr/local/include
+
 USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \
 	main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \
 	trap.o tty.o tls.o uaccess.o umid.o util.o
Index: linux-2.6.17/arch/um/os-Linux/fuse.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/arch/um/os-Linux/fuse.c	2007-11-19 11:32:36.000000000 -0500
@@ -0,0 +1,641 @@
+#include <stddef.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include "user.h"
+#include "uml-fuse.h"
+#include "kern_constants.h"
+#include "os.h"
+#define FUSE_USE_VERSION 25
+#include <fuse/fuse_lowlevel.h>
+
+struct fuse_data {
+	unsigned long long root_ino;
+};
+
+static void uml_fuse_init(void *userdata)
+{
+	struct fuse_data *data = userdata;
+
+	data->root_ino = fuse_vfs_lookup_root();
+}
+
+static void uml_fuse_destroy(void *userdata)
+{
+	struct fuse_data *data = userdata;
+
+	fuse_vfs_forget(data->root_ino, 1);
+}
+
+static void fill_stat(struct stat *stat, unsigned long long inode,
+		      unsigned long mode, unsigned long nlink,
+		      unsigned long uid, unsigned long gid, unsigned long rdev,
+		      unsigned long long size, unsigned long blksize,
+		      unsigned long long blocks, unsigned long long atime,
+		      unsigned long long mtime, unsigned long long ctime)
+{
+	stat->st_ino = inode;
+	stat->st_mode = mode;
+	stat->st_nlink = nlink;
+	stat->st_uid = uid;
+	stat->st_uid = uid;
+	stat->st_gid = gid;
+	stat->st_rdev = rdev;
+	stat->st_size = size;
+	stat->st_blksize = blksize;
+	stat->st_blocks = blocks;
+	stat->st_ctime = ctime;
+	stat->st_mtime = mtime;
+	stat->st_atime = atime;
+}
+
+static void fill_fuse_entry(struct fuse_entry_param *e,
+			    unsigned long long inode, unsigned long mode,
+			    unsigned long nlink, unsigned long uid,
+			    unsigned long gid, unsigned long rdev,
+			    unsigned long long size, unsigned long blksize,
+			    unsigned long long blocks, unsigned long long atime,
+			    unsigned long long mtime, unsigned long long ctime)
+{
+	struct stat *stbuf;
+
+	memset(e, 0, sizeof(*e));
+	stbuf = &e->attr;
+	fill_stat(stbuf, inode, mode, nlink, uid, gid, rdev, size, blksize,
+		  blocks, atime, mtime, ctime);
+
+	e->ino = stbuf->st_ino;
+	e->attr_timeout = 1.0;
+	e->entry_timeout = 1.0;
+}
+
+static void uml_fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+	struct fuse_entry_param e;
+	unsigned long long inode, size, blocks, atime, ctime, mtime;
+	unsigned long mode, nlink, uid, gid, rdev, blksize;
+	int err;
+
+	if(parent == FUSE_ROOT_ID){
+		struct fuse_data *data = fuse_req_userdata(req);
+
+		parent = data->root_ino;
+	}
+
+	err = fuse_vfs_lookup(parent, name, &inode, &mode, &nlink, &uid, &gid,
+			      &rdev, &size, &blksize, &blocks, &atime, &mtime,
+			      &ctime);
+	if(err){
+		fuse_reply_err(req, -err);
+		return;
+	}
+
+	fill_fuse_entry(&e, inode, mode, nlink, uid, gid, rdev, size, blksize,
+			blocks, atime, mtime, ctime);
+	fuse_reply_entry(req, &e);
+}
+
+static void uml_fuse_forget(fuse_req_t req, fuse_ino_t ino,
+			    unsigned long nlookup)
+{
+	fuse_vfs_forget(ino, nlookup);
+	fuse_reply_none(req);
+}
+
+static void uml_fuse_getattr(fuse_req_t req, fuse_ino_t ino,
+			     struct fuse_file_info *fi)
+{
+	struct stat stbuf;
+	unsigned long long inode, size, blocks, atime, ctime, mtime;
+	unsigned long mode, nlink, uid, gid, rdev, blksize;
+	int err;
+
+	if(ino == FUSE_ROOT_ID){
+		struct fuse_data *data = fuse_req_userdata(req);
+
+		ino = data->root_ino;
+	}
+
+	memset(&stbuf, 0, sizeof(stbuf));
+	err = fuse_vfs_stat(ino, &inode, &mode, &nlink, &uid, &gid, &rdev,
+			    &size, &blksize, &blocks, &atime, &mtime, &ctime);
+	if(err)
+		fuse_reply_err(req, -err);
+	else {
+		fill_stat(&stbuf, inode, mode, nlink, uid, gid, rdev, size,
+			  blksize, blocks, atime, mtime, ctime);
+		fuse_reply_attr(req, &stbuf, 1.0);
+	}
+}
+
+void uml_fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+		      int to_set, struct fuse_file_info *fi)
+{
+	unsigned long long inode, size, blocks, atime, ctime, mtime;
+	unsigned long mode, nlink, uid, gid, rdev, blksize;
+	int mask, err;
+
+	if(ino == FUSE_ROOT_ID){
+		struct fuse_data *data = fuse_req_userdata(req);
+
+		ino = data->root_ino;
+	}
+
+	log_info("uml_fuse_setattr - mask = 0x%x ino = %ld\n", to_set, ino);
+
+	mask = 0;
+	if(to_set & FUSE_SET_ATTR_MODE){
+		mask |= UML_FUSE_MODE;
+		mode = attr->st_mode;
+	}
+	if(to_set & FUSE_SET_ATTR_UID){
+		mask |= UML_FUSE_UID;
+		uid = attr->st_uid;
+	}
+	if(to_set & FUSE_SET_ATTR_GID){
+		mask |= UML_FUSE_GID;
+		gid = attr->st_gid;
+	}
+	if(to_set & FUSE_SET_ATTR_SIZE){
+		mask |= UML_FUSE_SIZE;
+		size = attr->st_size;
+	}
+	if(to_set & FUSE_SET_ATTR_ATIME){
+		mask |= UML_FUSE_ATIME;
+		atime = attr->st_atim.tv_sec * BILLION + attr->st_atim.tv_nsec;
+	}
+	if(to_set & FUSE_SET_ATTR_MTIME){
+		mask |= UML_FUSE_MTIME;
+		mtime = attr->st_mtim.tv_sec * BILLION + attr->st_mtim.tv_nsec;
+	}
+
+	err = fuse_vfs_setattr(ino, mask, &inode, &mode, &nlink, &uid, &gid,
+			       &rdev, &size, &blksize, &blocks, &atime, &mtime,
+			       &ctime);
+	log_info("uml_fuse_setattr returns %d\n", err);
+	if(err)
+		fuse_reply_err(req, -err);
+	else {
+		fill_stat(attr, inode, mode, nlink, uid, gid, rdev, size,
+			  blksize, blocks, atime, mtime, ctime);
+		fuse_reply_attr(req, attr, 1.0);
+	}
+}
+
+static void uml_fuse_readlink(fuse_req_t req, fuse_ino_t ino)
+{
+	char buf[64], *ptr = buf;
+	int len = sizeof(buf), err;
+
+again:
+	err = fuse_vfs_readlink(ino, ptr, len);
+	if(err == len){
+		if(ptr != buf)
+			kfree(ptr);
+
+		len *= 2;
+		ptr = um_kmalloc(len);
+		if(ptr != NULL)
+			goto again;
+
+		err = -ENOMEM;
+	}
+
+	if(err < 0)
+		fuse_reply_err(req, -err);
+	else fuse_reply_readlink(req, ptr);
+
+	if((ptr != NULL) && (ptr != buf))
+		kfree(ptr);
+}
+
+void uml_fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
+		    mode_t mode, dev_t rdev)
+{
+}
+
+void uml_fuse_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
+		    mode_t mode)
+{
+}
+
+void uml_fuse_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+}
+
+void uml_fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+}
+
+void uml_fuse_symlink(fuse_req_t req, const char *link, fuse_ino_t parent,
+		      const char *name)
+{
+}
+
+void uml_fuse_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
+                    fuse_ino_t newparent, const char *newname)
+{
+}
+
+void uml_fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+		   const char *newname)
+{
+}
+
+#define O_ACCMODE          0003
+#define O_RDONLY             00
+#define O_WRONLY             01
+#define O_RDWR               02
+
+static const int flag_bits[] = { [10] = OPEN_APPEND,
+				 [11] = OPEN_NONBLOCK,
+				 [12] = OPEN_SYNC,
+				 [13] = OPEN_FASYNC,
+				 [14] = OPEN_DIRECT,
+				 [16] = OPEN_DIRECTORY,
+				 [17] = OPEN_NOFOLLOW,
+				 [18] = OPEN_NOATIME };
+
+static int convert_mode(int flags)
+{
+	int i, mode = 0;
+
+	for(i = 0; i < sizeof(mode) * 8; i++){
+		if(flags & (1 << i))
+			mode |= (1 << flag_bits[i]);
+	}
+	switch(flags & O_ACCMODE){
+	case O_RDONLY:
+		mode |= (1 << OPEN_READ); break;
+	case O_WRONLY:
+		mode |= (1 << OPEN_WRITE); break;
+	case O_RDWR:
+		mode |= (1 << OPEN_READ) | (1 << OPEN_WRITE); break;
+	default:
+		printk("uml_fuse_open - bad mode %d\n", mode); break;
+	}
+
+	return mode;
+}
+
+void uml_fuse_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
+{
+	void *file;
+	unsigned long err;
+
+	err = fuse_vfs_open(ino, convert_mode(fi->flags), &file);
+	if(err)
+		fuse_reply_err(req, -err);
+	else {
+		fi->fh = (unsigned long) file;
+		fuse_reply_open(req, fi);
+	}
+}
+
+static void uml_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name,
+			    mode_t flags, struct fuse_file_info *fi)
+{
+	struct fuse_entry_param e;
+	unsigned long long inode, size, blocks, atime, ctime, mtime;
+	unsigned long mode, nlink, uid, gid, rdev, blksize;
+	void *file;
+	int err = 0;
+
+	err = fuse_vfs_create(parent, name, convert_mode(fi->flags), flags,
+			      &file, &inode, &mode, &nlink, &uid, &gid, &rdev,
+			      &size, &blksize, &blocks, &atime, &mtime, &ctime);
+	if(err){
+		fuse_reply_err(req, -err);
+		return;
+	}
+
+	fi->fh = (unsigned long) file;
+	fill_fuse_entry(&e, inode, mode, nlink, uid, gid, rdev, size, blksize,
+			blocks, atime, mtime, ctime);
+	fuse_reply_create(req, &e, fi);
+}
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
+                             off_t off, size_t maxsize)
+{
+	if (off < bufsize)
+		return fuse_reply_buf(req, buf + off,
+				      min(bufsize - off, maxsize));
+	else
+		return fuse_reply_buf(req, NULL, 0);
+}
+
+void uml_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+		   struct fuse_file_info *fi)
+{
+	void *buf;
+	int n;
+
+	buf = um_kmalloc(size);
+	if(buf == NULL){
+		fuse_reply_err(req, ENOMEM);
+		return;
+	}
+
+	n = fuse_vfs_read((void *) (unsigned long) fi->fh, off, buf, size);
+	if(n < 0)
+		fuse_reply_err(req, -n);
+	else fuse_reply_buf(req, buf, n);
+
+	kfree(buf);
+}
+
+void uml_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
+		    size_t size, off_t off, struct fuse_file_info *fi)
+{
+	int n;
+
+	n = fuse_vfs_write((void *) (unsigned long) fi->fh, off, buf, size);
+	if(n < 0)
+		fuse_reply_err(req, -n);
+	else fuse_reply_write(req, n);
+}
+
+void uml_fuse_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
+{
+}
+
+void uml_fuse_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
+		    struct fuse_file_info *fi)
+{
+}
+
+static void uml_fuse_opendir(fuse_req_t req, fuse_ino_t ino,
+			     struct fuse_file_info *fi)
+{
+	void *file;
+	int err;
+
+	if(ino == FUSE_ROOT_ID){
+		struct fuse_data *data = fuse_req_userdata(req);
+
+		ino = data->root_ino;
+	}
+
+	err = fuse_vfs_opendir(ino, &file);
+	if(err)
+		fuse_reply_err(req, -err);
+	else {
+		fi->fh = (uint64_t) (unsigned long) file;
+		fuse_reply_open(req, fi);
+	}
+}
+
+
+static inline int max(int a, int b)
+{
+	return a < b ? b : a;
+}
+
+int dirbuf_add(struct dirbuf *b, char *name, int namelen,
+	       unsigned long long ino, unsigned long mode)
+{
+	struct stat stbuf;
+	int new_size = b->size + fuse_dirent_size(namelen);
+	char save;
+
+	if(new_size > b->max)
+		return -EINVAL;
+
+	if(new_size > b->allocated){
+		int need = max(new_size, b->allocated + UM_KERN_PAGE_SIZE);
+		void *new = um_kmalloc(need);
+
+		if(new == NULL)
+			return -ENOMEM;
+
+		memcpy(new, b->p, b->size);
+		kfree(b->p);
+		b->p = new;
+		b->allocated = need;
+	}
+
+	stbuf.st_mode = mode;
+	stbuf.st_ino = ino;
+
+	/* fuse_add_dirent wants a NULL-terminated string, which name might
+	 * not be.  So, we temporarily NULL-terminate it and restore the old
+	 * character in case it was precious.
+	 */
+	save = name[namelen];
+	name[namelen] = '\0';
+	fuse_add_dirent(b->p + b->size, name, &stbuf, b->size);
+	name[namelen] = save;
+
+	b->size = new_size;
+
+	return 0;
+}
+
+static void uml_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+			     off_t off, struct fuse_file_info *fi)
+{
+	struct dirbuf b;
+	int err;
+
+	(void) fi;
+
+	b.allocated = UM_KERN_PAGE_SIZE;
+	err = -ENOMEM;
+	b.p = um_kmalloc(b.allocated);
+	if(b.p == NULL){
+		fuse_reply_err(req, -err);
+		return;
+	}
+
+	b.max = size;
+	b.size = 0;
+
+	fuse_vfs_readdir((void *) (unsigned long) fi->fh, &b);
+
+	reply_buf_limited(req, b.p, b.size, off, size);
+	kfree(b.p);
+}
+
+/* Used as both release and releasedir methods, since all they ultimately
+ * need to do is an fput.
+ */
+static void uml_fuse_release(fuse_req_t req, fuse_ino_t ino,
+			     struct fuse_file_info *fi)
+{
+	fuse_vfs_release((void *) (unsigned long) fi->fh);
+	fuse_reply_err(req, 0);
+}
+
+void uml_fuse_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
+		       struct fuse_file_info *fi)
+{
+}
+
+void uml_fuse_statfs(fuse_req_t req)
+{
+}
+
+void uml_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+		       const char *value, size_t size, int flags)
+{
+}
+
+void uml_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+		       size_t size)
+{
+}
+
+void uml_fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
+{
+}
+
+void uml_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
+{
+}
+
+void uml_fuse_access(fuse_req_t req, fuse_ino_t ino, int mask)
+{
+}
+
+const struct fuse_lowlevel_ops uml_fuse_ops = {
+	.init		= uml_fuse_init,
+	.destroy	= uml_fuse_destroy,
+	.lookup		= uml_fuse_lookup,
+	.forget		= uml_fuse_forget,
+	.getattr	= uml_fuse_getattr,
+	.setattr	= uml_fuse_setattr,
+	.readlink	= uml_fuse_readlink,
+	.release	= uml_fuse_release,
+	.opendir	= uml_fuse_opendir,
+	.readdir	= uml_fuse_readdir,
+	.releasedir	= uml_fuse_release,
+	.open		= uml_fuse_open,
+	.read		= uml_fuse_read,
+	.write		= uml_fuse_write,
+	.release	= uml_fuse_release,
+	.create		= uml_fuse_create,
+#if 0
+	.init		= uml_fuse_init,
+	.destroy	= uml_fuse_destroy,
+	.lookup		= uml_fuse_lookup,
+	.forget		= uml_fuse_forget,
+	.getattr	= uml_fuse_getattr,
+	.setattr	= uml_fuse_setattr,
+	.readlink	= uml_fuse_readlink,
+	.mknod		= uml_fuse_mknod,
+	.mkdir		= uml_fuse_mkdir,
+	.unlink		= uml_fuse_unlink,
+	.rmdir		= uml_fuse_rmdir,
+	.symlink	= uml_fuse_symlink,
+	.rename		= uml_fuse_rename,
+	.link		= uml_fuse_link,
+	.open		= uml_fuse_open,
+	.read		= uml_fuse_read,
+	.write		= uml_fuse_write,
+	.release	= uml_fuse_release,
+	.flush		= uml_fuse_flush,
+	.fsync		= uml_fuse_fsync,
+	.opendir	= uml_fuse_opendir,
+	.readdir	= uml_fuse_readdir,
+	.releasedir	= uml_fuse_releasedir,
+	.fsyncdir	= uml_fuse_fsyncdir,
+	.statfs		= uml_fuse_statfs,
+	.setxattr	= uml_fuse_setxattr,
+	.getxattr	= uml_fuse_getxattr,
+	.listxattr	= uml_fuse_listxattr,
+	.removexattr	= uml_fuse_removexattr,
+	.access		= uml_fuse_access,
+	.create		= uml_fuse_create,
+#endif
+};
+
+/* Percpu, so no locking needed as long as worker threads are bound to CPUs */
+static void *req_bufs[UM_NR_CPUS];
+
+/* This happens in process context, in a work_proc */
+int uml_fuse_request(void *se)
+{
+	struct fuse_session *session = se;
+	struct fuse_chan *ch = fuse_session_next_chan(session, NULL);
+	int res, fd = fuse_chan_fd(ch), c = cpu();
+	void *req_buf = req_bufs[c];
+
+	while(1){
+		if(fuse_session_exited(session))
+			return fd;
+
+		res = fuse_chan_receive(ch, req_buf, fuse_chan_bufsize(ch));
+		if(res == -EAGAIN)
+			break;
+
+		if(res == -1){
+			fuse_session_reset(session);
+			return -1;
+		}
+
+		if(res == 0)
+			break;
+
+		fuse_session_process(session, req_buf, res, ch);
+	}
+
+	return fd;
+}
+
+void *uml_fuse_new(int fd)
+{
+	char *argv = NULL;
+	int size;
+	struct fuse_args args = FUSE_ARGS_INIT(0, &argv);
+        struct fuse_session *se;
+	struct fuse_chan *ch;
+	struct fuse_data *data;
+
+	data = um_kmalloc(sizeof(*data));
+	if(data == NULL)
+		goto out;
+
+        se = fuse_lowlevel_new(&args, &uml_fuse_ops, sizeof(uml_fuse_ops),
+                               data);
+        if (se == NULL)
+		goto out;
+
+	ch = fuse_kern_chan_new(fd);
+	if (ch == NULL)
+		goto out_session;
+
+	fuse_session_add_chan(se, ch);
+
+	size = fuse_chan_bufsize(ch);
+	req_buf = um_vmalloc(fuse_chan_bufsize(ch));
+	if(req_buf == NULL){
+		printk("uml_fuse_new failed to allocate buf %d bytes\n",
+			 fuse_chan_bufsize(ch));
+		goto out_session;
+	}
+	/* The purpose of this is really to fault in req_buf, since it's
+	 * vmalloced, and not populated until it's referenced.  If this isn't
+	 * done, when FUSE tries to read into it, it will get -EFAULT from the
+	 * host.
+	 */
+	memset(req_buf, 0, size);
+
+	return se;
+
+out_session:
+	fuse_session_destroy(se);
+out:
+	close(fd);
+	return NULL;
+}
+
+void uml_fuse_free(void *session)
+{
+	fuse_session_destroy(session);
+}
Index: linux-2.6.17/arch/um/drivers/mconsole_kern.c
===================================================================
--- linux-2.6.17.orig/arch/um/drivers/mconsole_kern.c	2007-10-24 10:04:50.000000000 -0400
+++ linux-2.6.17/arch/um/drivers/mconsole_kern.c	2007-11-19 11:33:46.000000000 -0500
@@ -773,6 +773,100 @@ void mconsole_stack(struct mc_request *r
 	with_console(req, stack_proc, to);
 }
 
+#ifdef CONFIG_FUSE_SERVER
+#include "uml-fuse.h"
+
+static struct list_head fuse_requests = LIST_HEAD_INIT(fuse_requests);
+
+struct fuse_dev {
+	struct list_head list;
+	void *session;
+};
+
+static void fuse_work_proc(void *unused)
+{
+	struct fuse_dev *dev;
+	unsigned long flags;
+	int fd;
+
+	while(!list_empty(&fuse_requests)){
+		local_save_flags(flags);
+		dev = list_entry(fuse_requests.next, struct fuse_dev, list);
+		list_del_init(&dev->list);
+		local_irq_restore(flags);
+		fd = uml_fuse_request(dev->session);
+		if(fd >= 0)
+			reactivate_fd(fd, FUSE_IRQ);
+	}
+}
+
+static DECLARE_WORK(fuse_work, fuse_work_proc, NULL);
+
+static irqreturn_t fuse_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct fuse_dev *dev = dev_id;
+
+	if(list_empty(&dev->list)){
+		list_add(&dev->list, &fuse_requests);
+		schedule_work(&fuse_work);
+	}
+
+	return IRQ_HANDLED;
+}
+
+void mconsole_fuse(struct mc_request *req)
+{
+	int fd, err;
+	char *error;
+	struct fuse_dev *dev;
+
+	fd = req->ancillary;
+	if(req->anc_len != sizeof(req->ancillary)){
+		error = "Failed to receive FUSE file descriptor";
+		goto err;
+	}
+
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if(dev == NULL){
+		error = "Failed to allocate device id";
+		goto err_close;
+	}
+
+	INIT_LIST_HEAD(&dev->list);
+	dev->session = uml_fuse_new(fd);
+	if(dev->session == NULL){
+		error = "uml_fuse_init failed";
+		goto err_free;
+	}
+
+	err = um_request_irq(FUSE_IRQ, fd, IRQ_READ, fuse_intr,
+			     SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
+			     "umlfs", dev);
+	if(err){
+		printk("Failed to allocate FUSE irq, err = %d\n", err);
+		error = "Failed to allocate FUSE irq";
+		goto err_session;
+	}
+
+	mconsole_reply(req, "", 0, 0);
+	return;
+
+err_session:
+	uml_fuse_free(dev->session);
+err_free:
+	kfree(dev);
+err_close:
+	os_close_file(fd);
+err:
+	mconsole_reply(req, error, 1, 0);
+}
+#else
+void mconsole_fuse(struct mc_request *req)
+{
+	mconsole_reply(req, "CONFIG_FUSE_SERVER is not enabled", 1, 0);
+}
+#endif
+
 /*
  * Changed by mconsole_setup, which is __setup, and called before SMP is
  * active.
Index: linux-2.6.17/arch/um/drivers/mconsole_user.c
===================================================================
--- linux-2.6.17.orig/arch/um/drivers/mconsole_user.c	2007-10-24 10:04:50.000000000 -0400
+++ linux-2.6.17/arch/um/drivers/mconsole_user.c	2007-11-19 11:36:23.000000000 -0500
@@ -34,6 +34,7 @@ static struct mconsole_command commands[
 	{ "log", mconsole_log, MCONSOLE_INTR },
 	{ "proc", mconsole_proc, MCONSOLE_PROC },
 	{ "stack", mconsole_stack, MCONSOLE_INTR },
+	{ "umlfs", mconsole_fuse, MCONSOLE_PROC },
 };
 
 /* Initialized in mconsole_init, which is an initcall */
@@ -80,17 +81,43 @@ static struct mconsole_command *mconsole
 
 int mconsole_get_request(int fd, struct mc_request *req)
 {
+	int data;
+	char buf[CMSG_SPACE(sizeof(data))];
+	struct iovec iov;
+	struct msghdr hdr;
+	struct cmsghdr *cmsg;
 	int len;
 
 	req->originlen = sizeof(req->origin);
-	req->len = recvfrom(fd, &req->request, sizeof(req->request),
-			    MSG_DONTWAIT, (struct sockaddr *) req->origin,
-			    &req->originlen);
-	if (req->len < 0)
+	iov = ((struct iovec) { .iov_base	= &req->request,
+			        .iov_len	= sizeof(req->request)
+			       });
+	hdr = ((struct msghdr) { .msg_name		=
+			             (struct sockaddr *) req->origin,
+			         .msg_namelen		= req->originlen,
+			         .msg_iov		= &iov,
+			         .msg_iovlen		= 1,
+			         .msg_control		= buf,
+			         .msg_controllen	= sizeof(buf),
+			         .msg_flags		= 0 });
+	req->len = recvmsg(fd, &hdr, 0);
+	req->originlen = hdr.msg_namelen;
+	if (req->len < 0) {
+		if(errno != EAGAIN)
+			printk("mconsole_get_request - rcvmsg failed, "
+			       "errno = %d\n", errno);
 		return 0;
+	}
 
 	req->originating_fd = fd;
 
+	req->anc_len = 0;
+	cmsg = CMSG_FIRSTHDR(&hdr);
+	if (cmsg != NULL) {
+		req->ancillary = *((int *) CMSG_DATA(cmsg));
+		req->anc_len = sizeof(req->ancillary);
+	}
+
 	if (req->request.magic != MCONSOLE_MAGIC) {
 		/* Unversioned request */
 		len = MIN(sizeof(req->request.data) - 1,
Index: linux-2.6.17/arch/um/drivers/port_kern.c
===================================================================
--- linux-2.6.17.orig/arch/um/drivers/port_kern.c	2007-10-24 10:04:50.000000000 -0400
+++ linux-2.6.17/arch/um/drivers/port_kern.c	2007-11-19 11:37:47.000000000 -0500
@@ -44,7 +44,8 @@ static irqreturn_t pipe_interrupt(int ir
 	struct connection *conn = data;
 	int fd;
 
-	fd = os_rcv_fd(conn->socket[0], &conn->helper_pid);
+	fd = os_rcv_fd(conn->socket[0], &conn->helper_pid,
+		       sizeof(conn->helper_pid));
 	if (fd < 0) {
 		if (fd == -EAGAIN)
 			return IRQ_NONE;
Index: linux-2.6.17/arch/um/drivers/xterm_kern.c
===================================================================
--- linux-2.6.17.orig/arch/um/drivers/xterm_kern.c	2007-10-17 12:11:50.000000000 -0400
+++ linux-2.6.17/arch/um/drivers/xterm_kern.c	2007-11-19 11:32:36.000000000 -0500
@@ -22,7 +22,7 @@ static irqreturn_t xterm_interrupt(int i
 	struct xterm_wait *xterm = data;
 	int fd;
 
-	fd = os_rcv_fd(xterm->fd, &xterm->pid);
+	fd = os_rcv_fd(xterm->fd, &xterm->pid, sizeof(xterm->pid));
 	if (fd == -EAGAIN)
 		return IRQ_NONE;
 
Index: linux-2.6.17/arch/um/include/mconsole.h
===================================================================
--- linux-2.6.17.orig/arch/um/include/mconsole.h	2007-10-24 10:04:50.000000000 -0400
+++ linux-2.6.17/arch/um/include/mconsole.h	2007-11-19 11:38:13.000000000 -0500
@@ -64,6 +64,8 @@ struct mc_request
 	struct mconsole_request request;
 	struct mconsole_command *cmd;
 	struct uml_pt_regs regs;
+	int ancillary;
+	int anc_len;
 };
 
 extern char mconsole_socket_name[];
@@ -87,6 +89,7 @@ extern void mconsole_go(struct mc_reques
 extern void mconsole_log(struct mc_request *req);
 extern void mconsole_proc(struct mc_request *req);
 extern void mconsole_stack(struct mc_request *req);
+extern void mconsole_fuse(struct mc_request *req);
 
 extern int mconsole_get_request(int fd, struct mc_request *req);
 extern int mconsole_notify(char *sock_name, int type, const void *data,
Index: linux-2.6.17/arch/um/include/os.h
===================================================================
--- linux-2.6.17.orig/arch/um/include/os.h	2007-11-19 11:31:59.000000000 -0500
+++ linux-2.6.17/arch/um/include/os.h	2007-11-19 11:32:36.000000000 -0500
@@ -185,7 +185,7 @@ extern int os_remove_dir(const char *dir
 extern int os_make_dev(const char *name, int mode, int major, int minor);
 extern int os_shutdown_socket(int fd, int r, int w);
 extern void os_close_file(int fd);
-extern int os_rcv_fd(int fd, int *helper_pid_out);
+extern int os_rcv_fd(int fd, void *msg, int msg_len);
 extern int create_unix_socket(char *file, int len, int close_on_exec);
 extern int os_connect_socket(char *name);
 extern int os_file_type(char *file);
Index: linux-2.6.17/arch/um/os-Linux/file.c
===================================================================
--- linux-2.6.17.orig/arch/um/os-Linux/file.c	2007-11-19 11:31:59.000000000 -0500
+++ linux-2.6.17/arch/um/os-Linux/file.c	2007-11-19 11:39:26.000000000 -0500
@@ -670,31 +670,31 @@ int os_shutdown_socket(int fd, int r, in
 	return 0;
 }
 
-int os_rcv_fd(int fd, int *helper_pid_out)
+int os_rcv_fd(int fd, void *msg, int msg_len)
 {
 	int new, n;
 	char buf[CMSG_SPACE(sizeof(new))];
-	struct msghdr msg;
+	struct msghdr hdr;
 	struct cmsghdr *cmsg;
 	struct iovec iov;
 
-	msg.msg_name = NULL;
-	msg.msg_namelen = 0;
-	iov = ((struct iovec) { .iov_base  = helper_pid_out,
-				.iov_len   = sizeof(*helper_pid_out) });
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-	msg.msg_control = buf;
-	msg.msg_controllen = sizeof(buf);
-	msg.msg_flags = 0;
+	hdr.msg_name = NULL;
+	hdr.msg_namelen = 0;
+	iov = ((struct iovec) { .iov_base  = msg,
+				.iov_len   = msg_len });
+	hdr.msg_iov = &iov;
+	hdr.msg_iovlen = 1;
+	hdr.msg_control = buf;
+	hdr.msg_controllen = sizeof(buf);
+	hdr.msg_flags = 0;
 
-	n = recvmsg(fd, &msg, 0);
+	n = recvmsg(fd, &hdr, 0);
 	if(n < 0)
 		return -errno;
 	else if(n != iov.iov_len)
-		*helper_pid_out = -1;
+		return -EINVAL;
 
-	cmsg = CMSG_FIRSTHDR(&msg);
+	cmsg = CMSG_FIRSTHDR(&hdr);
 	if(cmsg == NULL){
 		printk("rcv_fd didn't receive anything, error = %d\n", errno);
 		return -1;
Index: linux-2.6.17/include/asm-um/irq.h
===================================================================
--- linux-2.6.17.orig/include/asm-um/irq.h	2007-11-19 11:22:17.000000000 -0500
+++ linux-2.6.17/include/asm-um/irq.h	2007-11-19 11:32:36.000000000 -0500
@@ -16,7 +16,8 @@
 #define XTERM_IRQ 		12
 #define AIO_IRQ			13
 #define X11_IRQ 		14
-#define LAST_IRQ X11_IRQ
+#define FUSE_IRQ		15
+#define LAST_IRQ FUSE_IRQ
 
 #define NR_IRQS (LAST_IRQ + 1)
 
Index: linux-2.6.17/arch/um/include/uml-fuse.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/arch/um/include/uml-fuse.h	2007-11-19 11:32:36.000000000 -0500
@@ -0,0 +1,87 @@
+#ifndef __FUSE_H___
+#define __FUSE_H___
+
+extern void *uml_fuse_new(int fd);
+extern void uml_fuse_free(void *session);
+extern int uml_fuse_request(void *se);
+
+extern int fuse_vfs_stat(unsigned long long file, unsigned long long *inode,
+			 unsigned long *mode, unsigned long *nlink,
+			 unsigned long *uid, unsigned long *gid,
+			 unsigned long *rdev, unsigned long long *size,
+			 unsigned long *blksize, unsigned long long *blocks,
+			 unsigned long long *atime, unsigned long long *mtime,
+			 unsigned long long *ctime);
+extern int fuse_vfs_lookup(unsigned long long parent, const char *file,
+			   unsigned long long *inode, unsigned long *mode,
+			   unsigned long *nlink, unsigned long *uid,
+			   unsigned long *gid, unsigned long *rdev,
+			   unsigned long long *size, unsigned long *blksize,
+			   unsigned long long *blocks,
+			   unsigned long long *atime,
+			   unsigned long long *mtime,
+			   unsigned long long *ctime);
+extern int fuse_vfs_opendir(unsigned long long inode, void **file_out);
+extern int fuse_vfs_readdir(void *f, void *data);
+extern void fuse_vfs_release(void *f);
+extern unsigned long long fuse_vfs_lookup_root(void);
+extern void fuse_vfs_forget(unsigned long long inode, int nforgets);
+extern int fuse_vfs_readlink(unsigned long long ino, char *buf, int bufsize);
+
+#define OPEN_READ 0
+#define OPEN_WRITE 1
+#define OPEN_APPEND 2
+#define OPEN_NONBLOCK 3
+#define OPEN_SYNC 4
+#define OPEN_FASYNC 5
+#define OPEN_DIRECT 6
+#define OPEN_LARGEFILE 7
+#define OPEN_DIRECTORY 8
+#define OPEN_NOFOLLOW 9
+#define OPEN_NOATIME 10
+
+extern unsigned long fuse_vfs_open(unsigned long long ino,
+				   unsigned long uflags, void **file_out);
+extern int fuse_vfs_read(void *f, unsigned long long off, void *buf,
+			 unsigned long size);
+extern int fuse_vfs_write(void *f, unsigned long long off, const void *buf,
+			  unsigned long size);
+extern int fuse_vfs_create(unsigned long long parent, const char *name,
+			   int flags, int perms, void **file_out,
+			   unsigned long long *inode, unsigned long *mode,
+			   unsigned long *nlink, unsigned long *uid,
+			   unsigned long *gid, unsigned long *rdev,
+			   unsigned long long *size, unsigned long *blksize,
+			   unsigned long long *blocks,
+			   unsigned long long *atime,
+			   unsigned long long *mtime,
+			   unsigned long long *ctime);
+
+#define UML_FUSE_MODE (1 << 0)
+#define UML_FUSE_UID (1 << 1)
+#define UML_FUSE_GID (1 << 2)
+#define UML_FUSE_SIZE (1 << 3)
+#define UML_FUSE_ATIME (1 << 4)
+#define UML_FUSE_MTIME (1 << 5)
+
+extern int fuse_vfs_setattr(unsigned long long file, int mask,
+			    unsigned long long *inode, unsigned long *mode,
+			    unsigned long *nlink, unsigned long *uid,
+			    unsigned long *gid, unsigned long *rdev,
+			    unsigned long long *size, unsigned long *blksize,
+			    unsigned long long *blocks,
+			    unsigned long long *atime,
+			    unsigned long long *mtime,
+			    unsigned long long *ctime);
+
+struct dirbuf {
+	char *p;
+	size_t size;
+	size_t max;
+	size_t allocated;
+};
+
+extern int dirbuf_add(struct dirbuf *b, char *name, int namelen,
+		      unsigned long long ino, unsigned long mode);
+
+#endif
Index: linux-2.6.17/arch/um/kernel/Makefile
===================================================================
--- linux-2.6.17.orig/arch/um/kernel/Makefile	2007-11-19 10:52:01.000000000 -0500
+++ linux-2.6.17/arch/um/kernel/Makefile	2007-11-19 11:32:36.000000000 -0500
@@ -14,6 +14,7 @@ obj-y = aio.o config.o exec.o exitcode.o
 obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
 obj-$(CONFIG_GPROF)	+= gprof_syms.o
 obj-$(CONFIG_GCOV)	+= gmon_syms.o
+obj-$(CONFIG_FUSE_SERVER) += fuse.o
 
 USER_OBJS := config.o
 
Index: linux-2.6.17/arch/um/kernel/fuse.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/arch/um/kernel/fuse.c	2007-11-19 11:32:36.000000000 -0500
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/stat.h"
+#include "linux/fs.h"
+#include "linux/namei.h"
+#include "linux/file.h"
+#include "linux/mount.h"
+#include "linux/rbtree.h"
+#include "linux/security.h"
+#include "linux/spinlock.h"
+#include "uml-fuse.h"
+#include "os.h"
+
+static int do_stat(struct dentry *dentry, struct vfsmount *mnt,
+		   unsigned long long *inode, unsigned long *mode,
+		   unsigned long *nlink, unsigned long *uid, unsigned long *gid,
+		   unsigned long *rdev, unsigned long long *size,
+		   unsigned long *blksize, unsigned long long *blocks,
+		   unsigned long long *atime, unsigned long long *mtime,
+		   unsigned long long *ctime)
+{
+	struct kstat stat;
+	int error;
+
+	error = vfs_getattr(mnt, dentry, &stat);
+	if(error)
+		goto err;
+
+	*inode = stat.ino;
+	*mode = stat.mode;
+	*nlink = stat.nlink;
+	*uid = stat.uid;
+	*gid = stat.gid;
+	*rdev = stat.rdev;
+	*size = stat.size;
+	*blksize = stat.blksize;
+	*blocks = stat.blocks;
+	*atime = stat.atime.tv_sec;
+	*ctime = stat.ctime.tv_sec;
+	*mtime = stat.mtime.tv_sec;
+err:
+	return error;
+}
+
+struct dentry_tree {
+	struct rb_node rb;
+	atomic_t count;
+	struct vfsmount *mnt;
+	struct dentry *dentry;
+};
+
+/* Protects the rb_tree structure */
+static SPINLOCK(dentry_spinlock);
+static struct rb_root dentry_root = RB_ROOT;
+
+static struct dentry_tree *rb_add_dentry(struct dentry_tree *new)
+{
+	struct rb_node **node = &dentry_root.rb_node;
+	struct rb_node * parent = NULL;
+	struct dentry_tree *tree;
+	unsigned long long inode = new->dentry->d_inode->i_ino;
+
+	spin_lock(&dentry_spinlock);
+
+	node = &dentry_root.rb_node;
+	while(*node != NULL){
+		unsigned long long this;
+
+		parent = *node;
+		tree = rb_entry(*node, struct dentry_tree, rb);
+		this = tree->dentry->d_inode->i_ino;
+		if(this == inode)
+			goto out;
+		else if(this < inode)
+			node = &(*node)->rb_left;
+		else node = &(*node)->rb_right;
+	}
+
+	rb_link_node(&new->rb, parent, node);
+	rb_insert_color(&new->rb, &dentry_root);
+	tree = NULL;
+out:
+	spin_unlock(&dentry_spinlock);
+
+	return tree;
+}
+
+static struct dentry_tree *rb_find_dentry(unsigned long long inode)
+{
+	struct rb_node *node = dentry_root.rb_node;
+	struct dentry_tree *tree;
+
+	spin_lock(&dentry_spinlock);
+
+	node = &dentry_root.rb_node;
+	while(node != NULL){
+		unsigned long long this;
+
+		tree = rb_entry(node, struct dentry_tree, rb);
+		this = tree->dentry->d_inode->i_ino;
+		if(this == inode)
+			goto out;
+		else if(this < inode)
+			node = node->rb_left;
+		else node = node->rb_right;
+	}
+	tree = NULL;
+out:
+	spin_unlock(&dentry_spinlock);
+
+	return tree;
+}
+
+static struct dentry_tree *remember_dentry(struct dentry *dentry,
+					   struct vfsmount *mnt)
+{
+	struct dentry_tree *ret, *rb = kmalloc(sizeof(*rb), GFP_KERNEL);
+
+	if(rb == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	*rb = ((struct dentry_tree)
+		{ .count	= ATOMIC_INIT(1),
+		  .mnt		= mntget(mnt),
+		  .dentry	= dget(dentry) });
+
+	ret = rb_add_dentry(rb);
+	if(ret != NULL){
+		/*
+		 * Another CPU added the entry, so delete what we just
+		 * allocated because it's not being used.
+		 */
+		kfree(rb);
+	}
+	return ret;
+}
+
+static struct dentry_tree *save_dentry(struct dentry *dentry,
+				       struct vfsmount *mnt)
+{
+	struct dentry_tree *rb = rb_find_dentry(dentry->d_inode->i_ino);
+
+	if(rb != NULL)
+		return rb;
+
+	return remember_dentry(dentry, mnt);
+}
+
+unsigned long long fuse_vfs_lookup_root(void)
+{
+	if(IS_ERR(remember_dentry(current->fs->root, current->fs->rootmnt)))
+		printk("Failed to look up root\n");
+
+	return(current->fs->root->d_inode->i_ino);
+}
+
+static struct dentry *do_vfs_lookup(unsigned long long parent, const char *file,
+				    struct vfsmount **mnt_out)
+{
+	struct dentry_tree *dir, *rb;
+	struct vfsmount *mnt;
+	struct dentry *dentry;
+	struct nameidata nd;
+	int error = -ENOENT;
+
+	dir = rb_find_dentry(parent);
+	if(dir == NULL)
+		goto err;
+
+	nd.mnt = mntget(dir->mnt);
+	nd.dentry = dget(dir->dentry);
+	nd.last_type = 0;
+	nd.flags = 0;
+	nd.depth = 0;
+
+	error = path_walk(file, &nd);
+	if(error)
+		goto err;
+
+	dentry = nd.dentry;
+	mnt = nd.mnt;
+
+	rb = save_dentry(dentry, mnt);
+	if(IS_ERR(rb)){
+		error = PTR_ERR(rb);
+		goto err_nd;
+	}
+	atomic_inc(&rb->count);
+
+	path_release(&nd);
+
+	*mnt_out = mnt;
+	return dentry;
+
+err_nd:
+	path_release(&nd);
+err:
+	return ERR_PTR(error);
+}
+
+int fuse_vfs_lookup(unsigned long long parent, const char *file,
+		    unsigned long long *inode, unsigned long *mode,
+		    unsigned long *nlink, unsigned long *uid,
+		    unsigned long *gid, unsigned long *rdev,
+		    unsigned long long *size, unsigned long *blksize,
+		    unsigned long long *blocks, unsigned long long *atime,
+		    unsigned long long *mtime, unsigned long long *ctime)
+{
+	struct dentry *dentry;
+	struct vfsmount *mnt;
+	int error;
+
+	dentry = do_vfs_lookup(parent, file, &mnt);
+
+	if(IS_ERR(dentry)){
+		error = PTR_ERR(dentry);
+		goto err;
+	}
+
+	error = do_stat(dentry, mnt, inode, mode, nlink, uid, gid, rdev, size,
+			blksize, blocks, atime, mtime, ctime);
+err:
+	return error;
+}
+
+int fuse_vfs_stat(unsigned long long file, unsigned long long *inode,
+		  unsigned long *mode, unsigned long *nlink, unsigned long *uid,
+		  unsigned long *gid, unsigned long *rdev,
+		  unsigned long long *size, unsigned long *blksize,
+		  unsigned long long *blocks, unsigned long long *atime,
+		  unsigned long long *mtime, unsigned long long *ctime)
+{
+	struct dentry_tree *dir = rb_find_dentry(file);
+	int error = -ENOENT;
+
+	if(dir == NULL)
+		goto err;
+
+	error = do_stat(dir->dentry, dir->mnt, inode, mode, nlink, uid, gid,
+			rdev, size, blksize, blocks, atime, mtime, ctime);
+err:
+	return error;
+}
+
+int fuse_vfs_setattr(unsigned long long file, int mask,
+		     unsigned long long *inode, unsigned long *mode,
+		     unsigned long *nlink, unsigned long *uid,
+		     unsigned long *gid, unsigned long *rdev,
+		     unsigned long long *size, unsigned long *blksize,
+		     unsigned long long *blocks, unsigned long long *atime,
+		     unsigned long long *mtime, unsigned long long *ctime)
+{
+	struct iattr attrs;
+	struct dentry_tree *rb = rb_find_dentry(file);
+	int err;
+
+	log_info("fuse_vfs_setattr - rb = 0x%p\n", rb);
+	if(rb == NULL)
+		return -ENOENT;
+	atomic_inc(&rb->count);
+
+	attrs.ia_valid = 0;
+	if(mask & UML_FUSE_MODE){
+		attrs.ia_valid |= ATTR_MODE;
+		attrs.ia_mode = *mode;
+	}
+	if(mask & UML_FUSE_UID){
+		attrs.ia_valid |= ATTR_UID;
+		attrs.ia_uid = *uid;
+	}
+	if(mask & UML_FUSE_GID){
+		attrs.ia_valid |= ATTR_GID;
+		attrs.ia_gid = *gid;
+	}
+	if(mask & UML_FUSE_SIZE){
+		attrs.ia_valid |= ATTR_SIZE;
+		attrs.ia_size = *size;
+	}
+	if(mask & UML_FUSE_ATIME){
+		attrs.ia_valid |= ATTR_ATIME;
+		attrs.ia_atime.tv_sec = *atime / BILLION;
+		attrs.ia_atime.tv_nsec = *atime % BILLION;
+	}
+	if(mask & UML_FUSE_MTIME){
+		attrs.ia_valid |= ATTR_MTIME;
+		attrs.ia_mtime.tv_sec = *mtime / BILLION;
+		attrs.ia_mtime.tv_nsec = *mtime % BILLION;
+	}
+
+	err = notify_change(rb->dentry, &attrs);
+	if(err)
+		goto err;
+
+	err = do_stat(rb->dentry, rb->mnt, inode, mode, nlink, uid, gid, rdev,
+		      size, blksize, blocks, atime, mtime, ctime);
+
+err:
+	atomic_dec(&rb->count);
+	return err;
+}
+
+void fuse_vfs_forget(unsigned long long inode, int nforgets)
+{
+	struct dentry_tree *node = rb_find_dentry(inode);
+
+	if(node == NULL){
+		printk("dentry for inode %Ld not found\n", inode);
+		return;
+	}
+
+	if(atomic_sub_and_test(nforgets, &node->count)){
+		spin_lock(&dentry_lock);
+		rb_erase(&node->rb, &dentry_root);
+		spin_unlock(&dentry_lock);
+		dput(node->dentry);
+		kfree(node);
+	}
+}
+
+int fuse_vfs_opendir(unsigned long long inode, void **file_out)
+{
+	struct file *file;
+	struct dentry_tree *rb;
+
+	rb = rb_find_dentry(inode);
+	if(rb == NULL)
+		return -ENOENT;
+
+	file = dentry_open(dget(rb->dentry), mntget(rb->mnt),
+			   O_RDONLY | O_LARGEFILE | O_DIRECTORY);
+	if(IS_ERR(file))
+		return PTR_ERR(file);
+
+	*file_out = file;
+	return 0;
+}
+
+static int filldir(void *buf, const char *name, int namelen, loff_t pos,
+		   ino_t ino, unsigned int type)
+{
+	return dirbuf_add(buf, (char *) name, namelen, ino, type << 12);
+}
+
+int fuse_vfs_readdir(void *f, void *data)
+{
+	struct file *file = f;
+	int err;
+
+	err = vfs_readdir(file, filldir, data);
+	if(err)
+		return err;
+	return 0;
+}
+
+void fuse_vfs_release(void *f)
+{
+	fput((struct file *) f);
+}
+
+int fuse_vfs_readlink(unsigned long long ino, char *buf, int bufsize)
+{
+	struct inode *inode;
+	struct dentry *dentry;
+	struct dentry_tree *rb;
+	int error;
+
+	rb = rb_find_dentry(ino);
+	if(rb == NULL)
+		return -ENOENT;
+
+	/* This is a copy of the guts of sys_readlinkat - there is no vfs
+	 * interface which does a readlink given a dentry.
+	 */
+	dentry = rb->dentry;
+	inode = dentry->d_inode;
+	error = -EINVAL;
+	if (inode->i_op && inode->i_op->readlink) {
+		error = security_inode_readlink(dentry);
+		if (!error) {
+			touch_atime(rb->mnt, dentry);
+			error = inode->i_op->readlink(dentry, buf, bufsize);
+
+			if((error >= 0) && (error < bufsize))
+				buf[error] = '\0';
+		}
+	}
+
+	return error;
+}
+
+static const int flag_bits[] = { [OPEN_APPEND]		= O_APPEND,
+				 [OPEN_NONBLOCK]	= O_NONBLOCK,
+				 [OPEN_SYNC]		= O_SYNC,
+				 [OPEN_FASYNC]		= FASYNC,
+				 [OPEN_DIRECT]		= O_DIRECT,
+				 [OPEN_LARGEFILE]	= O_LARGEFILE,
+				 [OPEN_NOFOLLOW]	= O_NOFOLLOW,
+				 [OPEN_NOATIME]		= O_NOATIME };
+
+static int convert_mode(int flags)
+{
+	int i, mode = 0;
+
+	for(i = 0; i < sizeof(mode) * 8; i++){
+		if(flags & (1 << i))
+			mode |= flag_bits[i];
+	}
+	mode = flags & ((1 << OPEN_READ) | (1 << OPEN_WRITE));
+	switch(mode){
+	case 1 << OPEN_READ:
+		mode |= O_RDONLY; break;
+	case 1 << OPEN_WRITE:
+		mode |= O_WRONLY; break;
+	case (1 << OPEN_READ) | (1 << OPEN_WRITE):
+		mode |= O_RDWR; break;
+	default:
+		printk("fuse_vfs_open - bad mode %d\n", mode); break;
+	}
+
+	return mode;
+}
+
+unsigned long fuse_vfs_open(unsigned long long ino, unsigned long flags,
+			    void **file_out)
+{
+	struct dentry_tree *rb;
+	struct vfsmount *mnt;
+	struct dentry *dentry;
+	struct file *file;
+	int error;
+
+	rb = rb_find_dentry(ino);
+	if(rb == NULL)
+		return -ENOENT;
+
+	dentry = dget(rb->dentry);
+	mnt = mntget(rb->mnt);
+	file = dentry_open(dentry, mnt, convert_mode(flags));
+	if(IS_ERR(file))
+		return PTR_ERR(file);
+
+	log_info("use_vfs_open saving inode %ld\n", dentry->d_inode->i_ino);
+	rb = save_dentry(dentry, mnt);
+	if(IS_ERR(rb)){
+		error = PTR_ERR(rb);
+		goto err;
+	}
+	atomic_inc(&rb->count);
+
+	*file_out = file;
+	return 0;
+
+err:
+	fput(file);
+	return error;
+}
+
+int fuse_vfs_create(unsigned long long parent, const char *name, int flags,
+		    int perms, void **file_out, unsigned long long *inode,
+		    unsigned long *mode, unsigned long *nlink,
+		    unsigned long *uid, unsigned long *gid, unsigned long *rdev,
+		    unsigned long long *size, unsigned long *blksize,
+		    unsigned long long *blocks, unsigned long long *atime,
+		    unsigned long long *mtime, unsigned long long *ctime)
+{
+	struct dentry_tree *dir, *rb;
+	struct dentry *dentry;
+	struct vfsmount *mnt;
+	struct file *file;
+	int error = -ENOENT;
+
+	dir = rb_find_dentry(parent);
+	if(dir == NULL)
+		goto err;
+
+	dentry = lookup_one_len(name, dir->dentry, strlen(name));
+	if(IS_ERR(dentry)){
+		error = PTR_ERR(dentry);
+		goto err;
+	}
+
+	error = vfs_create(dir->dentry->d_inode, dentry, perms, NULL);
+	if(error)
+		goto err;
+
+	dentry = dget(dentry);
+	mnt = mntget(dir->mnt);
+	file = dentry_open(dentry, mnt, convert_mode(flags));
+	if(IS_ERR(file)){
+		error = PTR_ERR(file);
+		goto err_unlink;
+	}
+
+	log_info("use_vfs_create saving inode %ld\n", dentry->d_inode->i_ino);
+	rb = save_dentry(dentry, mnt);
+	if(IS_ERR(rb)){
+		error = PTR_ERR(rb);
+		goto err_fput;
+	}
+	atomic_inc(&rb->count);
+
+	*file_out = file;
+	error = do_stat(dentry, dir->mnt, inode, mode, nlink, uid, gid, rdev,
+			size, blksize, blocks, atime, mtime, ctime);
+err:
+	return error;
+
+err_fput:
+	fput(file);
+err_unlink:
+	vfs_unlink(dentry->d_inode, dentry);
+
+	return error;
+}
+
+int fuse_vfs_read(void *f, unsigned long long off, void *buf,
+		  unsigned long size)
+{
+	struct file *file = f;
+	loff_t pos = off;
+	int err;
+
+	err = vfs_read(file, buf, size, &pos);
+	file->f_pos = pos;
+
+	return err;
+}
+
+int fuse_vfs_write(void *f, unsigned long long off, const void *buf,
+		   unsigned long size)
+{
+	struct file *file = f;
+	loff_t pos = off;
+	int err;
+
+	err = vfs_write(file, buf, size, &pos);
+	file->f_pos = pos;
+
+	return err;
+}
+