# This is the externfs/new hostfs/humfs patch.  hostfs now seems to be stable.
# The old hostfs will continue to exist until this one is as functional and 
# stable as it.
Index: linux-2.6.17/arch/um/defconfig
===================================================================
--- linux-2.6.17.orig/arch/um/defconfig	2007-11-19 11:58:12.000000000 -0500
+++ linux-2.6.17/arch/um/defconfig	2007-11-19 17:19:39.000000000 -0500
@@ -77,7 +77,7 @@ CONFIG_LD_SCRIPT_DYN=y
 CONFIG_NET=y
 CONFIG_BINFMT_ELF=y
 CONFIG_BINFMT_MISC=m
-# CONFIG_HOSTFS is not set
+CONFIG_HOSTFS=y
 # CONFIG_HPPFS is not set
 CONFIG_MCONSOLE=y
 CONFIG_MAGIC_SYSRQ=y
@@ -196,12 +196,36 @@ CONFIG_HOSTAUDIO=m
 CONFIG_UML_RANDOM=y
 
 #
-# Generic Driver Options
+# Block layer
+#
+# CONFIG_LBD is not set
+
 #
-CONFIG_STANDALONE=y
-CONFIG_PREVENT_FIRMWARE_BUILD=y
-# CONFIG_FW_LOADER is not set
-# CONFIG_DEBUG_DRIVER is not set
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV_UBD=y
+CONFIG_BLK_DEV_UBD_SYNC=y
+CONFIG_BLK_DEV_COW_COMMON=y
+# CONFIG_MMAPPER is not set
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_BLK_DEV_RAM_COUNT=16
+# CONFIG_ATA_OVER_ETH is not set
 
 #
 # Networking
@@ -470,6 +494,9 @@ CONFIG_NLS_DEFAULT="iso8859-1"
 # CONFIG_NLS_KOI8_R is not set
 # CONFIG_NLS_KOI8_U is not set
 # CONFIG_NLS_UTF8 is not set
+CONFIG_EXTERNFS=y
+CONFIG_HUMFS=y
+# CONFIG_OLD_HOSTFS is not set
 
 #
 # Security options
Index: linux-2.6.17/arch/um/drivers/ubd_kern.c
===================================================================
--- linux-2.6.17.orig/arch/um/drivers/ubd_kern.c	2007-11-19 11:58:12.000000000 -0500
+++ linux-2.6.17/arch/um/drivers/ubd_kern.c	2007-11-19 17:19:39.000000000 -0500
@@ -1201,7 +1201,10 @@ static int path_requires_switch(char *fr
 		printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
 		return 1;
 	}
-	if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
+
+	if((buf1.ust_major == buf2.ust_major) &&
+	   (buf1.ust_minor == buf2.ust_minor) &&
+	   (buf1.ust_ino == buf2.ust_ino))
 		return 0;
 
 	printk("Backing file mismatch - \"%s\" requested,\n"
Index: linux-2.6.17/arch/um/include/filehandle.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/arch/um/include/filehandle.h	2007-11-19 17:19:39.000000000 -0500
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __FILEHANDLE_H__
+#define __FILEHANDLE_H__
+
+#include "linux/list.h"
+#include "os.h"
+
+struct file_handle {
+	struct list_head list;
+	int fd;
+	char *(*get_name)(void *);
+	void *arg;
+	struct openflags flags;
+};
+
+extern struct file_handle bad_filehandle;
+
+extern void init_filehandle(struct file_handle *fh, int fd,
+			    struct openflags flags);
+extern int open_file(char *name, struct openflags flags, int mode);
+extern void *open_dir(char *file);
+extern int open_filehandle(char *name, struct openflags flags, int mode,
+			   struct file_handle *fh);
+extern int read_file(struct file_handle *fh, unsigned long long offset,
+		     char *buf, int len);
+extern int write_file(struct file_handle *fh, unsigned long long offset,
+		      const char *buf, int len);
+extern int fsync_file(struct file_handle *fh, int datasync);
+extern int truncate_file(struct file_handle *fh, unsigned long long size);
+extern int close_file(struct file_handle *fh);
+extern void not_reclaimable(struct file_handle *fh);
+extern void is_reclaimable(struct file_handle *fh, char *(name_proc)(void *),
+			   void *arg);
+extern int filehandle_fd(struct file_handle *fh);
+extern int make_pipe(struct file_handle *fhs);
+
+#endif
Index: linux-2.6.17/arch/um/include/os.h
===================================================================
--- linux-2.6.17.orig/arch/um/include/os.h	2007-11-19 16:41:56.000000000 -0500
+++ linux-2.6.17/arch/um/include/os.h	2007-11-19 17:19:39.000000000 -0500
@@ -36,7 +36,8 @@
  * (if they are wrong here, they are wrong there...).
  */
 struct uml_stat {
-	int                ust_dev;        /* device */
+	int                ust_major;      /* device */
+	int                ust_minor;
 	unsigned long long ust_ino;        /* inode */
 	int                ust_mode;       /* protection */
 	int                ust_nlink;      /* number of hard links */
@@ -48,6 +49,8 @@ struct uml_stat {
 	unsigned long      ust_atime;      /* time of last access */
 	unsigned long      ust_mtime;      /* time of last modification */
 	unsigned long      ust_ctime;      /* time of last change */
+	int                ust_rmajor;
+	int                ust_rminor;
 };
 
 struct openflags {
@@ -134,8 +137,13 @@ static inline struct openflags of_direct
 
 /* file.c */
 extern int os_stat_file(const char *file_name, struct uml_stat *buf);
+extern int os_lstat_file(const char *file_name, struct uml_stat *ubuf);
 extern int os_stat_fd(const int fd, struct uml_stat *buf);
 extern int os_access(const char *file, int mode);
+extern int os_set_file_time(const char *file, unsigned long access,
+			    unsigned long mod);
+extern int os_set_file_perms(const char *file, int mode);
+extern int os_set_file_owner(const char *file, int owner, int group);
 extern int os_get_exec_close(int fd, int *close_on_exec);
 extern int os_set_exec_close(int fd);
 extern int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg);
@@ -146,9 +154,21 @@ extern int os_mode_fd(int fd, int mode);
 
 extern int os_seek_file(int fd, unsigned long long offset);
 extern int os_open_file(char *file, struct openflags flags, int mode);
+extern void *os_open_dir(char *dir, int *err_out);
+extern int os_seek_dir(void *stream, unsigned long long pos);
+extern int os_read_dir(void *stream, unsigned long long *ino_out,
+		       char **name_out);
+extern int os_tell_dir(void *stream);
+extern int os_close_dir(void *stream);
+extern int os_remove_file(const char *file);
+extern int os_move_file(const char *from, const char *to);
+extern int os_fsync_fd(int fd, int datasync);
+extern int os_truncate_file(const char *file, unsigned long long len);
+extern int os_truncate_fd(int fd, unsigned long long len);
 extern int os_read_file(int fd, void *buf, int len);
 extern int os_write_file(int fd, const void *buf, int count);
 extern int os_file_size(char *file, unsigned long long *size_out);
+extern int os_fd_size(int fd, long long *size_out);
 extern int os_file_modtime(char *file, unsigned long *modtime);
 extern int os_pipe(int *fd, int stream, int close_on_exec);
 extern int os_set_fd_async(int fd, int owner);
@@ -156,6 +176,12 @@ extern int os_clear_fd_async(int fd);
 extern int os_set_fd_block(int fd, int blocking);
 extern int os_accept_connection(int fd);
 extern int os_create_unix_socket(char *file, int len, int close_on_exec);
+extern int os_make_symlink(const char *to, const char *from);
+extern int os_read_symlink(const char *file, char *buf, int size);
+extern int os_link_file(const char *to, const char *from);
+extern int os_make_dir(const char *dir, int mode);
+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);
@@ -209,6 +235,14 @@ extern int os_unmap_memory(void *addr, i
 extern int os_drop_memory(void *addr, int length);
 extern int can_drop_memory(void);
 extern void os_flush_stdout(void);
+extern int os_stat_filesystem(char *path, long *bsize_out,
+			      long long *blocks_out, long long *bfree_out,
+			      long long *bavail_out, long long *files_out,
+			      long long *ffree_out, void *fsid_out,
+			      int fsid_size, long *namelen_out,
+			      long *spare_out);
+extern int os_change_dir(char *dir);
+extern int os_fchange_dir(int fd);
 
 /* uaccess.c */
 extern unsigned long __do_user_copy(void *to, const void *from, int n,
Index: linux-2.6.17/arch/um/kernel/filehandle.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/arch/um/kernel/filehandle.c	2007-11-19 17:19:39.000000000 -0500
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/slab.h"
+#include "linux/list.h"
+#include "linux/spinlock.h"
+#include "linux/fs.h"
+#include "linux/errno.h"
+#include "linux/err.h"
+#include "filehandle.h"
+#include "os.h"
+#include "kern_util.h"
+
+static DEFINE_SPINLOCK(open_files_lock);
+static LIST_HEAD(open_files);
+
+#define NUM_RECLAIM 128
+
+static void reclaim_fds(void)
+{
+	struct file_handle *victim;
+	int closed = NUM_RECLAIM;
+
+	spin_lock(&open_files_lock);
+	while(!list_empty(&open_files) && closed--){
+		victim = list_entry(open_files.prev, struct file_handle, list);
+		os_close_file(victim->fd);
+		victim->fd = -EBADF;
+		list_del_init(&victim->list);
+	}
+	spin_unlock(&open_files_lock);
+}
+
+int open_file(char *name, struct openflags flags, int mode)
+{
+	int fd;
+
+	fd = os_open_file(name, flags, mode);
+	if(fd != -EMFILE)
+		return fd;
+
+	reclaim_fds();
+	fd = os_open_file(name, flags, mode);
+
+	return fd;
+}
+
+void *open_dir(char *file)
+{
+	void *dir;
+	int err;
+
+	dir = os_open_dir(file, &err);
+	if(dir != NULL)
+		return dir;
+	if(err != -EMFILE)
+		return ERR_PTR(err);
+
+	reclaim_fds();
+
+	dir = os_open_dir(file, &err);
+	if(dir == NULL)
+		dir = ERR_PTR(err);
+
+	return dir;
+}
+
+void not_reclaimable(struct file_handle *fh)
+{
+	char *name;
+
+	if(fh->fd == -EBADF)
+		return;
+
+	if(fh->get_name == NULL)
+		return;
+
+	if(list_empty(&fh->list)){
+		name = (*fh->get_name)(fh->arg);
+		if(!IS_ERR(name)){
+			fh->fd = open_file(name, fh->flags, 0);
+			kfree(name);
+		}
+		else printk("File descriptor %d has no name, err = %ld\n",
+			    fh->fd, PTR_ERR(name));
+	}
+	else {
+		spin_lock(&open_files_lock);
+		list_del_init(&fh->list);
+		spin_unlock(&open_files_lock);
+	}
+}
+
+void is_reclaimable(struct file_handle *fh, char *(name_proc)(void *arg),
+		    void *arg)
+{
+	fh->get_name = name_proc;
+	fh->arg = arg;
+
+	spin_lock(&open_files_lock);
+	list_add(&fh->list, &open_files);
+	spin_unlock(&open_files_lock);
+}
+
+static int active_handle(struct file_handle *fh)
+{
+	int fd;
+	char *name;
+
+	spin_lock(&open_files_lock);
+	if(!list_empty(&fh->list))
+		list_move(&fh->list, &open_files);
+	spin_unlock(&open_files_lock);
+
+	if(fh->fd != -EBADF)
+		return 0;
+
+	name = (*fh->get_name)(fh->arg);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+
+	fd = open_file(name, fh->flags, 0);
+	kfree(name);
+	if(fd < 0)
+		return fd;
+
+	fh->fd = fd;
+	is_reclaimable(fh, fh->get_name, fh->arg);
+
+	return 0;
+}
+
+int filehandle_fd(struct file_handle *fh)
+{
+	int err;
+
+	err = active_handle(fh);
+	if(err)
+		return err;
+
+	return fh->fd;
+}
+
+void init_filehandle(struct file_handle *fh, int fd, struct openflags flags)
+{
+	flags.c = 0;
+	*fh = ((struct file_handle) { .list	= LIST_HEAD_INIT(fh->list),
+				      .fd	= fd,
+			              .get_name	= NULL,
+			              .arg	= NULL,
+				      .flags	= flags });
+}
+
+int open_filehandle(char *name, struct openflags flags, int mode,
+		    struct file_handle *fh)
+{
+	int fd;
+
+	fd = open_file(name, flags, mode);
+	if(fd < 0)
+		return fd;
+
+	flags.c = 0;
+	init_filehandle(fh, fd, flags);
+	return 0;
+}
+
+int close_file(struct file_handle *fh)
+{
+	spin_lock(&open_files_lock);
+	list_del_init(&fh->list);
+	spin_unlock(&open_files_lock);
+
+	os_close_file(fh->fd);
+
+	fh->fd = -EBADF;
+	return 0;
+}
+
+int read_file(struct file_handle *fh, unsigned long long offset, char *buf,
+	      int len)
+{
+	int err;
+
+	err = active_handle(fh);
+	if(err)
+		return err;
+
+	err = os_seek_file(fh->fd, offset);
+	if(err)
+		return err;
+
+	return os_read_file(fh->fd, buf, len);
+}
+
+int write_file(struct file_handle *fh, unsigned long long offset,
+	       const char *buf, int len)
+{
+	int err;
+
+	err = active_handle(fh);
+	if(err)
+		return err;
+
+	if(offset != -1)
+		err = os_seek_file(fh->fd, offset);
+	if(err)
+		return err;
+
+	return os_write_file(fh->fd, buf, len);
+}
+
+int fsync_file(struct file_handle *fh, int datasync)
+{
+	int err;
+
+	err = active_handle(fh);
+	if(err)
+		return err;
+
+	return os_fsync_fd(fh->fd, datasync);
+}
+
+int truncate_file(struct file_handle *fh, unsigned long long size)
+{
+	int err;
+
+	err = active_handle(fh);
+	if(err)
+		return err;
+
+	return os_truncate_fd(fh->fd, size);
+}
+
+int make_pipe(struct file_handle *fhs)
+{
+	int fds[2], err;
+
+	err = os_pipe(fds, 1, 1);
+	if(err && (err != -EMFILE))
+		return err;
+
+	if(err){
+		reclaim_fds();
+		err = os_pipe(fds, 1, 1);
+	}
+	if(err)
+		return err;
+
+	init_filehandle(&fhs[0], fds[0], OPENFLAGS());
+	init_filehandle(&fhs[1], fds[1], OPENFLAGS());
+	return 0;
+}
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:58:12.000000000 -0500
+++ linux-2.6.17/arch/um/os-Linux/file.c	2007-11-19 17:19:40.000000000 -0500
@@ -8,6 +8,9 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <utime.h>
+#include <dirent.h>
+#include <linux/kdev_t.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
@@ -15,6 +18,7 @@
 #include <sys/ioctl.h>
 #include <sys/mount.h>
 #include <sys/uio.h>
+#include <sys/vfs.h>
 #include "os.h"
 #include "user.h"
 #include "kern_util.h"
@@ -22,7 +26,8 @@
 static void copy_stat(struct uml_stat *dst, struct stat64 *src)
 {
 	*dst = ((struct uml_stat) {
-		.ust_dev     = src->st_dev,     /* device */
+		.ust_major   = MAJOR(src->st_dev),     /* device */
+		.ust_minor   = MINOR(src->st_dev),
 		.ust_ino     = src->st_ino,     /* inode */
 		.ust_mode    = src->st_mode,    /* protection */
 		.ust_nlink   = src->st_nlink,   /* number of hard links */
@@ -68,6 +73,23 @@ int os_stat_file(const char *file_name, 
 	return err;
 }
 
+int os_lstat_file(const char *file_name, struct uml_stat *ubuf)
+{
+	struct stat64 sbuf;
+	int err;
+
+	do {
+		err = lstat64(file_name, &sbuf);
+	} while((err < 0) && (errno == EINTR)) ;
+
+	if(err < 0)
+		return -errno;
+
+	if(ubuf != NULL)
+		copy_stat(ubuf, &sbuf);
+	return err;
+}
+
 int os_access(const char* file, int mode)
 {
 	int amode, err;
@@ -82,6 +104,41 @@ int os_access(const char* file, int mode
 	return 0;
 }
 
+int os_set_file_time(const char *file, unsigned long access, unsigned long mod)
+{
+	struct utimbuf buf = ((struct utimbuf){ .actime = access,
+						.modtime = mod });
+	int err;
+
+	err = utime(file, &buf);
+	if(err < 0)
+		return -errno;
+
+	return 0;
+}
+
+int os_set_file_perms(const char *file, int mode)
+{
+	int err;
+
+	err = chmod(file, mode);
+	if(err < 0)
+		return -errno;
+
+	return 0;
+}
+
+int os_set_file_owner(const char *file, int owner, int group)
+{
+	int err;
+
+	err = chown(file, owner, group);
+	if(err < 0)
+		return -errno;
+
+	return 0;
+}
+
 /* FIXME? required only by hostaudio (because it passes ioctls verbatim) */
 int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg)
 {
@@ -149,7 +206,7 @@ int os_file_type(char *file)
 	struct uml_stat buf;
 	int err;
 
-	err = os_stat_file(file, &buf);
+	err = os_lstat_file(file, &buf);
 	if(err < 0)
 		return err;
 
@@ -216,6 +273,109 @@ int os_open_file(char *file, struct open
 	return fd;
 }
 
+void *os_open_dir(char *path, int *err_out)
+{
+       void *dir;
+
+       dir = opendir(path);
+       *err_out = -errno;
+       return dir;
+}
+
+int os_seek_dir(void *stream, unsigned long long pos)
+{
+	seekdir(stream, pos);
+	return 0;
+}
+
+int os_read_dir(void *stream, unsigned long long *ino_out, char **name_out)
+{
+	struct dirent *ent;
+
+	errno = 0;
+	ent = readdir(stream);
+	if(ent == NULL){
+		if(errno != 0)
+			return -errno;
+		*name_out = NULL;
+		return 0;
+	}
+
+	*ino_out = ent->d_ino;
+	*name_out = ent->d_name;
+	return 0;
+}
+
+int os_tell_dir(void *stream)
+{
+	return telldir(stream);
+}
+
+int os_close_dir(void *stream)
+{
+	int err;
+
+	err = closedir(stream);
+	if(err < 0)
+		return -errno;
+	return 0;
+}
+
+int os_remove_file(const char *file)
+{
+	int err;
+
+	err = unlink(file);
+	if(err)
+		return -errno;
+
+	return 0;
+}
+
+int os_move_file(const char *from, const char *to)
+{
+	int err;
+
+	err = rename(from, to);
+	if(err)
+		return -errno;
+
+	return 0;
+}
+
+int os_fsync_fd(int fd, int datasync)
+{
+	int ret;
+	if (datasync)
+		ret = fdatasync(fd);
+	else
+		ret = fsync(fd);
+
+	if (ret < 0)
+		return -errno;
+	return 0;
+}
+
+int os_truncate_fd(int fd, unsigned long long len)
+{
+	int err;
+
+	err = ftruncate(fd, len);
+	if(err)
+		return -errno;
+	return 0;
+}
+
+int os_truncate_file(const char *file, unsigned long long len)
+{
+	int err;
+
+	err = truncate(file, len);
+	if(err)
+		return -errno;
+	return 0;
+}
+
 int os_connect_socket(char *name)
 {
 	struct sockaddr_un sock;
@@ -314,6 +474,19 @@ int os_file_size(char *file, unsigned lo
 	return 0;
 }
 
+int os_fd_size(int fd, long long *size_out)
+{
+	struct stat buf;
+	int err;
+
+	err = fstat(fd, &buf);
+	if(err)
+		return -errno;
+
+	*size_out = buf.st_size;
+	return 0;
+}
+
 int os_file_modtime(char *file, unsigned long *modtime)
 {
 	struct uml_stat buf;
@@ -542,6 +715,72 @@ int os_create_unix_socket(char *file, in
 	return sock;
 }
 
+int os_make_symlink(const char *to, const char *from)
+{
+	int err;
+
+	err = symlink(to, from);
+	if(err)
+		return -errno;
+
+	return 0;
+}
+
+int os_read_symlink(const char *file, char *buf, int size)
+{
+	int err;
+
+	err = readlink(file, buf, size);
+	if(err < 0)
+		return -errno;
+
+	return err;
+}
+
+int os_link_file(const char *to, const char *from)
+{
+	int err;
+
+	err = link(to, from);
+	if(err)
+		return -errno;
+
+	return 0;
+}
+
+int os_make_dir(const char *dir, int mode)
+{
+	int err;
+
+	err = mkdir(dir, mode);
+	if(err)
+		return -errno;
+
+	return 0;
+}
+
+int os_make_dev(const char *name, int mode, int major, int minor)
+{
+	int err;
+
+	err = mknod(name, mode, makedev(major, minor));
+	if(err)
+		return -errno;
+
+	return 0;
+}
+
+int os_remove_dir(const char *dir)
+{
+	int err;
+
+	err = rmdir(dir);
+	if(err)
+		return -errno;
+
+	return 0;
+}
+
 void os_flush_stdout(void)
 {
 	fflush(stdout);
@@ -572,3 +811,57 @@ int os_lock_file(int fd, int excl)
  out:
 	return err;
 }
+
+int os_stat_filesystem(char *path, long *bsize_out, long long *blocks_out,
+		       long long *bfree_out, long long *bavail_out,
+		       long long *files_out, long long *ffree_out,
+		       void *fsid_out, int fsid_size, long *namelen_out,
+		       long *spare_out)
+{
+	struct statfs64 buf;
+	int err;
+
+	err = statfs64(path, &buf);
+	if(err < 0)
+		return -errno;
+
+	*bsize_out = buf.f_bsize;
+	*blocks_out = buf.f_blocks;
+	*bfree_out = buf.f_bfree;
+	*bavail_out = buf.f_bavail;
+	*files_out = buf.f_files;
+	*ffree_out = buf.f_ffree;
+	memcpy(fsid_out, &buf.f_fsid,
+	       sizeof(buf.f_fsid) > fsid_size ? fsid_size :
+	       sizeof(buf.f_fsid));
+	*namelen_out = buf.f_namelen;
+	spare_out[0] = buf.f_spare[0];
+	spare_out[1] = buf.f_spare[1];
+	spare_out[2] = buf.f_spare[2];
+	spare_out[3] = buf.f_spare[3];
+	spare_out[4] = buf.f_spare[4];
+	spare_out[5] = buf.f_spare[5];
+	return 0;
+}
+
+int os_change_dir(char *dir)
+{
+	int err;
+
+	err = chdir(dir);
+	if(err < 0)
+		return -errno;
+
+	return 0;
+}
+
+int os_fchange_dir(int fd)
+{
+	int err;
+
+	err = fchdir(fd);
+	if(err < 0)
+		return -errno;
+
+	return 0;
+}
Index: linux-2.6.17/fs/Kconfig
===================================================================
--- linux-2.6.17.orig/fs/Kconfig	2007-11-19 11:58:12.000000000 -0500
+++ linux-2.6.17/fs/Kconfig	2007-11-19 17:19:40.000000000 -0500
@@ -2114,6 +2114,75 @@ menu "Partition Types"
 
 source "fs/partitions/Kconfig"
 
+config EXTERNFS
+	bool
+	default n
+
+config HUMFS
+	tristate 'Usable host filesystem'
+	depends on UML
+	select EXTERNFS
+	default y
+	help
+	humfs is a host-based filesystem for UML which handles permissions
+	properly, unlike hostfs.  The humfs mountpoint on the host needs
+	to be prepared beforehand with the humfsify script.
+
+config HOSTFS
+	tristate "New Host filesystem"
+	depends on UML
+	select EXTERNFS
+	default y
+	help
+	This is the new externfs-based hostfs.  externfs is a general
+	framework for importing host data into a UML as a filesystem,
+	and this version of hostfs plugs into it, as opposed to the
+	monolithic structure of the old hostfs.
+
+        While the User-Mode Linux port uses its own root file system for
+        booting and normal file access, this module lets the UML user
+        access files stored on the host.  It does not require any
+        network connection between the Host and UML.  An example use of
+        this might be:
+
+        mount none /tmp/fromhost -t hostfs -o /tmp/umlshare
+
+        where /tmp/fromhost is an empty directory inside UML and
+        /tmp/umlshare is a directory on the host with files the UML user
+        wishes to access.
+
+        For more information, see
+        <http://user-mode-linux.sourceforge.net/hostfs.html>.
+
+        If you'd like to be able to work with files stored on the host,
+        say Y or M here; otherwise say N.
+
+config OLD_HOSTFS
+	tristate "Old Host filesystem"
+	depends on UML
+	default n
+	help
+	This is the old, non-externfs-based hostfs.  This will be
+	going away when the new hostfs is considered stable.
+
+        While the User-Mode Linux port uses its own root file system for
+        booting and normal file access, this module lets the UML user
+        access files stored on the host.  It does not require any
+        network connection between the Host and UML.  An example use of
+        this might be:
+
+        mount none /tmp/fromhost -t hostfs -o /tmp/umlshare
+
+        where /tmp/fromhost is an empty directory inside UML and
+        /tmp/umlshare is a directory on the host with files the UML user
+        wishes to access.
+
+        For more information, see
+        <http://user-mode-linux.sourceforge.net/hostfs.html>.
+
+        If you'd like to be able to work with files stored on the host,
+        say Y or M here; otherwise say N.
+
 endmenu
 endif
 
Index: linux-2.6.17/fs/Makefile
===================================================================
--- linux-2.6.17.orig/fs/Makefile	2007-11-19 11:58:12.000000000 -0500
+++ linux-2.6.17/fs/Makefile	2007-11-19 17:19:40.000000000 -0500
@@ -113,8 +113,9 @@ obj-$(CONFIG_XFS_FS)		+= xfs/
 obj-$(CONFIG_9P_FS)		+= 9p/
 obj-$(CONFIG_AFS_FS)		+= afs/
 obj-$(CONFIG_BEFS_FS)		+= befs/
-obj-$(CONFIG_HOSTFS)		+= hostfs/
 obj-$(CONFIG_HPPFS)		+= hppfs/
 obj-$(CONFIG_DEBUG_FS)		+= debugfs/
 obj-$(CONFIG_OCFS2_FS)		+= ocfs2/
 obj-$(CONFIG_GFS2_FS)           += gfs2/
+obj-$(CONFIG_OLD_HOSTFS)	+= hostfs/
+obj-$(CONFIG_EXTERNFS)		+= externfs/
Index: linux-2.6.17/fs/externfs/Makefile
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/fs/externfs/Makefile	2007-11-19 17:19:40.000000000 -0500
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+# Licensed under the GPL
+#
+
+obj-y =
+obj-$(CONFIG_EXTERNFS) += externfs.o
+obj-$(CONFIG_HOSTFS) += host_fs.o host_file.o
+obj-$(CONFIG_HUMFS) += humfs.o meta_fs.o
+
+SINGLE_OBJS = $(foreach f,$(patsubst %.o,%,$(obj-y) $(obj-m)),$($(f)-objs))
+
+USER_OBJS := $(filter %_user.o,$(obj-y) $(obj-m) $(SINGLE_OBJS))
+USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file))
+
+$(USER_OBJS) : %.o: %.c
+	$(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $<
Index: linux-2.6.17/fs/externfs/externfs.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/fs/externfs/externfs.c	2007-11-19 17:19:40.000000000 -0500
@@ -0,0 +1,1308 @@
+/*
+ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/stddef.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/writeback.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/blkdev.h>
+#include <linux/statfs.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "hostfs.h"
+#include "kern_util.h"
+#include "kern.h"
+#include "mem.h"
+#include "filehandle.h"
+
+struct externfs {
+	struct list_head list;
+	struct externfs_mount_ops *mount_ops;
+	struct file_system_type type;
+};
+
+static inline struct externfs_inode *EXTERNFS_I(struct inode *inode)
+{
+	return container_of(inode, struct externfs_inode, vfs_inode);
+}
+
+#define file_externfs_i(file) EXTERNFS_I((file)->f_path.dentry->d_inode)
+
+#define EXTERNFS_SUPER_MAGIC 0x00c0ffee
+
+static const struct inode_operations externfs_iops;
+static const struct inode_operations externfs_dir_iops;
+static const struct address_space_operations externfs_link_aops;
+
+static char *dentry_name(struct dentry *dentry, int extra)
+{
+	struct dentry *parent;
+	char *name;
+	int len;
+
+	len = 0;
+	parent = dentry;
+	while(parent->d_parent != parent){
+		len += parent->d_name.len + 1;
+		parent = parent->d_parent;
+	}
+
+	name = kmalloc(len + extra + 1, GFP_KERNEL);
+	if(name == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	name[len] = '\0';
+	parent = dentry;
+	while(parent->d_parent != parent){
+		len -= parent->d_name.len + 1;
+		name[len] = '/';
+		strncpy(&name[len + 1], parent->d_name.name,
+			parent->d_name.len);
+		parent = parent->d_parent;
+	}
+
+	return name;
+}
+
+static char *inode_name(struct inode *ino, int extra)
+{
+	struct dentry *dentry;
+
+	if(list_empty(&ino->i_dentry))
+		return ERR_PTR(-ENOENT);
+
+	dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias);
+	return dentry_name(dentry, extra);
+}
+
+char *externfs_name_prefix(struct externfs_inode *ext, char *prefix)
+{
+	struct inode *inode = &ext->vfs_inode;
+	int len;
+	char *name;
+
+	len = strlen(prefix);
+	name = inode_name(inode, len);
+	if(IS_ERR(name))
+		return name;
+
+	memmove(&name[len], name, strlen(name) + 1);
+	memcpy(name, prefix, strlen(prefix));
+	return name;
+}
+
+static char *inode_dentry_name(struct inode *ino, struct dentry *dentry)
+{
+	char *file;
+	int len;
+
+	file = inode_name(ino, dentry->d_name.len + 1);
+	if(IS_ERR(file))
+		return file;
+
+	strcat(file, "/");
+	len = strlen(file);
+	strncat(file, dentry->d_name.name, dentry->d_name.len);
+	file[len + dentry->d_name.len] = '\0';
+	return file;
+}
+
+/* Directory removal is deferred to here, when the dentry is finally
+ * dropped, rather than when ->rmdir is called because some process
+ * may be cd-ed into it, in which case, it shouldn't be removed.  It
+ * can be removed at this point because switching to a new directory
+ * will cause the dentry to be released.
+ */
+static int externfs_delete_dentry(struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	struct externfs_data *ed;
+	struct externfs_file_ops *ops;
+	char *file;
+	int err;
+
+	if((inode == NULL) || !(inode->i_mode & S_IFDIR) ||
+	   (inode->i_nlink != 0))
+		return 0;
+
+	ed = inode->i_sb->s_fs_info;
+	ops = EXTERNFS_I(inode)->ops;
+	file = dentry_name(dentry, 0);
+	if(IS_ERR(file)){
+		printk("externfs_delete_dentry - didn't get file name, "
+		       "err = %lu\n", PTR_ERR(file));
+		return 1;
+	}
+
+	err = (*ops->remove_dir)(file, ed);
+	if(err)
+		printk("externfs_delete_dentry - ->remove_dir failed, "
+		       "err = %d\n", err);
+
+	return 1;
+}
+
+static struct dentry_operations externfs_dentry_ops = {
+	.d_delete	= externfs_delete_dentry,
+};
+
+static void copy_inode_info(struct inode *ino,
+			    struct externfs_inode_info *info)
+{
+	ino->i_atime.tv_sec = info->atime;
+	ino->i_atime.tv_nsec = 0;
+
+	ino->i_ctime.tv_sec = info->ctime;
+	ino->i_ctime.tv_nsec = 0;
+
+	ino->i_mtime.tv_sec = info->mtime;
+	ino->i_mtime.tv_nsec = 0;
+
+	ino->i_uid = info->uid;
+	ino->i_gid = info->gid;
+	ino->i_ino = info->ino;
+	ino->i_rdev = info->rdev;
+	ino->i_mode = info->mode;
+	ino->i_nlink = info->nlink;
+	ino->i_size = info->size;
+	ino->i_blocks = info->blocks;
+
+	/* atime is set automatically by the host, so there's no need to
+	 * sync it out ourselves.
+	 */
+	ino->i_flags |= S_NOATIME;
+}
+
+int externfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+	struct externfs_file_ops *ops = file_externfs_i(file)->ops;
+	struct inode *inode = dentry->d_inode;
+
+	return (*ops->fsync_file)(EXTERNFS_I(inode), datasync);
+}
+
+static int externfs_readdir(struct file *file, void *ent, filldir_t filldir)
+{
+	void *dir;
+	char *name;
+	unsigned long long next, ino;
+	int error, len;
+	struct externfs_file_ops *ops = file_externfs_i(file)->ops;
+	struct externfs_data *ed = file->f_path.dentry->d_inode->i_sb->s_fs_info;
+
+	name = dentry_name(file->f_path.dentry, 0);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+
+	dir = (*ops->open_dir)(name, current->fsuid, current->fsgid, ed);
+	kfree(name);
+	if(IS_ERR(dir))
+		return PTR_ERR(dir);
+
+	next = file->f_pos;
+	while((name = (*ops->read_dir)(dir, &next, &ino, &len, ed)) != NULL){
+		error = (*filldir)(ent, name, len, file->f_pos, ino,
+				   DT_UNKNOWN);
+		if(error)
+			break;
+		file->f_pos = next;
+	}
+	(*ops->close_dir)(dir, ed);
+	return 0;
+}
+
+struct wp_info {
+	struct page *page;
+	int count;
+	unsigned long long start;
+	unsigned long long size;
+	int (*truncate)(struct externfs_inode *ext, __u64 size);
+	struct externfs_inode *ei;
+};
+
+static void externfs_finish_writepage(char *buffer, int res, void *arg)
+{
+	struct wp_info *wp = arg;
+
+	if(res == wp->count){
+		ClearPageError(wp->page);
+		if(wp->start + res > wp->size)
+			(*wp->truncate)(wp->ei, wp->size);
+	}
+	else {
+		SetPageError(wp->page);
+		ClearPageUptodate(wp->page);
+	}
+
+	kunmap(wp->page);
+	unlock_page(wp->page);
+	kfree(wp);
+}
+
+static int externfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode = mapping->host;
+	struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
+	struct wp_info *wp;
+	char *buffer;
+	unsigned long long base;
+	int count = PAGE_CACHE_SIZE;
+	int end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+	int err, offset;
+
+	base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;
+
+	/* If we are entirely outside the file, then return an error */
+	err = -EIO;
+	offset = inode->i_size & (PAGE_CACHE_SIZE-1);
+	if (page->index > end_index ||
+	    ((page->index == end_index) && !offset))
+		goto out_unlock;
+
+	err = -ENOMEM;
+	wp = kmalloc(sizeof(*wp), GFP_KERNEL);
+	if(wp == NULL)
+		goto out_unlock;
+
+	*wp = ((struct wp_info) { .page		= page,
+				  .count	= count,
+				  .start	= base,
+				  .size		= inode->i_size,
+				  .truncate	= ops->truncate_file,
+				  .ei		= EXTERNFS_I(inode) });
+
+	buffer = kmap(page);
+	err = (*ops->write_file)(EXTERNFS_I(inode), base, buffer, 0,
+				 count, externfs_finish_writepage, wp);
+
+	return err;
+
+ out_unlock:
+	unlock_page(page);
+	return err;
+}
+
+static void externfs_finish_readpage(char *buffer, int res, void *arg)
+{
+	struct page *page = arg;
+	struct inode *inode;
+
+	if(res < 0){
+		SetPageError(page);
+		goto out;
+	}
+
+	inode = page->mapping->host;
+	if(inode->i_size >> PAGE_CACHE_SHIFT == page->index)
+		res = inode->i_size % PAGE_CACHE_SIZE;
+
+	memset(&buffer[res], 0, PAGE_CACHE_SIZE - res);
+
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	if (PageError(page))
+		ClearPageError(page);
+ out:
+	kunmap(page);
+	unlock_page(page);
+}
+
+static int externfs_readpage(struct file *file, struct page *page)
+{
+	struct inode *ino = page->mapping->host;
+	struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+	char *buffer;
+	long long start;
+	int err = 0;
+
+	start = (long long) page->index << PAGE_CACHE_SHIFT;
+	buffer = kmap(page);
+
+	if(ops->map_file_page != NULL){
+		/* XXX What happens when PAGE_SIZE != PAGE_CACHE_SIZE? */
+		err = (*ops->map_file_page)(file_externfs_i(file), start,
+					    buffer,
+					    file->f_mode & FMODE_WRITE);
+		if(!err)
+			err = PAGE_CACHE_SIZE;
+	}
+	else err = (*ops->read_file)(file_externfs_i(file), start, buffer,
+				     PAGE_CACHE_SIZE, 0, 0,
+				     externfs_finish_readpage, page);
+
+	if(err > 0)
+		err = 0;
+	return err;
+}
+
+static int externfs_removepage(struct page *page, gfp_t gfpmask)
+{
+	physmem_remove_mapping(page_address(page));
+	return 0;
+}
+
+struct writepage_info {
+	struct completion completion;
+	int res;
+};
+
+static void externfs_finish_prepare(char *buffer, int res, void *arg)
+{
+	struct writepage_info *wp = arg;
+
+	wp->res = res;
+	complete(&wp->completion);
+}
+
+int externfs_prepare_write(struct file *file, struct page *page,
+			 unsigned int from, unsigned int to)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode = mapping->host;
+	struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
+	void *buffer;
+	long long start = page->index << PAGE_SHIFT;
+	int err;
+	struct writepage_info wp =
+		{ .completion	= COMPLETION_INITIALIZER_ONSTACK(wp.completion),
+		  .res		= 0 };
+
+	if(PageUptodate(page))
+		return 0;
+
+	buffer = kmap(page);
+	if(ops->map_file_page != NULL){
+		err = (*ops->map_file_page)(file_externfs_i(file), start,
+					    buffer,
+					    file->f_mode & FMODE_WRITE);
+		goto out;
+	}
+
+	err = (*ops->read_file)(file_externfs_i(file), start, buffer,
+				PAGE_CACHE_SIZE, from, to,
+				externfs_finish_prepare, &wp);
+	wait_for_completion(&wp.completion);
+	if(err < 0)
+		goto out;
+
+	err = wp.res;
+	if(err < 0){
+		if(from > 0)
+			memset(buffer, 0, from);
+		if(to < PAGE_CACHE_SIZE)
+			memset(buffer + to, 0, PAGE_CACHE_SIZE - to);
+		goto out;
+	}
+
+	SetPageUptodate(page);
+	err = 0;
+ out:
+	kunmap(page);
+	return err;
+}
+
+static int externfs_commit_write(struct file *file, struct page *page,
+				 unsigned from, unsigned to)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode = mapping->host;
+	struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
+	unsigned long long size;
+	long long start;
+	int err;
+
+	start = (((long long) page->index) << PAGE_CACHE_SHIFT);
+
+	if(ops->map_file_page != NULL)
+		err = 0;
+	else {
+		size = start + to;
+		if(size > inode->i_size){
+			inode->i_size = size;
+			mark_inode_dirty(inode);
+		}
+	}
+
+	set_page_dirty(page);
+	return 0;
+}
+
+static int externfs_file_open(struct inode *ino, struct file *file)
+{
+	struct externfs_inode *ext = EXTERNFS_I(ino);
+	struct externfs_file_ops *ops = ext->ops;
+	char *name = inode_name(ino, 0);
+	int err;
+
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+
+	err = (*ops->open_file)(ext, name, current->fsuid, current->fsgid);
+	kfree(name);
+
+	return err;
+}
+
+struct read_inode_info {
+	struct list_head list;
+	int pid;
+	char *name;
+	struct externfs_inode_info stat;
+};
+
+static DEFINE_SPINLOCK(inode_info_lock);
+static struct list_head inode_info = LIST_HEAD_INIT(inode_info);
+
+static struct file_operations externfs_file_fops = {
+	.open		= externfs_file_open,
+	.llseek		= generic_file_llseek,
+	.read		= do_sync_read,
+	.write		= do_sync_write,
+	.aio_read	= generic_file_aio_read,
+	.aio_write	= generic_file_aio_write,
+	.mmap		= generic_file_mmap,
+	.fsync		= externfs_fsync,
+};
+
+static struct file_operations externfs_dir_fops = {
+	.readdir	= externfs_readdir,
+	.read		= generic_read_dir,
+};
+
+static const struct address_space_operations externfs_aops = {
+	.writepage 	= externfs_writepage,
+	.readpage	= externfs_readpage,
+	.releasepage	= externfs_removepage,
+/* 	.set_page_dirty = __set_page_dirty_nobuffers, */
+	.prepare_write	= externfs_prepare_write,
+	.commit_write	= externfs_commit_write
+};
+
+static void externfs_read_inode(struct inode *ino)
+{
+	struct list_head *ele;
+	struct read_inode_info *info;
+	int err = -EIO, pid = os_getpid(), mode;
+
+	spin_lock(&inode_info_lock);
+	list_for_each(ele, &inode_info){
+		info = list_entry(ele, struct read_inode_info, list);
+		if(info->pid == pid){
+			spin_unlock(&inode_info_lock);
+			goto found;
+		}
+	}
+
+	spin_unlock(&inode_info_lock);
+	goto out;
+
+found:
+	copy_inode_info(ino, &info->stat);
+	err = 0;
+
+	mode = info->stat.mode;
+	if(S_ISLNK(mode))
+		ino->i_op = &page_symlink_inode_operations;
+	else if(S_ISDIR(mode))
+		ino->i_op = &externfs_dir_iops;
+	else ino->i_op = &externfs_iops;
+
+	if(S_ISDIR(mode))
+		ino->i_fop = &externfs_dir_fops;
+	else ino->i_fop = &externfs_file_fops;
+
+	if(S_ISLNK(mode))
+		ino->i_mapping->a_ops = &externfs_link_aops;
+	else ino->i_mapping->a_ops = &externfs_aops;
+
+	if(S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
+		init_special_inode(ino, mode & S_IFMT, info->stat.rdev);
+
+out:
+	if(err)
+		make_bad_inode(ino);
+}
+
+static int externfs_statfs(struct dentry *dentry, struct kstatfs *sf)
+{
+	/* do_statfs uses struct statfs64 internally, but the linux kernel
+	 * struct statfs still has 32-bit versions for most of these fields,
+	 * so we convert them here
+	 */
+	int err;
+	long long f_blocks;
+	long long f_bfree;
+	long long f_bavail;
+	long long f_files;
+	long long f_ffree;
+	struct externfs_data *ed = dentry->d_sb->s_fs_info;
+
+	err = (*ed->file_ops->statfs)(&sf->f_bsize, &f_blocks, &f_bfree,
+				      &f_bavail, &f_files, &f_ffree,
+				      &sf->f_fsid, sizeof(sf->f_fsid),
+				      &sf->f_namelen, sf->f_spare, ed);
+	if(err)
+		return err;
+
+	sf->f_blocks = f_blocks;
+	sf->f_bfree = f_bfree;
+	sf->f_bavail = f_bavail;
+	sf->f_files = f_files;
+	sf->f_ffree = f_ffree;
+	sf->f_type = EXTERNFS_SUPER_MAGIC;
+	return 0;
+}
+
+static struct inode *externfs_alloc_inode(struct super_block *sb)
+{
+	struct externfs_data *ed = sb->s_fs_info;
+	struct externfs_inode *ext;
+
+	ext = (*ed->mount_ops->init_file)(ed);
+	if(ext == NULL)
+		return NULL;
+
+	*ext = ((struct externfs_inode) { .ops	= ed->file_ops });
+
+	inode_init_once(&ext->vfs_inode);
+	return &ext->vfs_inode;
+}
+
+static void externfs_destroy_inode(struct inode *inode)
+{
+	struct externfs_inode *ext = EXTERNFS_I(inode);
+
+	(*ext->ops->close_file)(ext, inode->i_size);
+	(*ext->ops->free_file)(ext);
+}
+
+static void externfs_put_super(struct super_block *sb)
+{
+	struct externfs_data *sb_data = sb->s_fs_info;
+
+	(*sb_data->mount_ops->umount)(sb_data);
+}
+
+static const struct super_operations externfs_sbops = {
+	.alloc_inode	= externfs_alloc_inode,
+	.destroy_inode	= externfs_destroy_inode,
+	.read_inode	= externfs_read_inode,
+	.statfs		= externfs_statfs,
+	.put_super	= externfs_put_super,
+};
+
+static struct inode *do_iget(struct super_block *sb, char *name,
+			     struct read_inode_info *info)
+{
+	struct inode *inode;
+
+	INIT_LIST_HEAD(&info->list);
+	info->pid = os_getpid();
+	info->name = name;
+
+	spin_lock(&inode_info_lock);
+	list_add(&info->list, &inode_info);
+	spin_unlock(&inode_info_lock);
+
+	inode = iget(sb, info->stat.ino);
+
+	spin_lock(&inode_info_lock);
+	list_del(&info->list);
+	spin_unlock(&inode_info_lock);
+
+	return inode;
+}
+
+int externfs_create(struct inode *dir, struct dentry *dentry, int mode,
+		    struct nameidata *nd)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(dir)->ops;
+	struct read_inode_info info;
+	struct inode *inode;
+	struct externfs_data *ed = dir->i_sb->s_fs_info;
+	char *file;
+	int err;
+
+	file = inode_dentry_name(dir, dentry);
+	if(IS_ERR(file)){
+		err = PTR_ERR(file);
+		goto out;
+	}
+
+	err = (*ops->create_file)(file, mode, current->fsuid, current->fsgid,
+				  ed);
+	if(err)
+		goto out_free;
+
+	err = (*ops->stat_file)(file, ed, &info.stat);
+	if(err)
+		goto out_rm;
+
+	err = -ENOMEM;
+	inode = do_iget(dir->i_sb, file, &info);
+	if(inode == NULL)
+		goto out_rm;
+
+	err = 0;
+	dentry->d_op = &externfs_dentry_ops;
+	d_instantiate(dentry, inode);
+
+out_free:
+	kfree(file);
+out:
+	return err;
+
+out_rm:
+	(*ops->unlink_file)(file, ed);
+	goto out_free;
+}
+
+struct dentry *externfs_lookup(struct inode *ino, struct dentry *dentry,
+			       struct nameidata *nd)
+{
+	struct inode *inode = NULL;
+	struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+	struct externfs_data *ed = ino->i_sb->s_fs_info;
+	struct read_inode_info info;
+	char *name;
+	int err = -ENOMEM;
+
+	name = dentry_name(dentry, 0);
+	if(IS_ERR(name)){
+		err = PTR_ERR(name);
+		goto out;
+	}
+
+	err = (*ops->stat_file)(name, ed, &info.stat);
+	if(err && (err != -ENOENT))
+		goto out_free;
+
+	if(!err){
+		inode = do_iget(ino->i_sb, name, &info);
+		err = -ENOMEM;
+		if(inode == NULL)
+			goto out_free;
+	}
+
+	kfree(name);
+	return d_splice_alias(inode, dentry);
+
+ out_free:
+	kfree(name);
+ out:
+	return ERR_PTR(err);
+}
+
+int externfs_link(struct dentry *to, struct inode *dir, struct dentry *from)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(dir)->ops;
+	struct externfs_data *ed = dir->i_sb->s_fs_info;
+	struct inode *inode = to->d_inode;
+	char *from_name, *to_name;
+	int err;
+
+	from_name = inode_dentry_name(dir, from);
+	if(IS_ERR(from_name)){
+		err = PTR_ERR(from_name);
+		goto out;
+	}
+
+	to_name = dentry_name(to, 0);
+	if(IS_ERR(to_name)){
+		err = PTR_ERR(to_name);
+		goto out_free_from;
+	}
+
+	err = (*ops->link_file)(to_name, from_name, current->fsuid,
+				current->fsgid, ed);
+	if(err)
+		goto out_free_to;
+
+	inode->i_ctime = CURRENT_TIME_SEC;
+	inc_nlink(inode);
+
+	atomic_inc(&inode->i_count);
+	from->d_op = &externfs_dentry_ops;
+	d_instantiate(from, inode);
+out_free_to:
+	kfree(to_name);
+out_free_from:
+	kfree(from_name);
+out:
+	return err;
+}
+
+int externfs_unlink(struct inode *ino, struct dentry *dentry)
+{
+	struct inode *inode;
+	struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+	struct externfs_data *ed = ino->i_sb->s_fs_info;
+	char *file;
+	int err;
+
+	file = inode_dentry_name(ino, dentry);
+	if(IS_ERR(file))
+		return PTR_ERR(file);
+
+	inode = dentry->d_inode;
+	if((inode->i_nlink == 1) && (ops->invisible != NULL))
+		(*ops->invisible)(EXTERNFS_I(inode));
+
+	err = (*ops->unlink_file)(file, ed);
+	kfree(file);
+
+	if(!err){
+		inode->i_ctime = ino->i_ctime;
+		drop_nlink(inode);
+	}
+
+	return err;
+}
+
+static int externfs_symlink(struct inode *ino, struct dentry *dentry,
+			    const char *to)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+	struct read_inode_info info;
+	struct inode *inode;
+	struct externfs_data *ed = ino->i_sb->s_fs_info;
+	char *file;
+	int err;
+
+	file = inode_dentry_name(ino, dentry);
+	if(IS_ERR(file)){
+		err = PTR_ERR(file);
+		goto out;
+	}
+
+	err = (*ops->make_symlink)(file, to, current->fsuid, current->fsgid,
+				   ed);
+	if(err)
+		goto out_free;
+
+	err = (*ops->stat_file)(file, ed, &info.stat);
+	if(err)
+		goto out_rm;
+
+	err = -ENOMEM;
+	inode = do_iget(ino->i_sb, file, &info);
+	if(inode == NULL)
+		goto out_rm;
+
+	dentry->d_op = &externfs_dentry_ops;
+	d_instantiate(dentry, inode);
+	err = 0;
+
+ out_free:
+	kfree(file);
+ out:
+	return err;
+
+ out_rm:
+	(*ops->unlink_file)(file, ed);
+	goto out_free;
+}
+
+static int externfs_make_dir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(dir)->ops;
+	struct read_inode_info info;
+	struct inode *inode;
+	struct externfs_data *ed = dir->i_sb->s_fs_info;
+	char *file;
+	int err;
+
+	file = inode_dentry_name(dir, dentry);
+	if(IS_ERR(file)){
+		err = PTR_ERR(file);
+		goto out;
+	}
+
+	err = (*ops->make_dir)(file, mode, current->fsuid, current->fsgid, ed);
+	if(err)
+		goto out_free;
+
+	err = (*ops->stat_file)(file, ed, &info.stat);
+	if(err)
+		goto out_rm;
+
+	err = -ENOMEM;
+	inode = do_iget(dir->i_sb, file, &info);
+	if(inode == NULL)
+		goto out_rm;
+
+	/* No bumping of inode nlink since we just read that from the host */
+	inc_nlink(dir);
+
+	err = 0;
+	dentry->d_op = &externfs_dentry_ops;
+	d_instantiate(dentry, inode);
+
+ out_free:
+	kfree(file);
+ out:
+	return err;
+ out_rm:
+	(*ops->remove_dir)(file, ed);
+	goto out_free;
+}
+
+int externfs_remove_dir(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+
+	inode->i_size = 0;
+
+	/* Parent */
+	drop_nlink(inode);
+	/* . */
+	drop_nlink(inode);
+	drop_nlink(dir);
+
+	return 0;
+}
+
+int externfs_make_node(struct inode *dir, struct dentry *dentry, int mode,
+		       dev_t dev)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(dir)->ops;
+	struct externfs_data *ed = dir->i_sb->s_fs_info;
+	struct read_inode_info info;
+	struct inode *inode;
+	char *name;
+	int err;
+
+	name = dentry_name(dentry, 0);
+	if(IS_ERR(name)){
+		err = PTR_ERR(name);
+		goto out;
+	}
+
+	err = (*ops->make_node)(name, mode & S_IRWXUGO, current->fsuid,
+				current->fsgid, mode & S_IFMT, MAJOR(dev),
+				MINOR(dev), ed);
+	if(err)
+		goto out_free;
+
+	err = (*ops->stat_file)(name, ed, &info.stat);
+	if(err)
+		goto out_rm;
+
+	err = -ENOMEM;
+	inode = do_iget(dir->i_sb, name, &info);
+	if(inode == NULL)
+		goto out_rm;
+
+	err = 0;
+	init_special_inode(inode, mode, dev);
+	dentry->d_op = &externfs_dentry_ops;
+	d_instantiate(dentry, inode);
+ out_free:
+	kfree(name);
+ out:
+	return err;
+
+ out_rm:
+	(*ops->unlink_file)(name, ed);
+	goto out_free;
+}
+
+int externfs_rename(struct inode *from_dir, struct dentry *from,
+		    struct inode *to_dir, struct dentry *to)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(from_dir)->ops;
+	struct externfs_data *ed = from_dir->i_sb->s_fs_info;
+	struct inode *from_ino = from->d_inode, *to_ino = to->d_inode;
+	char *from_name, *to_name;
+	int err;
+
+	from_name = inode_dentry_name(from_dir, from);
+	if(IS_ERR(from_name)){
+		err = PTR_ERR(from_name);
+		goto out;
+	}
+
+	to_name = inode_dentry_name(to_dir, to);
+	if(IS_ERR(to_name)){
+		err = PTR_ERR(to_name);
+		goto out_free_from;
+	}
+
+	err = (*ops->rename_file)(from_name, to_name, ed);
+	if(err)
+		goto out_free_to;
+
+	inc_nlink(from_ino);
+	if(to_ino != NULL){
+		drop_nlink(to_ino);
+		to_ino->i_ctime = CURRENT_TIME;
+	}
+	drop_nlink(from_ino);
+
+	drop_nlink(from_dir);
+	inc_nlink(to_dir);
+
+out_free_to:
+	kfree(to_name);
+ out_free_from:
+	kfree(from_name);
+ out:
+
+	return err;
+}
+
+void externfs_truncate(struct inode *inode)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
+
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+	(*ops->truncate_file)(EXTERNFS_I(inode), inode->i_size);
+}
+
+int externfs_permission(struct inode *ino, int desired, struct nameidata *nd)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+	struct externfs_data *ed = ino->i_sb->s_fs_info;
+	char *name;
+	int r = 0, w = 0, x = 0, err = 0;
+
+	if(ops->access_file != NULL){
+		if(desired & MAY_READ) r = 1;
+		if(desired & MAY_WRITE) w = 1;
+		if(desired & MAY_EXEC) x = 1;
+		name = inode_name(ino, 0);
+		if(IS_ERR(name))
+			return PTR_ERR(name);
+
+		err = (*ops->access_file)(name, r, w, x, current->fsuid,
+					  current->fsgid, ed);
+		kfree(name);
+	}
+
+	if(!err)
+		err = generic_permission(ino, desired, NULL);
+	return err;
+}
+
+/* This is a copy of ext2_sync_inode */
+int externfs_sync_inode(struct inode *inode)
+{
+	struct writeback_control wbc = {
+		.sync_mode = WB_SYNC_ALL,
+		.nr_to_write = 0,	/* sys_fsync did this */
+	};
+	return sync_inode(inode, &wbc);
+}
+
+int externfs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	struct externfs_file_ops *ops = EXTERNFS_I(dentry->d_inode)->ops;
+	struct externfs_data *ed = dentry->d_inode->i_sb->s_fs_info;
+	struct externfs_iattr attrs;
+	char *name;
+	int err;
+
+	attrs.ia_valid = 0;
+	if(attr->ia_valid & ATTR_MODE){
+		attrs.ia_valid |= EXTERNFS_ATTR_MODE;
+		attrs.ia_mode = attr->ia_mode;
+	}
+	if(attr->ia_valid & ATTR_UID){
+		attrs.ia_valid |= EXTERNFS_ATTR_UID;
+		attrs.ia_uid = attr->ia_uid;
+	}
+	if(attr->ia_valid & ATTR_GID){
+		attrs.ia_valid |= EXTERNFS_ATTR_GID;
+		attrs.ia_gid = attr->ia_gid;
+	}
+	if(attr->ia_valid & ATTR_SIZE){
+		attrs.ia_valid |= EXTERNFS_ATTR_SIZE;
+		attrs.ia_size = attr->ia_size;
+	}
+	if(attr->ia_valid & ATTR_ATIME){
+		attrs.ia_valid |= EXTERNFS_ATTR_ATIME;
+		attrs.ia_atime = attr->ia_atime.tv_sec;
+	}
+	if(attr->ia_valid & ATTR_MTIME){
+		attrs.ia_valid |= EXTERNFS_ATTR_MTIME;
+		attrs.ia_mtime = attr->ia_mtime.tv_sec;
+	}
+	if(attr->ia_valid & ATTR_CTIME){
+		attrs.ia_valid |= EXTERNFS_ATTR_CTIME;
+		attrs.ia_ctime = attr->ia_ctime.tv_sec;
+	}
+	if(attr->ia_valid & ATTR_ATIME_SET){
+		attrs.ia_valid |= EXTERNFS_ATTR_ATIME_SET;
+		attrs.ia_atime = attr->ia_atime.tv_sec;
+	}
+	if(attr->ia_valid & ATTR_MTIME_SET){
+		attrs.ia_valid |= EXTERNFS_ATTR_MTIME_SET;
+	}
+	name = dentry_name(dentry, 0);
+	if(IS_ERR(name))
+		return PTR_ERR(name);
+
+	err = (*ops->set_attr)(name, &attrs, ed);
+	kfree(name);
+	if(err)
+		return err;
+
+	/* This marks the inode dirty and there seems to be nothing I
+	 * can do about it except sync it by hand afterwards.
+	 */
+	err = inode_setattr(dentry->d_inode, attr);
+	if(err)
+		return err;
+
+	/* Get the inode marked clean (because the host is already
+	 * synced up) so that it will be freed when i_count goes to
+	 * zero and that, when after this file is deleted and a new
+	 * file is created with the same inode number, it doesn't
+	 * reuse this inode.
+	 */
+	err = externfs_sync_inode(dentry->d_inode);
+
+	return err;
+}
+
+int externfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+		     struct kstat *stat)
+{
+	generic_fillattr(dentry->d_inode, stat);
+	return 0;
+}
+
+static const struct inode_operations externfs_iops = {
+	.create		= externfs_create,
+	.link		= externfs_link,
+	.unlink		= externfs_unlink,
+	.symlink	= externfs_symlink,
+	.mkdir		= externfs_make_dir,
+	.rmdir		= externfs_remove_dir,
+	.mknod		= externfs_make_node,
+	.rename		= externfs_rename,
+	.truncate	= externfs_truncate,
+	.permission	= externfs_permission,
+	.setattr	= externfs_setattr,
+	.getattr	= externfs_getattr,
+};
+
+static const struct inode_operations externfs_dir_iops = {
+	.create		= externfs_create,
+	.lookup		= externfs_lookup,
+	.link		= externfs_link,
+	.unlink		= externfs_unlink,
+	.symlink	= externfs_symlink,
+	.mkdir		= externfs_make_dir,
+	.rmdir		= externfs_remove_dir,
+	.mknod		= externfs_make_node,
+	.rename		= externfs_rename,
+	.truncate	= externfs_truncate,
+	.permission	= externfs_permission,
+	.setattr	= externfs_setattr,
+	.getattr	= externfs_getattr,
+};
+
+int externfs_link_readpage(struct file *file, struct page *page)
+{
+	struct inode *ino = page->mapping->host;
+	struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+	struct externfs_data *ed = ino->i_sb->s_fs_info;
+	char *buffer, *name;
+	int err;
+
+	buffer = kmap(page);
+
+	name = inode_name(ino, 0);
+	if(IS_ERR(name)){
+		err = PTR_ERR(name);
+		goto out;
+	}
+
+	err = (*ops->read_link)(name, buffer, PAGE_CACHE_SIZE, ed);
+
+	kfree(name);
+	if(err == PAGE_CACHE_SIZE)
+		err = -E2BIG;
+	else if(err > 0){
+		flush_dcache_page(page);
+		SetPageUptodate(page);
+		if (PageError(page))
+			ClearPageError(page);
+		err = 0;
+	}
+ out:
+	kunmap(page);
+	unlock_page(page);
+	return err;
+}
+
+struct externfs_data *inode_externfs_info(struct inode *inode)
+{
+	return inode->i_sb->s_fs_info;
+}
+
+static const struct address_space_operations externfs_link_aops = {
+	.readpage	= externfs_link_readpage,
+	.releasepage	= externfs_removepage,
+};
+
+DECLARE_MUTEX(externfs_sem);
+struct list_head externfses = LIST_HEAD_INIT(externfses);
+
+static struct externfs *find_externfs(struct file_system_type *type)
+{
+	struct list_head *ele;
+	struct externfs *fs;
+
+	down(&externfs_sem);
+	list_for_each(ele, &externfses){
+		fs = list_entry(ele, struct externfs, list);
+		if(&fs->type == type)
+			goto out;
+	}
+	fs = NULL;
+ out:
+	up(&externfs_sem);
+	return fs;
+}
+
+#define DEFAULT_ROOT "/"
+
+char *host_root_filename(char *mount_arg)
+{
+	char *root = DEFAULT_ROOT;
+
+	if((mount_arg != NULL) && (*mount_arg != '\0'))
+		root = mount_arg;
+
+	return uml_strdup(root);
+}
+
+struct list_head *watch_list = NULL;
+
+static int externfs_fill_sb(struct super_block *sb, void *data, int silent)
+{
+	struct externfs *fs;
+	struct inode *root_inode;
+	struct externfs_data *sb_data;
+	struct read_inode_info info;
+	int err = -EINVAL;
+
+	sb->s_blocksize = 1024;
+	sb->s_blocksize_bits = 10;
+	sb->s_magic = EXTERNFS_SUPER_MAGIC;
+	sb->s_op = &externfs_sbops;
+
+	fs = find_externfs(sb->s_type);
+	if(fs == NULL){
+		printk("Couldn't find externfs for filesystem '%s'\n",
+		       sb->s_type->name);
+		goto out;
+	}
+
+	/* XXX Does this allocate anything which needs freeing on later
+	 * failure?
+	 */
+	sb_data = (*fs->mount_ops->mount)(data);
+	if(IS_ERR(sb_data)){
+		err = PTR_ERR(sb_data);
+		goto out;
+	}
+
+	sb->s_fs_info = sb_data;
+	sb_data->mount_ops = fs->mount_ops;
+
+	err = (*sb_data->file_ops->stat_file)("/", sb_data, &info.stat);
+	if(err)
+		goto out_umount;
+
+	root_inode = do_iget(sb, "/", &info);
+	if(root_inode == NULL)
+		goto out_umount;
+
+	err = -ENOMEM;
+	sb->s_root = d_alloc_root(root_inode);
+	if(sb->s_root == NULL)
+		goto out_put;
+
+	err = 0;
+ out:
+	return err;
+
+ out_put:
+	iput(root_inode);
+ out_umount:
+	(*fs->mount_ops->umount)(sb_data);
+	goto out;
+}
+
+static int externfs_read_super(struct file_system_type *type, int flags,
+			       const char *dev_name, void *data,
+			       struct vfsmount *mnt)
+{
+	return get_sb_nodev(type, flags, data, externfs_fill_sb, mnt);
+}
+
+void init_externfs(struct externfs_data *ed, struct externfs_file_ops *ops)
+{
+	ed->file_ops = ops;
+}
+
+int register_externfs(char *name, struct externfs_mount_ops *mount_ops)
+{
+	struct externfs *new;
+	int err = -ENOMEM;
+
+	new = kmalloc(sizeof(*new), GFP_KERNEL);
+	if(new == NULL)
+		goto out;
+
+	memset(new, 0, sizeof(*new));
+	*new = ((struct externfs) { .list	= LIST_HEAD_INIT(new->list),
+				    .mount_ops	= mount_ops,
+				    .type = { .name	= name,
+					      .get_sb	= externfs_read_super,
+					      .kill_sb	= kill_anon_super,
+					      .fs_flags	= 0,
+					      .owner	= THIS_MODULE } });
+	list_add(&new->list, &externfses);
+
+	err = register_filesystem(&new->type);
+	if(err)
+		goto out_del;
+	return 0;
+
+ out_del:
+	list_del(&new->list);
+	kfree(new);
+ out:
+	return err;
+}
+
+void unregister_externfs(char *name)
+{
+	struct list_head *ele;
+	struct externfs *fs;
+
+	down(&externfs_sem);
+	list_for_each(ele, &externfses){
+		fs = list_entry(ele, struct externfs, list);
+		if(!strcmp(fs->type.name, name)){
+			list_del(ele);
+			kfree(fs);
+			up(&externfs_sem);
+			return;
+		}
+	}
+	up(&externfs_sem);
+	printk("Unregister_externfs - filesystem '%s' not found\n", name);
+}
Index: linux-2.6.17/fs/externfs/host_file.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/fs/externfs/host_file.c	2007-11-19 17:19:40.000000000 -0500
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/string.h"
+#include "linux/errno.h"
+#include "linux/types.h"
+#include "linux/slab.h"
+#include "linux/fs.h"
+#include "asm/fcntl.h"
+#include "hostfs.h"
+#include "filehandle.h"
+
+#if 0
+extern int append;
+#endif
+
+char *get_path(const char *path[], char *buf, int size)
+{
+	const char **s;
+	char *p;
+	int new = 1;
+
+	for(s = path; *s != NULL; s++){
+		new += strlen(*s);
+		if((*(s + 1) != NULL) && (strlen(*s) > 0) &&
+		   ((*s)[strlen(*s) - 1] != '/'))
+			new++;
+	}
+
+	if(new > size){
+		buf = kmalloc(new, GFP_KERNEL);
+		if(buf == NULL)
+			return NULL;
+	}
+
+	p = buf;
+	for(s = path; *s != NULL; s++){
+		strcpy(p, *s);
+		p += strlen(*s);
+		if((*(s + 1) != NULL) && (strlen(*s) > 0) &&
+		   ((*s)[strlen(*s) - 1] != '/'))
+			strcpy(p++, "/");
+	}
+
+	return buf;
+}
+
+void free_path(const char *buf, char *tmp)
+{
+	if((buf != tmp) && (buf != NULL))
+		kfree((char *) buf);
+}
+
+int host_open_file(const char *path[], int r, int w, int append,
+		   struct file_handle *fh, char *(*get_name)(void *arg),
+		   void *arg)
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int mode = 0, err;
+	struct openflags flags = OPENFLAGS();
+
+	if (r)
+		flags = of_read(flags);
+	if (w)
+		flags = of_write(flags);
+	if(append)
+		flags = of_append(flags);
+
+	err = -ENOMEM;
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = open_filehandle(file, flags, mode, fh);
+	if(err)
+		goto out_free;
+
+	is_reclaimable(fh, get_name, arg);
+
+ out_free:
+	free_path(file, tmp);
+ out:
+	return err;
+}
+
+void *host_open_dir(const char *path[])
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	void *dir = ERR_PTR(-ENOMEM);
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	dir = open_dir(file);
+ out:
+	free_path(file, tmp);
+	return dir;
+}
+
+char *host_read_dir(void *stream, unsigned long long *pos,
+		    unsigned long long *ino_out, int *len_out)
+{
+	int err;
+	char *name;
+
+	err = os_seek_dir(stream, *pos);
+	if(err)
+		return ERR_PTR(err);
+
+	err = os_read_dir(stream, ino_out, &name);
+	if(err)
+		return ERR_PTR(err);
+
+	if(name == NULL)
+		return NULL;
+
+	*len_out = strlen(name);
+	*pos = os_tell_dir(stream);
+	return name;
+}
+
+int host_file_type(const char *path[], int *rdev)
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+ 	struct uml_stat buf;
+	int ret;
+
+	ret = -ENOMEM;
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	if(rdev != NULL){
+		ret = os_lstat_file(file, &buf);
+		if(ret)
+			goto out;
+		*rdev = MKDEV(buf.ust_rmajor, buf.ust_rminor);
+	}
+
+	ret = os_file_type(file);
+ out:
+	free_path(file, tmp);
+	return ret;
+}
+
+int host_create_file(const char *path[], int mode)
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int fd, err = -ENOMEM;
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	fd = open_file(file, of_create(of_rdwr(OPENFLAGS())), mode);
+	if(fd < 0){
+		err = fd;
+		goto out_free;
+	}
+	os_close_file(fd);
+	err = 0;
+
+ out_free:
+	free_path(file, tmp);
+ out:
+	return err;
+}
+
+static int do_stat_file(const char *path, struct externfs_inode_info *info_out)
+{
+	struct uml_stat buf;
+	int err;
+
+	err = os_lstat_file(path, &buf);
+	if(err < 0)
+		return err;
+
+	*info_out = ((struct externfs_inode_info)
+	            { .atime 		= buf.ust_atime,
+		      .mtime 		= buf.ust_mtime,
+		      .ctime 		= buf.ust_ctime,
+		      .uid		= buf.ust_uid,
+		      .gid		= buf.ust_gid,
+		      .ino		= buf.ust_ino,
+		      .rdev		= MKDEV(buf.ust_rmajor,
+						buf.ust_rminor),
+		      .mode		= buf.ust_mode,
+		      .nlink		= buf.ust_nlink,
+		      .size		= buf.ust_size,
+		      .blksize		= buf.ust_blksize,
+		      .blocks		= buf.ust_blocks });
+
+	return 0;
+}
+
+int host_stat_file(const char *path[], struct externfs_inode_info *info_out)
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int err;
+
+	err = -ENOMEM;
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = do_stat_file(file, info_out);
+ out:
+	free_path(file, tmp);
+	return err;
+}
+
+int host_set_attr(const char *path[], struct externfs_iattr *attrs)
+{
+	struct externfs_inode_info info;
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int err = 0, ma;
+
+#if 0
+	if(append && (attrs->ia_valid & EXTERNFS_ATTR_SIZE))
+		return -EPERM;
+#endif
+
+	err = -ENOMEM;
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	if(attrs->ia_valid & EXTERNFS_ATTR_MODE){
+		err = os_set_file_perms(file, attrs->ia_mode);
+		if(err < 0)
+			goto out;
+	}
+	if(attrs->ia_valid & EXTERNFS_ATTR_UID){
+		err = os_set_file_owner(file, attrs->ia_uid, -1);
+		if(err < 0)
+			goto out;
+	}
+	if(attrs->ia_valid & EXTERNFS_ATTR_GID){
+		err = os_set_file_owner(file, -1, attrs->ia_gid);
+		if(err < 0)
+			goto out;
+	}
+	if(attrs->ia_valid & EXTERNFS_ATTR_SIZE){
+		err = os_truncate_file(file, attrs->ia_size);
+		if(err < 0)
+			goto out;
+	}
+	ma = EXTERNFS_ATTR_ATIME_SET | EXTERNFS_ATTR_MTIME_SET;
+	if((attrs->ia_valid & ma) == ma){
+		err = os_set_file_time(file, attrs->ia_atime, attrs->ia_mtime);
+		if(err)
+			goto out;
+	}
+	else {
+		if(attrs->ia_valid & EXTERNFS_ATTR_ATIME_SET){
+			err = do_stat_file(file, &info);
+			if(err != 0)
+				goto out;
+
+			err = os_set_file_time(file, attrs->ia_atime,
+					       info.atime);
+			if(err)
+				goto out;
+		}
+		if(attrs->ia_valid & EXTERNFS_ATTR_MTIME_SET){
+			err = do_stat_file(file, &info);
+			if(err != 0)
+				goto out;
+
+			err = os_set_file_time(file, info.mtime,
+					       attrs->ia_mtime);
+			if(err)
+				goto out;
+		}
+	}
+	if(attrs->ia_valid & EXTERNFS_ATTR_CTIME)
+		;
+	if(attrs->ia_valid & (EXTERNFS_ATTR_ATIME | EXTERNFS_ATTR_MTIME)){
+		err = do_stat_file(file, &info);
+		if(err != 0)
+			goto out;
+
+		attrs->ia_atime = info.atime;
+		attrs->ia_mtime = info.mtime;
+	}
+
+	err = 0;
+ out:
+	free_path(file, tmp);
+	return err;
+}
+
+int host_make_symlink(const char *from[], const char *to)
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int err = -ENOMEM;
+
+	file = get_path(from, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = os_make_symlink(to, file);
+ out:
+	free_path(file, tmp);
+	return err;
+}
+
+int host_unlink_file(const char *path[])
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int err = -ENOMEM;
+
+#if 0
+	if(append)
+		return -EPERM;
+#endif
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = os_remove_file(file);
+ out:
+	free_path(file, tmp);
+	return err;
+}
+
+int host_make_dir(const char *path[], int mode)
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int err = -ENOMEM;
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = os_make_dir(file, mode);
+ out:
+	free_path(file, tmp);
+	return err;
+}
+
+int host_remove_dir(const char *path[])
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int err = -ENOMEM;
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = os_remove_dir(file);
+ out:
+	free_path(file, tmp);
+	return err;
+}
+
+int host_link_file(const char *to[], const char *from[])
+{
+	char from_tmp[HOSTFS_BUFSIZE], *f, to_tmp[HOSTFS_BUFSIZE], *t;
+	int err = -ENOMEM;
+
+	f = get_path(from, from_tmp, sizeof(from_tmp));
+	t = get_path(to, to_tmp, sizeof(to_tmp));
+	if((f == NULL) || (t == NULL))
+		goto out;
+
+	err = os_link_file(t, f);
+ out:
+	free_path(f, from_tmp);
+	free_path(t, to_tmp);
+	return err;
+}
+
+int host_read_link(const char *path[], char *buf, int size)
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int n = -ENOMEM;
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	n = os_read_symlink(file, buf, size);
+	if(n < size)
+		buf[n] = '\0';
+
+ out:
+	free_path(file, tmp);
+	return n;
+}
+
+int host_rename_file(const char *from[], const char *to[])
+{
+	char from_tmp[HOSTFS_BUFSIZE], *f, to_tmp[HOSTFS_BUFSIZE], *t;
+	int err = -ENOMEM;
+
+	f = get_path(from, from_tmp, sizeof(from_tmp));
+	t = get_path(to, to_tmp, sizeof(to_tmp));
+	if((f == NULL) || (t == NULL))
+		goto out;
+
+	err = os_move_file(f, t);
+ out:
+	free_path(f, from_tmp);
+	free_path(t, to_tmp);
+	return err;
+}
+
+int host_stat_fs(const char *path[], long *bsize_out, long long *blocks_out,
+		 long long *bfree_out, long long *bavail_out,
+		 long long *files_out, long long *ffree_out, void *fsid_out,
+		 int fsid_size, long *namelen_out, long *spare_out)
+{
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int err = -ENOMEM;
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = os_stat_filesystem(file, bsize_out, blocks_out, bfree_out,
+				 bavail_out, files_out, ffree_out, fsid_out,
+				 fsid_size, namelen_out, spare_out);
+ out:
+	free_path(file, tmp);
+	return err;
+}
+
+char *generic_host_read_dir(void *stream, unsigned long long *pos,
+			    unsigned long long *ino_out, int *len_out,
+			    void *mount)
+{
+	return host_read_dir(stream, pos, ino_out, len_out);
+}
+
+int generic_host_truncate_file(struct file_handle *fh, __u64 size, void *m)
+{
+	return truncate_file(fh, size);
+}
+
Index: linux-2.6.17/fs/externfs/host_fs.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/fs/externfs/host_fs.c	2007-11-19 17:19:40.000000000 -0500
@@ -0,0 +1,590 @@
+/*
+ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/string.h"
+#include "linux/types.h"
+#include "linux/errno.h"
+#include "linux/slab.h"
+#include "linux/list.h"
+#include "linux/init.h"
+#include "linux/fs.h"
+#include "linux/stat.h"
+#include "hostfs.h"
+#include "kern.h"
+#include "init.h"
+#include "kern_util.h"
+#include "mconsole_kern.h"
+#include "filehandle.h"
+#include "os.h"
+
+/* Changed in hostfs_args before the kernel starts running */
+static char *jail_dir = "/";
+int append = 0;
+
+static int hostfs_args(char *options, int *add)
+{
+	char *ptr;
+
+	ptr = strchr(options, ',');
+	if(ptr != NULL)
+		*ptr++ = '\0';
+	if(*options != '\0')
+		jail_dir = options;
+
+	options = ptr;
+	while(options){
+		ptr = strchr(options, ',');
+		if(ptr != NULL)
+			*ptr++ = '\0';
+		if(*options != '\0'){
+			if(!strcmp(options, "append"))
+				append = 1;
+			else printf("hostfs_args - unsupported option - %s\n",
+				    options);
+		}
+		options = ptr;
+	}
+	return 0;
+}
+
+/* XXX Have to do some locking here */
+
+static int hostfs_config(char *str, char **error_out)
+{
+	str++;
+	str = uml_strdup(str);
+	if(str == NULL){
+		*error_out = "Failed to strdup hostfs config string";
+		return -ENOMEM;
+	}
+	else return hostfs_args(str, NULL);
+}
+
+static int hostfs_get_config(char *name, char *str, int size, char **error_out)
+{
+	char *last;
+	int len = 0;
+
+	CONFIG_CHUNK(str, size, len, jail_dir, 0);
+	last = append ? ",append" : "";
+	CONFIG_CHUNK(str, size, len, last, 1);
+
+	return len;
+}
+
+static struct mc_device hostfs_mc = {
+	.list		= LIST_HEAD_INIT(hostfs_mc.list),
+	.name		= "hostfs",
+	.config		= hostfs_config,
+ 	.get_config	= hostfs_get_config,
+};
+
+static int hostfs_mc_init(void)
+{
+	mconsole_register_dev(&hostfs_mc);
+	return 0;
+}
+
+__initcall(hostfs_mc_init);
+
+__uml_setup("hostfs=", hostfs_args,
+"hostfs=<root dir>,<flags>,...\n"
+"    This is used to set hostfs parameters.  The root directory argument\n"
+"    is used to confine all hostfs mounts to within the specified directory\n"
+"    tree on the host.  If this isn't specified, then a user inside UML can\n"
+"    mount anything on the host that's accessible to the user that's running\n"
+"    it.\n"
+"    The only flag currently supported is 'append', which specifies that all\n"
+"    files opened by hostfs will be opened in append mode.\n\n"
+);
+
+
+
+struct hostfs_data {
+	struct externfs_data ext;
+	char *mount;
+};
+
+struct hostfs_file {
+	struct externfs_inode ext;
+	struct file_handle fh;
+	struct hostfs_data *mount;
+};
+
+static int hostfs_access_file(char *file, int uid, int w, int x, int gid,
+			      int r, struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+	char tmp[HOSTFS_BUFSIZE];
+	int err, mode = 0;
+
+	if(r) mode = OS_ACC_R_OK;
+	if(w) mode |= OS_ACC_W_OK;
+	if(x) mode |= OS_ACC_X_OK;
+
+	err = -ENOMEM;
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = os_access(file, mode);
+	free_path(file, tmp);
+ out:
+	return err;
+}
+
+static int hostfs_make_node(const char *file, int mode, int uid, int gid,
+			    int type, int major, int minor,
+			    struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+	char tmp[HOSTFS_BUFSIZE];
+	int err = -ENOMEM;
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	/* XXX Pass type in an OS-independent way */
+	mode |= type;
+
+	err = os_make_dev(file, mode, major, minor);
+	free_path(file, tmp);
+ out:
+	return err;
+}
+
+static int hostfs_stat_file(const char *file, struct externfs_data *ed,
+			    struct externfs_inode_info *info_out)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_stat_file(path, info_out);
+}
+
+static int hostfs_file_type(const char *file, int *rdev,
+			    struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_file_type(path, rdev);
+}
+
+static char *hostfs_name(void *arg)
+{
+	struct externfs_inode *ext = arg;
+	struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+
+	return externfs_name_prefix(ext, hf->mount->mount);
+}
+
+static struct externfs_inode *hostfs_init_file(struct externfs_data *ed)
+{
+	struct hostfs_data *hd = container_of(ed, struct hostfs_data, ext);
+	struct hostfs_file *hf;
+
+	hf = kmalloc(sizeof(*hf), GFP_KERNEL);
+	if(hf == NULL)
+		return NULL;
+
+	init_filehandle(&hf->fh, -ENOENT, OPENFLAGS());
+	hf->mount = hd;
+
+	return &hf->ext;
+}
+
+static int hostfs_open_file(struct externfs_inode *ext, char *file,
+			    int uid, int gid)
+{
+	struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+	char *mount = hf->mount->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+	int err;
+
+	if(hf->fh.fd >= 0)
+		return 0;
+
+	err = host_open_file(path, 1, 1, append, &hf->fh, hostfs_name, ext);
+	if(err == -EISDIR)
+		goto out;
+
+	if(err == -EACCES)
+		err = host_open_file(path, 1, 0, append, &hf->fh, hostfs_name,
+				     ext);
+ out:
+	return err;
+}
+
+static void *hostfs_open_dir(char *file, int uid, int gid,
+			     struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_open_dir(path);
+}
+
+static void hostfs_close_dir(void *stream, struct externfs_data *ed)
+{
+	os_close_dir(stream);
+}
+
+static char *hostfs_read_dir(void *stream, unsigned long long *pos,
+			     unsigned long long *ino_out, int *len_out,
+			     struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+
+	return generic_host_read_dir(stream, pos, ino_out, len_out, mount);
+}
+
+static int hostfs_read_file(struct externfs_inode *ext,
+			    unsigned long long offset, char *buf, int len,
+			    int ignore_start, int ignore_end,
+			    void (*completion)(char *, int, void *), void *arg)
+{
+	struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+	int err = 0;
+
+	if(ignore_start != 0){
+		err = read_file(&hf->fh, offset, buf, ignore_start);
+		if(err < 0)
+			goto out;
+	}
+
+	if(ignore_end != len)
+		err = read_file(&hf->fh, offset + ignore_end, buf + ignore_end,
+				len - ignore_end);
+
+ out:
+
+	(*completion)(buf, err, arg);
+	if (err > 0)
+		err = 0;
+	return err;
+}
+
+static int hostfs_write_file(struct externfs_inode *ext,
+			     unsigned long long offset, const char *buf,
+			     int start, int len,
+			     void (*completion)(char *, int, void *),
+			     void *arg)
+{
+	struct file_handle *fh;
+	int err;
+
+	fh = &container_of(ext, struct hostfs_file, ext)->fh;
+	err = write_file(fh, offset + start, buf + start, len);
+
+	(*completion)((char *) buf, err, arg);
+	if (err > 0)
+		err = 0;
+
+	return err;
+}
+
+static int hostfs_create_file(const char *file, int mode, int uid, int gid,
+			      struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_create_file(path, mode);
+}
+
+static int hostfs_set_attr(const char *file, struct externfs_iattr *attrs,
+			   struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_set_attr(path, attrs);
+}
+
+static int hostfs_make_symlink(const char *from, const char *to, int uid,
+			       int gid, struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, from, NULL };
+
+	return host_make_symlink(path, to);
+}
+
+static int hostfs_link_file(const char *to, const char *from, int uid, int gid,
+			    struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *to_path[] = { jail_dir, mount, to, NULL };
+	const char *from_path[] = { jail_dir, mount, from, NULL };
+
+	return host_link_file(to_path, from_path);
+}
+
+static int hostfs_unlink_file(const char *file, struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_unlink_file(path);
+}
+
+static int hostfs_make_dir(const char *file, int mode, int uid, int gid,
+			   struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_make_dir(path, mode);
+}
+
+static int hostfs_remove_dir(const char *file, struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_remove_dir(path);
+}
+
+static int hostfs_read_link(char *file, char *buf, int size,
+			    struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, file, NULL };
+
+	return host_read_link(path, buf, size);
+}
+
+static int hostfs_rename_file(char *from, char *to, struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *to_path[] = { jail_dir, mount, to, NULL };
+	const char *from_path[] = { jail_dir, mount, from, NULL };
+
+	return host_rename_file(from_path, to_path);
+}
+
+static int hostfs_stat_fs(long *bsize_out, long long *blocks_out,
+			  long long *bfree_out, long long *bavail_out,
+			  long long *files_out, long long *ffree_out,
+			  void *fsid_out, int fsid_size, long *namelen_out,
+			  long *spare_out, struct externfs_data *ed)
+{
+	char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+	const char *path[] = { jail_dir, mount, NULL };
+
+	return host_stat_fs(path, bsize_out, blocks_out, bfree_out, bavail_out,
+			    files_out, ffree_out, fsid_out, fsid_size,
+			    namelen_out, spare_out);
+}
+
+static void hostfs_close_file(struct externfs_inode *ext,
+                              unsigned long long size)
+{
+	struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+
+	if(hf->fh.fd == -ENOENT){
+		if(!list_empty(&hf->fh.list))
+			printk("Non-empty list\n");
+		return;
+	}
+
+	truncate_file(&hf->fh, size);
+	close_file(&hf->fh);
+}
+
+static void hostfs_free_file(struct externfs_inode *ext)
+{
+	struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+
+	kfree(hf);
+}
+
+static int hostfs_fsync_file(struct externfs_inode *ext, int datasync)
+{
+	struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+
+	return fsync_file(&hf->fh, datasync);
+}
+
+static int hostfs_truncate_file(struct externfs_inode *ext, __u64 size)
+{
+	struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+
+	return truncate_file(&hf->fh, size);
+}
+
+static struct externfs_file_ops hostfs_file_ops = {
+	.stat_file		= hostfs_stat_file,
+	.file_type		= hostfs_file_type,
+	.access_file		= hostfs_access_file,
+	.open_file		= hostfs_open_file,
+	.open_dir		= hostfs_open_dir,
+	.read_dir		= hostfs_read_dir,
+	.read_file		= hostfs_read_file,
+	.write_file		= hostfs_write_file,
+	.map_file_page		= NULL,
+	.close_file		= hostfs_close_file,
+	.free_file		= hostfs_free_file,
+	.close_dir		= hostfs_close_dir,
+	.invisible		= NULL,
+	.create_file		= hostfs_create_file,
+	.set_attr		= hostfs_set_attr,
+	.make_symlink		= hostfs_make_symlink,
+	.unlink_file		= hostfs_unlink_file,
+	.make_dir		= hostfs_make_dir,
+	.remove_dir		= hostfs_remove_dir,
+	.make_node		= hostfs_make_node,
+	.link_file		= hostfs_link_file,
+	.read_link		= hostfs_read_link,
+	.rename_file		= hostfs_rename_file,
+	.statfs			= hostfs_stat_fs,
+	.fsync_file		= hostfs_fsync_file,
+	.truncate_file		= hostfs_truncate_file
+};
+
+#define SAME_INO(a, b) (((a).ust_ino == (b).ust_ino) && \
+			((a).ust_major == (b).ust_major) && \
+			((a).ust_minor == (b).ust_minor))
+
+/* This does a careful check of whether it's being asked to mount something
+ * that's outside the hostfs jail.  It does so by cd-ing to the requested
+ * directory and "cd .." until it either reaches / or the jail directory.
+ * If it reaches /, then we were outside the jail and we return -EPERM.
+ * I do things this way rather than trying to examine the passed-in root
+ * to avoid having to parse the string for instances of ".." and the like.
+ * Examining the string also doesn't help with symlinks that point outside
+ * the jail.  Plus, if there's no parsing, there's no parser to try to exploit.
+ */
+static int escaping_jail(char *root)
+{
+	struct uml_stat jail_inode;
+	struct uml_stat last_dir, cur_dir;
+	int save_pwd, err, escaping = -EPERM;
+	const char *path[] = { jail_dir, root, NULL };
+	char tmp[HOSTFS_BUFSIZE], *file;
+
+	err = os_stat_file(jail_dir, &jail_inode);
+	if(err)
+		goto out;
+
+	save_pwd = os_open_file(".", of_read(OPENFLAGS()), 0);
+	if(save_pwd < 0)
+		goto out;
+
+	file = get_path(path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out_close;
+
+	err = os_change_dir(file);
+	if(err)
+		goto out_free;
+
+	err = os_stat_file(".", &cur_dir);
+	if(err)
+		goto out_back;
+
+	do {
+		if(SAME_INO(cur_dir, jail_inode)){
+			escaping = 0;
+			break;
+		}
+
+		err = os_change_dir("..");
+		if(err)
+			goto out_back;
+
+		last_dir = cur_dir;
+
+		err = os_stat_file(".", &cur_dir);
+		if(err)
+			goto out_back;
+
+	} while(!SAME_INO(last_dir, cur_dir));
+
+	err = os_fchange_dir(save_pwd);
+	if(err)
+		goto out_close;
+
+	free_path(file, tmp);
+	os_close_file(save_pwd);
+
+	return escaping;
+
+out_back:
+	os_fchange_dir(save_pwd);
+out_free:
+	free_path(file, tmp);
+out_close:
+	os_close_file(save_pwd);
+out:
+	return err;
+}
+
+static struct externfs_data *mount_fs(char *mount_arg)
+{
+	struct hostfs_data *hd;
+	int err = -ENOMEM;
+
+	hd = kmalloc(sizeof(*hd), GFP_KERNEL);
+	if(hd == NULL)
+		goto out;
+
+	hd->mount = host_root_filename(mount_arg);
+	if(hd->mount == NULL)
+		goto out_free;
+
+	err = escaping_jail(hd->mount);
+	if(err)
+		goto out_free_mount;
+
+	init_externfs(&hd->ext, &hostfs_file_ops);
+
+	return &hd->ext;
+
+out_free_mount:
+	kfree(hd->mount);
+out_free:
+	kfree(hd);
+out:
+	return ERR_PTR(err);
+}
+
+static void hostfs_umount(struct externfs_data *ed)
+{
+	struct hostfs_data *hd = container_of(ed, struct hostfs_data, ext);
+
+	kfree(hd->mount);
+	kfree(hd);
+}
+
+static struct externfs_mount_ops hostfs_mount_ops = {
+	.init_file		= hostfs_init_file,
+	.mount			= mount_fs,
+	.umount			= hostfs_umount,
+};
+
+static int __init init_hostfs(void)
+{
+	return register_externfs("hostfs", &hostfs_mount_ops);
+}
+
+static void __exit exit_hostfs(void)
+{
+	unregister_externfs("hostfs");
+}
+
+__initcall(init_hostfs);
+__exitcall(exit_hostfs);
+
+#if 0
+module_init(init_hostfs)
+module_exit(exit_hostfs)
+MODULE_LICENSE("GPL");
+#endif
Index: linux-2.6.17/fs/externfs/hostfs.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/fs/externfs/hostfs.h	2007-11-19 17:19:40.000000000 -0500
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_FS_HOSTFS
+#define __UM_FS_HOSTFS
+
+#include "linux/fs.h"
+#include "filehandle.h"
+#include "os.h"
+
+/* These are exactly the same definitions as in fs.h, but the names are
+ * changed so that this file can be included in both kernel and user files.
+ */
+
+#define EXTERNFS_ATTR_MODE	1
+#define EXTERNFS_ATTR_UID 	2
+#define EXTERNFS_ATTR_GID 	4
+#define EXTERNFS_ATTR_SIZE	8
+#define EXTERNFS_ATTR_ATIME	16
+#define EXTERNFS_ATTR_MTIME	32
+#define EXTERNFS_ATTR_CTIME	64
+#define EXTERNFS_ATTR_ATIME_SET	128
+#define EXTERNFS_ATTR_MTIME_SET	256
+#define EXTERNFS_ATTR_FORCE	512	/* Not a change, but a change it */
+#define EXTERNFS_ATTR_ATTR_FLAG	1024
+
+struct externfs_iattr {
+	unsigned int	ia_valid;
+	mode_t		ia_mode;
+	uid_t		ia_uid;
+	gid_t		ia_gid;
+	loff_t		ia_size;
+	time_t		ia_atime;
+	time_t		ia_mtime;
+	time_t		ia_ctime;
+	unsigned int	ia_attr_flags;
+};
+
+struct externfs_inode_info {
+	unsigned long atime;
+	unsigned long ctime;
+	unsigned long mtime;
+	int uid;
+	int gid;
+	unsigned long long ino;
+	dev_t rdev;
+	int mode;
+	int nlink;
+	unsigned long long size;
+	int blksize;
+	unsigned long long blocks;
+};
+
+struct externfs_data {
+	struct externfs_file_ops *file_ops;
+	struct externfs_mount_ops *mount_ops;
+};
+
+struct externfs_inode {
+	struct inode vfs_inode;
+	struct externfs_file_ops *ops;
+};
+
+struct externfs_mount_ops {
+	struct externfs_data *(*mount)(char *mount_arg);
+	void (*umount)(struct externfs_data *mount);
+	struct externfs_inode *(*init_file)(struct externfs_data *ed);
+};
+
+struct externfs_file_ops {
+	int (*stat_file)(const char *path, struct externfs_data *ed,
+			 struct externfs_inode_info *info_out);
+	int (*file_type)(const char *path, int *rdev,
+			 struct externfs_data *ed);
+	int (*access_file)(char *path, int r, int w, int x, int uid, int gid,
+			   struct externfs_data *ed);
+	int (*open_file)(struct externfs_inode *ext, char *file,
+			 int uid, int gid);
+	void (*close_file)(struct externfs_inode *ext,
+			   unsigned long long size);
+	void (*free_file)(struct externfs_inode *ext);
+	void *(*open_dir)(char *path, int uid, int gid,
+			  struct externfs_data *ed);
+	char *(*read_dir)(void *stream, unsigned long long *pos,
+			  unsigned long long *ino_out, int *len_out,
+			  struct externfs_data *ed);
+	int (*read_file)(struct externfs_inode *ext,
+			 unsigned long long offset, char *buf, int len,
+			 int ignore_start, int ignore_end,
+			 void (*completion)(char *, int, void *), void *arg);
+	int (*write_file)(struct externfs_inode *ext,
+			  unsigned long long offset, const char *buf,
+			  int start, int len,
+			  void (*completion)(char *, int, void *), void *arg);
+	int (*map_file_page)(struct externfs_inode *ext,
+			     unsigned long long offset, char *buf, int w);
+	void (*close_dir)(void *stream, struct externfs_data *ed);
+	void (*invisible)(struct externfs_inode *ext);
+	int (*create_file)(const char *path, int mode, int uid, int gid,
+			   struct externfs_data *ed);
+	int (*set_attr)(const char *path, struct externfs_iattr *attrs,
+			struct externfs_data *ed);
+	int (*make_symlink)(const char *from, const char *to, int uid, int gid,
+			    struct externfs_data *ed);
+	int (*unlink_file)(const char *path, struct externfs_data *ed);
+	int (*make_dir)(const char *path, int mode, int uid, int gid,
+			struct externfs_data *ed);
+	int (*remove_dir)(const char *path, struct externfs_data *ed);
+	int (*make_node)(const char *path, int mode, int uid, int gid,
+			 int type, int maj, int min, struct externfs_data *ed);
+	int (*link_file)(const char *to, const char *from, int uid, int gid,
+			 struct externfs_data *ed);
+	int (*read_link)(char *path, char *buf, int size,
+			 struct externfs_data *ed);
+	int (*rename_file)(char *from, char *to, struct externfs_data *ed);
+	int (*statfs)(long *bsize_out, long long *blocks_out,
+		      long long *bfree_out, long long *bavail_out,
+		      long long *files_out, long long *ffree_out,
+		      void *fsid_out, int fsid_size, long *namelen_out,
+		      long *spare_out, struct externfs_data *ed);
+	int (*fsync_file)(struct externfs_inode *ext, int datasync);
+	int (*truncate_file)(struct externfs_inode *ext, __u64 size);
+};
+
+#define HOSTFS_BUFSIZE 64
+
+extern int register_externfs(char *name, struct externfs_mount_ops *mount_ops);
+extern void unregister_externfs(char *name);
+extern void init_externfs(struct externfs_data *ed,
+			  struct externfs_file_ops *ops);
+struct externfs_data *inode_externfs_info(struct inode *inode);
+
+extern char *generic_root_filename(char *mount_arg);
+extern void host_close_file(void *stream);
+extern int host_read_file(int fd, unsigned long long offset, char *buf,
+			  int len);
+extern int host_open_file(const char *path[], int r, int w, int append,
+			  struct file_handle *fh, char *(*get_name)(void *arg),
+			  void *arg);
+extern void *host_open_dir(const char *path[]);
+extern char *host_read_dir(void *stream, unsigned long long *pos,
+			   unsigned long long *ino_out, int *len_out);
+extern int host_file_type(const char *path[], int *rdev);
+extern char *host_root_filename(char *mount_arg);
+extern char *get_path(const char *path[], char *buf, int size);
+extern void free_path(const char *buf, char *tmp);
+extern int host_create_file(const char *path[], int mode);
+extern int host_set_attr(const char *path[], struct externfs_iattr *attrs);
+extern int host_make_symlink(const char *from[], const char *to);
+extern int host_unlink_file(const char *path[]);
+extern int host_make_dir(const char *path[], int mode);
+extern int host_remove_dir(const char *path[]);
+extern int host_link_file(const char *to[], const char *from[]);
+extern int host_read_link(const char *path[], char *buf, int size);
+extern int host_rename_file(const char *from[], const char *to[]);
+extern int host_stat_fs(const char *path[], long *bsize_out,
+			long long *blocks_out, long long *bfree_out,
+			long long *bavail_out, long long *files_out,
+			long long *ffree_out, void *fsid_out, int fsid_size,
+			long *namelen_out, long *spare_out);
+extern int host_stat_file(const char *path[],
+			  struct externfs_inode_info *info_out);
+
+extern char *generic_host_read_dir(void *stream, unsigned long long *pos,
+			      unsigned long long *ino_out, int *len_out,
+			      void *mount);
+extern int generic_host_read_file(int fd, unsigned long long offset, char *buf,
+			     int len, void *mount);
+extern void generic_host_close_file(void *stream, unsigned long long size,
+				    void *mount);
+extern int generic_host_truncate_file(struct file_handle *fh, __u64 size,
+				      void *m);
+
+extern char *externfs_name_prefix(struct externfs_inode *ext, char *prefix);
+
+#endif
Index: linux-2.6.17/fs/externfs/humfs.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/fs/externfs/humfs.c	2007-11-19 19:54:14.000000000 -0500
@@ -0,0 +1,1006 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/kdev_t.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <asm/irq.h>
+#include "hostfs.h"
+#include "mem.h"
+#include "os.h"
+#include "aio.h"
+#include "irq_user.h"
+#include "irq_kern.h"
+#include "filehandle.h"
+#include "metadata.h"
+#include "aio-restart.h"
+
+#define HUMFS_VERSION 2
+
+static int humfs_stat_file(const char *path, struct externfs_data *ed,
+			   struct externfs_inode_info *info)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+	int err, mode, perms, major, minor;
+	char type;
+
+	err = host_stat_file(data_path, info);
+	if(err)
+		return err;
+
+	err = (*mount->meta->ownerships)(path, &perms, &info->uid, &info->gid,
+					 &type, &major, &minor, mount);
+	if(err)
+		return err;
+
+	info->mode = (info->mode & ~S_IRWXUGO) | perms;
+
+	mode = 0;
+	switch(type){
+	case 'c':
+		mode = S_IFCHR;
+		break;
+	case 'b':
+		mode = S_IFBLK;
+		break;
+	case 's':
+		mode = S_IFSOCK;
+		break;
+	default:
+		break;
+	}
+
+	if(mode != 0){
+		info->mode = (info->mode & ~S_IFMT) | mode;
+		info->rdev = MKDEV(major, minor);
+	}
+
+	return 0;
+}
+
+static int meta_type(const char *path, int *dev_out, void *m)
+{
+	struct humfs *mount = m;
+	int err, type, maj, min;
+	char c;
+
+	err = (*mount->meta->ownerships)(path, NULL, NULL, NULL, &c, &maj,
+					 &min, mount);
+	if(err)
+		return err;
+
+	if(c == 0)
+		return 0;
+
+	if(dev_out)
+		*dev_out = MKDEV(maj, min);
+
+	switch(c){
+	case 'c':
+		type = OS_TYPE_CHARDEV;
+		break;
+	case 'b':
+		type = OS_TYPE_BLOCKDEV;
+		break;
+	case 'p':
+		type = OS_TYPE_FIFO;
+		break;
+	case 's':
+		type = OS_TYPE_SOCK;
+		break;
+	default:
+		type = -EINVAL;
+		break;
+	}
+
+	return type;
+}
+
+static int humfs_file_type(const char *path, int *dev_out,
+			   struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+	int type;
+
+	type = meta_type(path, dev_out, mount);
+	if(type != 0)
+		return type;
+
+	return host_file_type(data_path, dev_out);
+}
+
+static char *humfs_data_name(void *arg)
+{
+	struct externfs_inode *ext = arg;
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+
+	return externfs_name_prefix(ext, hf->mount->data);
+}
+
+char *humfs_name_prefix(struct humfs_file *hf, char *prefix)
+{
+	return externfs_name_prefix(&hf->ext, prefix);
+}
+
+static struct externfs_inode *humfs_init_file(struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	struct humfs_file *hf;
+
+	hf = (*mount->meta->init_file)(mount);
+	if(IS_ERR(hf))
+		return (struct externfs_inode *) hf;
+
+	hf->mount = mount;
+	init_filehandle(&hf->data, -ENOENT, OPENFLAGS());
+
+	return &hf->ext;
+}
+
+static int humfs_open_file(struct externfs_inode *ext, char *path, int uid,
+			   int gid)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+	struct humfs *mount = hf->mount;
+	const char *data_path[3] = { mount->data, path, NULL };
+	struct openflags flags;
+	char tmp[HOSTFS_BUFSIZE], *file;
+	int err = -ENOMEM;
+
+	if(hf->data.fd >= 0)
+		return 0;
+
+	file = get_path(data_path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	flags = of_rdwr(OPENFLAGS());
+	if(mount->direct)
+		flags = of_direct(flags);
+
+	if(path == NULL)
+		path = "";
+	err = (*mount->meta->open_file)(hf, path, mount);
+	if(err)
+		goto out_free;
+
+	err = open_filehandle(file, flags, 0, &hf->data);
+	if(err == -EISDIR)
+		goto out_free;
+	else if(err == -EPERM){
+		flags = of_set_rw(flags, 1, 0);
+		err = open_filehandle(file, flags, 0, &hf->data);
+	}
+
+	if(err)
+		goto out_close;
+
+	hf->mount = mount;
+	is_reclaimable(&hf->data, humfs_data_name, ext);
+
+ out_free:
+	free_path(file, tmp);
+ out:
+	return err;
+
+ out_close:
+	(*mount->meta->close_file)(hf);
+	goto out_free;
+}
+
+static void *humfs_open_dir(char *path, int uid, int gid,
+			    struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+
+	return host_open_dir(data_path);
+}
+
+static void humfs_close_dir(void *stream, struct externfs_data *ed)
+{
+	os_close_dir(stream);
+}
+
+static char *humfs_read_dir(void *stream, unsigned long long *pos,
+			    unsigned long long *ino_out, int *len_out,
+			    struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+
+	return generic_host_read_dir(stream, pos, ino_out, len_out, mount);
+}
+
+LIST_HEAD(humfs_replies);
+
+struct humfs_aio {
+	struct aio_context aio;
+	struct list_head list;
+	void (*completion)(char *, int, void *);
+	char *buf;
+	int real_len;
+	void *data;
+};
+
+void humfs_work_proc(struct work_struct *unused)
+{
+	struct humfs_aio *aio;
+	unsigned long flags;
+
+	while(!list_empty(&humfs_replies)){
+		local_irq_save(flags);
+		aio = list_entry(humfs_replies.next, struct humfs_aio, list);
+
+		list_del(&aio->list);
+		local_irq_restore(flags);
+
+		if(aio->aio.len >= 0)
+			aio->aio.len = aio->real_len;
+		(*aio->completion)(aio->buf, aio->aio.len, aio->data);
+
+		kfree(aio);
+	}
+}
+
+DECLARE_WORK(humfs_work, humfs_work_proc);
+
+static void humfs_restart_aio(void *arg)
+{
+	struct completion *completion = arg;
+
+	complete(completion);
+}
+
+static void humfs_interrupt(struct aio_context *context)
+{
+	struct humfs_aio *aio;
+
+	while(context){
+		if(context->len > 0)
+			submit_aio(context);
+		else {
+			aio = container_of(context, struct humfs_aio, aio);
+			list_add(&aio->list, &humfs_replies);
+		}
+		context = context->next;
+	}
+	schedule_work(&humfs_work);
+}
+
+static struct aio_driver humfs_aio_driver = {
+	.list		= LIST_HEAD_INIT(humfs_aio_driver.list),
+	.handler	= humfs_interrupt,
+	.requests	= NULL,
+};
+
+static int humfs_aio(enum aio_type type, int fd, unsigned long long offset,
+		     char *buf, int len, int real_len,
+		     void (*finish)(char *, int, void *), void *arg)
+{
+	struct humfs_aio *aio;
+	int err = -ENOMEM;
+
+	aio = kmalloc(sizeof(*aio), GFP_KERNEL);
+	if(aio == NULL)
+		goto out;
+	*aio = ((struct humfs_aio) { .aio	= INIT_AIO(type, fd, buf, len,
+							   offset,
+							   &humfs_aio_driver),
+				     .list	= LIST_HEAD_INIT(aio->list),
+				     .completion= finish,
+				     .buf	= buf,
+				     .real_len	= real_len,
+				     .data	= arg });
+
+retry:
+	err = submit_aio(&aio->aio);
+	if(err == -EAGAIN){
+		DECLARE_COMPLETION(completion);
+		struct aio_restart restart =
+			{ .list		= LIST_HEAD_INIT(restart.list),
+			  .restart_proc = humfs_restart_aio,
+			  .arg	= &completion };
+
+		init_completion(&completion);
+		aio_add_restart(&restart);
+		wait_for_completion(&completion);
+		goto retry;
+	}
+	if(err)
+		(*finish)(buf, err, arg);
+
+ out:
+	return err;
+}
+
+static int humfs_read_file(struct externfs_inode *ext,
+			   unsigned long long offset, char *buf, int len,
+			   int ignore_start, int ignore_end,
+			   void (*completion)(char *, int, void *), void *arg)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+	int fd = filehandle_fd(&hf->data);
+
+	if(fd < 0){
+		(*completion)(buf, fd, arg);
+		return fd;
+	}
+
+	return humfs_aio(AIO_READ, fd, offset, buf, len, len, completion,
+			 arg);
+}
+
+static int humfs_write_file(struct externfs_inode *ext,
+			    unsigned long long offset,
+			    const char *buf, int start, int len,
+			    void (*completion)(char *, int, void *), void *arg)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+	struct humfs *mount = hf->mount;
+	int err, orig_len = len, fd = filehandle_fd(&hf->data);
+
+	if(fd < 0){
+		(*completion)((char *) buf, fd, arg);
+		return fd;
+	}
+
+	if(mount->direct)
+		len = PAGE_SIZE;
+	else {
+		offset += start;
+		buf += start;
+	}
+
+	err = humfs_aio(AIO_WRITE, fd, offset, (char *) buf, len, orig_len,
+			completion, arg);
+
+	if(err < 0)
+		return err;
+
+	if(mount->direct)
+		err = orig_len;
+
+	return err;
+}
+
+static int humfs_map_file_page(struct externfs_inode *ext,
+			       unsigned long long offset, char *buf, int w)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+	unsigned long long size, need;
+	int err, fd = filehandle_fd(&hf->data);
+
+	if(fd < 0)
+		return fd;
+
+	err = os_fd_size(fd, &size);
+	if(err)
+		return err;
+
+	need = offset + PAGE_SIZE;
+	if(size < need){
+		err = os_truncate_fd(fd, need);
+		if(err)
+			return err;
+	}
+
+	return physmem_subst_mapping(buf, fd, offset, w);
+}
+
+static void humfs_close_file(struct externfs_inode *ext,
+			     unsigned long long size)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+	int fd;
+
+	(*hf->mount->meta->close_file)(hf);
+
+	if(hf->data.fd == -ENOENT)
+		return;
+
+	fd = filehandle_fd(&hf->data);
+	physmem_forget_descriptor(fd);
+	truncate_file(&hf->data, size);
+	close_file(&hf->data);
+}
+
+static void humfs_free_file(struct externfs_inode *ext)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+
+	(*hf->mount->meta->free_file)(hf);
+}
+
+/* XXX Assumes that you can't make a normal file */
+
+static int humfs_make_node(const char *path, int mode, int uid, int gid,
+			   int type, int major, int minor,
+			   struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+	int err;
+	char t;
+
+	err = host_create_file(data_path, S_IRWXUGO);
+	if(err)
+		goto out;
+
+	switch(type){
+	case S_IFCHR:
+		t = 'c';
+		break;
+	case S_IFBLK:
+		t = 'b';
+		break;
+	case S_IFIFO:
+		t = 'p';
+		break;
+	case S_IFSOCK:
+		t = 's';
+		break;
+	default:
+		err = -EINVAL;
+		printk("make_node - bad node type : %d\n", type);
+		goto out_rm;
+	}
+
+	err = (*mount->meta->make_node)(path, mode, uid, gid, t, major, minor,
+					mount);
+	if(err)
+		goto out_rm;
+
+ out:
+	return err;
+
+ out_rm:
+	host_unlink_file(data_path);
+	goto out;
+}
+
+static int humfs_create_file(const char *path, int mode, int uid, int gid,
+			     struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+	int err;
+
+	err = (*mount->meta->create_file)(path, mode, uid, gid, mount);
+	if(err)
+		goto out;
+
+	err = host_create_file(data_path, S_IRWXUGO);
+	if(err)
+		goto out_rm;
+
+	return 0;
+
+ out_rm:
+	(*mount->meta->remove_file)(path, mount);
+ out:
+	return err;
+}
+
+static int humfs_set_attr(const char *path, struct externfs_iattr *attrs,
+			  struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+	int (*chown)(const char *, int, int, int, struct humfs *);
+	int err;
+
+	chown = mount->meta->change_ownerships;
+	if(attrs->ia_valid & EXTERNFS_ATTR_MODE){
+		err = (*chown)(path, attrs->ia_mode, -1, -1, mount);
+		if(err)
+			return err;
+	}
+	if(attrs->ia_valid & EXTERNFS_ATTR_UID){
+		err = (*chown)(path, -1, attrs->ia_uid, -1, mount);
+		if(err)
+			return err;
+	}
+	if(attrs->ia_valid & EXTERNFS_ATTR_GID){
+		err = (*chown)(path, -1, -1, attrs->ia_gid, mount);
+		if(err)
+			return err;
+	}
+
+	attrs->ia_valid &= ~(EXTERNFS_ATTR_MODE | EXTERNFS_ATTR_UID |
+			     EXTERNFS_ATTR_GID);
+
+	return host_set_attr(data_path, attrs);
+}
+
+static int humfs_make_symlink(const char *from, const char *to, int uid,
+			      int gid, struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, from, NULL };
+	int err = -ENOMEM;
+
+	err = (*mount->meta->create_file)(from, S_IRWXUGO, uid, gid, mount);
+	if(err)
+		goto out;
+
+	err = host_make_symlink(data_path, to);
+	if(err)
+		(*mount->meta->remove_file)(from, mount);
+
+ out:
+	return err;
+}
+
+static int humfs_link_file(const char *to, const char *from, int uid, int gid,
+			   struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path_from[3] = { mount->data, from, NULL };
+	const char *data_path_to[3] = { mount->data, to, NULL };
+	int err;
+
+	err = (*mount->meta->create_link)(to, from, mount);
+	if(err)
+		return err;
+
+	err = host_link_file(data_path_to, data_path_from);
+	if(err)
+		(*mount->meta->remove_file)(from, mount);
+
+	return err;
+}
+
+static int humfs_unlink_file(const char *path, struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+	int err;
+
+	err = (*mount->meta->remove_file)(path, mount);
+	if (err)
+		return err;
+
+	return host_unlink_file(data_path);
+}
+
+static void humfs_invisible(struct externfs_inode *ext)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+	struct humfs *mount = hf->mount;
+
+	(*mount->meta->invisible)(hf);
+	not_reclaimable(&hf->data);
+}
+
+static int humfs_make_dir(const char *path, int mode, int uid, int gid,
+			  struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+	int err;
+
+	err = (*mount->meta->create_dir)(path, mode, uid, gid, mount);
+	if(err)
+		return err;
+
+	err = host_make_dir(data_path, S_IRWXUGO);
+	if(err)
+		(*mount->meta->remove_dir)(path, mount);
+
+	return err;
+}
+
+static int humfs_remove_dir(const char *path, struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, path, NULL };
+	int err;
+
+	err = host_remove_dir(data_path);
+	if (err)
+		return err;
+
+	(*mount->meta->remove_dir)(path, mount);
+
+	return err;
+}
+
+static int humfs_read_link(char *file, char *buf, int size,
+			   struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, file, NULL };
+
+	return host_read_link(data_path, buf, size);
+}
+
+struct humfs *inode_humfs_info(struct inode *inode)
+{
+	return container_of(inode_externfs_info(inode), struct humfs, ext);
+}
+
+static int humfs_rename_file(char *from, char *to, struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path_from[3] = { mount->data, from, NULL };
+	const char *data_path_to[3] = { mount->data, to, NULL };
+	int err;
+
+	err = (*mount->meta->rename_file)(from, to, mount);
+	if(err)
+		return err;
+
+	err = host_rename_file(data_path_from, data_path_to);
+	if(err)
+		(*mount->meta->rename_file)(to, from, mount);
+
+	return err;
+}
+
+static int humfs_stat_fs(long *bsize_out, long long *blocks_out,
+			 long long *bfree_out, long long *bavail_out,
+			 long long *files_out, long long *ffree_out,
+			 void *fsid_out, int fsid_size, long *namelen_out,
+			 long *spare_out, struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+	const char *data_path[3] = { mount->data, NULL };
+	int err;
+
+	/* XXX Needs to maintain this info as metadata */
+	err = host_stat_fs(data_path, bsize_out, blocks_out, bfree_out,
+			   bavail_out, files_out, ffree_out, fsid_out,
+			   fsid_size, namelen_out, spare_out);
+	if(err)
+		return err;
+
+	*blocks_out = mount->total / *bsize_out;
+	*bfree_out = (mount->total - mount->used) / *bsize_out;
+	*bavail_out = (mount->total - mount->used) / *bsize_out;
+	return 0;
+}
+
+static int humfs_fsync_file(struct externfs_inode *ext, int datasync)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+	struct humfs *mount = hf->mount;
+	int err;
+
+	err = (*mount->meta->sync_file)(hf, datasync);
+	if(err)
+		return err;
+
+	return fsync_file(&hf->data, datasync);
+}
+
+int humfs_truncate_file(struct externfs_inode *ext, __u64 size)
+{
+	struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
+
+	return truncate_file(&hf->data, size);
+}
+
+static char *humfs_path(char *dir, char *file)
+{
+	int need_slash, len = strlen(dir) + strlen(file);
+	char *new;
+
+	need_slash = (dir[strlen(dir) - 1] != '/');
+	if(need_slash)
+		len++;
+
+	new = kmalloc(len + 1, GFP_KERNEL);
+	if(new == NULL)
+		return NULL;
+
+	strcpy(new, dir);
+	if(need_slash)
+		strcat(new, "/");
+	strcat(new, file);
+
+	return new;
+}
+
+DECLARE_MUTEX(meta_sem);
+struct list_head metas = LIST_HEAD_INIT(metas);
+
+static struct humfs_meta_ops *find_meta(const char *name)
+{
+	struct list_head *ele;
+	struct humfs_meta_ops *m;
+
+	down(&meta_sem);
+	list_for_each(ele, &metas){
+		m = list_entry(ele, struct humfs_meta_ops, list);
+		if(!strcmp(m->name, name))
+			goto out;
+	}
+	m = NULL;
+ out:
+	up(&meta_sem);
+	return m;
+}
+
+void register_meta(struct humfs_meta_ops *ops)
+{
+	down(&meta_sem);
+	list_add(&ops->list, &metas);
+	up(&meta_sem);
+}
+
+void unregister_meta(struct humfs_meta_ops *ops)
+{
+	down(&meta_sem);
+	list_del(&ops->list);
+	up(&meta_sem);
+}
+
+static struct humfs *read_superblock(char *root)
+{
+	struct humfs *mount;
+	struct humfs_meta_ops *meta = NULL;
+	struct file_handle *fh;
+	const char *path[] = { root, "superblock", NULL };
+	u64 used, total;
+	char meta_buf[33], line[HOSTFS_BUFSIZE], *newline;
+	unsigned long long pos;
+	int version, i, n, err = -ENOMEM;
+
+	fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+	if(fh == NULL)
+		goto err;
+
+	err = host_open_file(path, 1, 0, 0, fh, NULL, NULL);
+	if(err){
+		printk("Failed to open %s/%s, errno = %d\n", path[0],
+		       path[1], err);
+		goto err_free_fh;
+	}
+
+	used = 0;
+	total = 0;
+	pos = 0;
+	i = 0;
+	err = -EINVAL;
+	while(1){
+		n = read_file(fh, pos, &line[i], sizeof(line) - i - 1);
+		if((n == 0) && (i == 0))
+			break;
+		if(n < 0){
+			err = n;
+			goto err_free_fh;
+		}
+
+		pos += n;
+		if(n > 0)
+			line[n + i] = '\0';
+
+		newline = strchr(line, '\n');
+		if(newline == NULL){
+			printk("read_superblock - line too long : '%s'\n",
+			       line);
+			goto err_free_fh;
+		}
+		newline++;
+
+		if(sscanf(line, "version %d\n", &version) == 1){
+			if(version != HUMFS_VERSION){
+				printk("humfs version mismatch - want version "
+				       "%d, got version %d.\n", HUMFS_VERSION,
+				       version);
+				goto err_free_fh;
+			}
+		}
+		else if(sscanf(line, "used %Lu\n", &used) == 1) ;
+		else if(sscanf(line, "total %Lu\n", &total) == 1) ;
+		else if(sscanf(line, "metadata %32s\n", meta_buf) == 1){
+			meta = find_meta(meta_buf);
+			if(meta == NULL){
+				printk("read_superblock - meta api \"%s\" not "
+				       "registered\n", meta_buf);
+				goto err_free_fh;
+			}
+		}
+		else {
+			printk("read_superblock - bogus line : '%s'\n", line);
+			goto err_free_fh;
+		}
+
+		i = newline - line;
+		memmove(line, newline, sizeof(line) - i);
+		i = strlen(line);
+	}
+
+	if(used == 0){
+		printk("read_superblock - used not specified or set to "
+		       "zero\n");
+		goto err_free_fh;
+	}
+	if(total == 0){
+		printk("read_superblock - total not specified or set to "
+		       "zero\n");
+		goto err_free_fh;
+	}
+	if(used > total){
+		printk("read_superblock - used is greater than total\n");
+		goto err_free_fh;
+	}
+
+	if(meta == NULL)
+		meta = find_meta("shadow_fs");
+
+	if(meta == NULL){
+		printk("read_superblock - valid meta api was not specified\n");
+		goto err_free_fh;
+	}
+
+	mount = (*meta->init_mount)(root);
+	if(IS_ERR(mount)){
+		err = PTR_ERR(mount);
+		goto err_free_fh;
+	}
+
+	*mount = ((struct humfs) { .total	= total,
+				   .used	= used,
+				   .meta	= meta });
+	return mount;
+
+err_free_fh:
+	kfree(fh);
+err:
+	return ERR_PTR(err);
+}
+
+struct externfs_file_ops humfs_no_mmap_file_ops = {
+	.stat_file		= humfs_stat_file,
+	.file_type		= humfs_file_type,
+	.access_file		= NULL,
+	.open_file		= humfs_open_file,
+	.open_dir		= humfs_open_dir,
+	.read_dir		= humfs_read_dir,
+	.read_file		= humfs_read_file,
+	.write_file		= humfs_write_file,
+	.map_file_page		= NULL,
+	.close_file		= humfs_close_file,
+	.free_file		= humfs_free_file,
+	.close_dir		= humfs_close_dir,
+	.invisible		= humfs_invisible,
+	.create_file		= humfs_create_file,
+	.set_attr		= humfs_set_attr,
+	.make_symlink		= humfs_make_symlink,
+	.unlink_file		= humfs_unlink_file,
+	.make_dir		= humfs_make_dir,
+	.remove_dir		= humfs_remove_dir,
+	.make_node		= humfs_make_node,
+	.link_file		= humfs_link_file,
+	.read_link		= humfs_read_link,
+	.rename_file		= humfs_rename_file,
+	.statfs			= humfs_stat_fs,
+	.fsync_file		= humfs_fsync_file,
+	.truncate_file		= humfs_truncate_file
+};
+
+struct externfs_file_ops humfs_mmap_file_ops = {
+	.stat_file		= humfs_stat_file,
+	.file_type		= humfs_file_type,
+	.access_file		= NULL,
+	.open_file		= humfs_open_file,
+	.open_dir		= humfs_open_dir,
+	.invisible		= humfs_invisible,
+	.read_dir		= humfs_read_dir,
+	.read_file		= humfs_read_file,
+	.write_file		= humfs_write_file,
+	.map_file_page		= humfs_map_file_page,
+	.close_file		= humfs_close_file,
+	.free_file		= humfs_free_file,
+	.close_dir		= humfs_close_dir,
+	.create_file		= humfs_create_file,
+	.set_attr		= humfs_set_attr,
+	.make_symlink		= humfs_make_symlink,
+	.unlink_file		= humfs_unlink_file,
+	.make_dir		= humfs_make_dir,
+	.remove_dir		= humfs_remove_dir,
+	.make_node		= humfs_make_node,
+	.link_file		= humfs_link_file,
+	.read_link		= humfs_read_link,
+	.rename_file		= humfs_rename_file,
+	.statfs			= humfs_stat_fs,
+	.fsync_file		= humfs_fsync_file,
+	.truncate_file		= humfs_truncate_file
+};
+
+static struct externfs_data *mount_fs(char *mount_arg)
+{
+	char *root, *data, *flags;
+	struct humfs *mount;
+	struct externfs_file_ops *file_ops;
+	int err, do_mmap = 0;
+
+	if(mount_arg == NULL){
+		printk("humfs - no host directory specified\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	flags = strchr((char *) mount_arg, ',');
+	if(flags != NULL){
+		do {
+			*flags++ = '\0';
+
+			if(!strcmp(flags, "mmap"))
+				do_mmap = 1;
+
+			flags = strchr(flags, ',');
+		} while(flags != NULL);
+	}
+
+	err = -ENOMEM;
+	root = host_root_filename(mount_arg);
+	if(root == NULL)
+		goto err;
+
+	mount = read_superblock(root);
+	if(IS_ERR(mount)){
+		err = PTR_ERR(mount);
+		goto err_free_root;
+	}
+
+	data = humfs_path(root, "data/");
+	if(data == NULL)
+		goto err_free_mount;
+
+	mount->data = data;
+	mount->mmap = do_mmap;
+
+	file_ops = do_mmap ? &humfs_mmap_file_ops : &humfs_no_mmap_file_ops;
+	init_externfs(&mount->ext, file_ops);
+
+	return &mount->ext;
+
+ err_free_mount:
+	mount->meta->free_mount(mount);
+ err_free_root:
+	kfree(root);
+ err:
+	return ERR_PTR(err);
+}
+
+static void humfs_umount(struct externfs_data *ed)
+{
+	struct humfs *mount = container_of(ed, struct humfs, ext);
+
+	kfree(mount->data);
+	(*mount->meta->free_mount)(mount);
+}
+
+struct externfs_mount_ops humfs_mount_ops = {
+	.init_file		= humfs_init_file,
+	.mount			= mount_fs,
+	.umount			= humfs_umount,
+};
+
+static int __init init_humfs(void)
+{
+	register_aio_driver(&humfs_aio_driver);
+
+	return register_externfs("humfs", &humfs_mount_ops);
+}
+
+static void __exit exit_humfs(void)
+{
+	unregister_externfs("humfs");
+}
+
+__initcall(init_humfs);
+__exitcall(exit_humfs);
Index: linux-2.6.17/fs/externfs/meta_fs.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/fs/externfs/meta_fs.c	2007-11-19 17:19:40.000000000 -0500
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include "hostfs.h"
+#include "metadata.h"
+#include "kern_util.h"
+
+#define METADATA_FILE_PATH(meta) (meta)->root, "file_metadata"
+#define METADATA_DIR_PATH(meta) (meta)->root, "dir_metadata"
+
+struct meta_fs {
+	struct humfs humfs;
+	char *root;
+};
+
+struct meta_file {
+	struct humfs_file humfs;
+	struct file_handle fh;
+	struct meta_fs *mount;
+};
+
+static char *meta_file_name(const char *path, struct meta_fs *meta,
+			    char *file_buf, int file_len)
+{
+	const char *data_path[] = { meta->root, "data", path, NULL };
+	const char *meta_path[5] = { meta->root, NULL, path, NULL, NULL };
+	char data_tmp[HOSTFS_BUFSIZE], meta_name[sizeof("metadatannnnnn\0")];
+	char *data_file = get_path(data_path, data_tmp, sizeof(data_tmp));
+	char *file;
+	int type, i = 0;
+
+	if(data_file == NULL)
+		return NULL;
+
+	strcpy(meta_name, "metadata");
+	type = os_file_type(data_file);
+
+	while(1){
+		if(type == OS_TYPE_DIR){
+			meta_path[1] = "dir_metadata";
+			meta_path[3] = meta_name;
+		}
+		else meta_path[1] = "file_metadata";
+
+		file = get_path(meta_path, file_buf, file_len);
+		if(file == NULL)
+			return NULL;
+
+		if((type != OS_TYPE_DIR) ||
+		   (os_file_type(file) == OS_TYPE_FILE))
+			break;
+
+		snprintf(meta_name, sizeof(meta_name), "metadata%d", i++);
+	}
+
+	free_path(data_file, data_tmp);
+
+	return file;
+}
+
+static char *meta_fs_name(void *arg)
+{
+	struct humfs_file *hf = arg;
+	struct meta_file *mf = container_of(hf, struct meta_file, humfs);
+	struct meta_fs *meta = mf->mount;
+	char tmp[HOSTFS_BUFSIZE], *name, *file;
+
+	file = meta_file_name("", meta, tmp, sizeof(tmp));
+	if(file == NULL)
+		return NULL;
+
+	name = humfs_name_prefix(hf, file);
+
+	free_path(file, tmp);
+	return name;
+}
+
+static int open_meta_file(const char *path, struct humfs *humfs,
+			  struct file_handle *fh, struct meta_file *mf)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+	char meta_tmp[HOSTFS_BUFSIZE];
+	char *meta_file;
+	int err = -ENOMEM;
+
+	meta_file = meta_file_name(path, meta, meta_tmp, sizeof(meta_tmp));
+	if(meta_file == NULL)
+		goto out;
+
+	err = open_filehandle(meta_file, of_rdwr(OPENFLAGS()), 0, fh);
+	if(err)
+		goto out;
+
+	if(mf != NULL)
+		is_reclaimable(fh, meta_fs_name, mf);
+ out:
+	free_path(meta_file, meta_tmp);
+	return err;
+}
+
+static void metafs_invisible(struct humfs_file *hf)
+{
+	struct meta_file *mf = container_of(hf, struct meta_file, humfs);
+
+	not_reclaimable(&mf->fh);
+}
+
+static struct humfs_file *metafs_init_file(struct humfs *humfs)
+{
+	struct meta_file *mf;
+	int err = -ENOMEM;
+
+	mf = kmalloc(sizeof(*mf), GFP_KERNEL);
+	if(mf == NULL)
+		return ERR_PTR(err);
+
+	init_filehandle(&mf->fh, -ENOENT, OPENFLAGS());
+	mf->mount = container_of(humfs, struct meta_fs, humfs);
+	return &mf->humfs;
+}
+
+static void metafs_free_file(struct humfs_file *hf)
+{
+	struct meta_file *meta = container_of(hf, struct meta_file, humfs);
+
+	kfree(meta);
+}
+
+static int metafs_open_file(struct humfs_file *hf, const char *path,
+			    struct humfs *humfs)
+{
+	struct meta_file *mf = container_of(hf, struct meta_file, humfs);
+
+	return open_meta_file(path, humfs, &mf->fh, mf);
+}
+
+static void metafs_close_file(struct humfs_file *hf)
+{
+	struct meta_file *meta = container_of(hf, struct meta_file, humfs);
+
+	close_file(&meta->fh);
+}
+
+static int metafs_create_file(const char *path, int mode, int uid, int gid,
+			      struct humfs *humfs)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+	struct file_handle fh;
+	char tmp[HOSTFS_BUFSIZE];
+	const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL };
+	char *file = get_path(metadata_path, tmp, sizeof(tmp));
+	char buf[sizeof("mmmm uuuuuuuuuu gggggggggg")];
+	int err = -ENOMEM;
+
+	if(file == NULL)
+		goto out;
+
+	err = open_filehandle(file, of_write(of_create(OPENFLAGS())), 0644,
+			      &fh);
+	if(err)
+		goto out_free_path;
+
+	sprintf(buf, "%d %d %d\n", mode & S_IRWXUGO, uid, gid);
+	err = write_file(&fh, 0, buf, strlen(buf));
+	close_file(&fh);
+	if(err < 0)
+		goto out_rm;
+
+	free_path(file, tmp);
+	return 0;
+
+ out_rm:
+	os_remove_file(file);
+ out_free_path:
+	free_path(file, tmp);
+ out:
+	return err;
+}
+
+static int metafs_create_link(const char *to, const char *from,
+			      struct humfs *humfs)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+	const char *path_to[] = { METADATA_FILE_PATH(meta), to,  NULL };
+	const char *path_from[] = { METADATA_FILE_PATH(meta), from, NULL };
+
+	return host_link_file(path_to, path_from);
+}
+
+static int metafs_remove_file(const char *path, struct humfs *humfs)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+	char tmp[HOSTFS_BUFSIZE];
+	const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL };
+	char *file = get_path(metadata_path, tmp, sizeof(tmp));
+	int err = -ENOMEM;
+
+	if(file == NULL)
+		goto out;
+
+	err = os_remove_file(file);
+
+ out:
+	free_path(file, tmp);
+	return err;
+}
+
+static int metafs_create_directory(const char *path, int mode, int uid,
+				   int gid, struct humfs *humfs)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+	char tmp[HOSTFS_BUFSIZE];
+	const char *dir_path[] = { METADATA_DIR_PATH(meta), path, NULL, NULL };
+	const char *file_path[] = { METADATA_FILE_PATH(meta), path, NULL,
+				    NULL };
+	char *file, dir_meta[sizeof("mmmm uuuuuuuuuu gggggggggg\n")];
+	int err, fd;
+
+	err = host_make_dir(dir_path, 0755);
+	if(err)
+		goto out;
+
+	err = host_make_dir(file_path, 0755);
+	if(err)
+		goto out_rm;
+
+	/* This to make the index independent of the number of elements in
+	 * METADATA_DIR_PATH().
+	 */
+	dir_path[sizeof(dir_path) / sizeof(dir_path[0]) - 2] = "metadata";
+
+	err = -ENOMEM;
+	file = get_path(dir_path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out_rm;
+
+	fd = open_file(file, of_create(of_rdwr(OPENFLAGS())), 0644);
+	if(fd < 0){
+		err = fd;
+		goto out_free;
+	}
+
+	sprintf(dir_meta, "%d %d %d\n", mode & S_IRWXUGO, uid, gid);
+	err = os_write_file(fd, dir_meta, strlen(dir_meta));
+	if(err > 0)
+		err = 0;
+
+	os_close_file(fd);
+
+ out_free:
+	free_path(file, tmp);
+ out_rm:
+	host_remove_dir(dir_path);
+ out:
+	return err;
+}
+
+static int metafs_remove_directory(const char *path, struct humfs *humfs)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+	char tmp[HOSTFS_BUFSIZE], *file;
+	const char *dir_path[] = { METADATA_DIR_PATH(meta), path, "metadata",
+				   NULL };
+	const char *file_path[] = { METADATA_FILE_PATH(meta), path, NULL };
+	char *slash;
+	int err;
+
+	err = -ENOMEM;
+	file = get_path(dir_path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = os_remove_file(file);
+	if(err)
+		goto out_free;
+
+	slash = strrchr(file, '/');
+	if(slash == NULL){
+		printk("remove_shadow_directory failed to find last slash\n");
+		goto out_free;
+	}
+	*slash = '\0';
+	err = os_remove_dir(file);
+	free_path(file, tmp);
+
+	file = get_path(file_path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = os_remove_dir(file);
+ out_free:
+	free_path(file, tmp);
+ out:
+	return err;
+}
+
+static int metafs_make_node(const char *path, int mode, int uid, int gid,
+			    int type, int maj, int min, struct humfs *humfs)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+	struct file_handle fh;
+	char tmp[HOSTFS_BUFSIZE];
+	const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL };
+	int err;
+	char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")], *file;
+
+	sprintf(buf, "%d %d %d %c %d %d\n", mode & S_IRWXUGO, uid, gid, type,
+		maj, min);
+
+	err = -ENOMEM;
+	file = get_path(metadata_path, tmp, sizeof(tmp));
+	if(file == NULL)
+		goto out;
+
+	err = open_filehandle(file,
+			      of_create(of_rdwr(OPENFLAGS())), 0644, &fh);
+	if(err)
+		goto out_free;
+
+	err = write_file(&fh, 0, buf, strlen(buf));
+	if(err > 0)
+		err = 0;
+
+	close_file(&fh);
+
+ out_free:
+	free_path(file, tmp);
+ out:
+	return err;
+}
+
+static int metafs_ownerships(const char *path, int *mode_out, int *uid_out,
+			     int *gid_out, char *type_out, int *maj_out,
+			     int *min_out, struct humfs *humfs)
+{
+	struct file_handle fh;
+	char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")];
+	int err, n, mode, uid, gid, maj, min;
+	char type;
+
+	err = open_meta_file(path, humfs, &fh, NULL);
+	if(err)
+		goto out;
+
+	err = os_read_file(fh.fd, buf, sizeof(buf) - 1);
+	if(err < 0)
+		goto out_close;
+
+	buf[err] = '\0';
+	err = 0;
+
+	n = sscanf(buf, "%d %d %d %c %d %d", &mode, &uid, &gid, &type, &maj,
+		   &min);
+	if(n == 3){
+		maj = -1;
+		min = -1;
+		type = 0;
+		err = 0;
+	}
+	else if(n != 6)
+		err = -EINVAL;
+
+	if(mode_out != NULL)
+		*mode_out = mode;
+	if(uid_out != NULL)
+		*uid_out = uid;
+	if(gid_out != NULL)
+		*gid_out = uid;
+	if(type_out != NULL)
+		*type_out = type;
+	if(maj_out != NULL)
+		*maj_out = maj;
+	if(min_out != NULL)
+		*min_out = min;
+
+ out_close:
+	close_file(&fh);
+ out:
+	return err;
+}
+
+static int metafs_change_ownerships(const char *path, int mode, int uid,
+				    int gid, struct humfs *humfs)
+{
+	struct file_handle fh;
+	char type;
+	char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")];
+	int err = -ENOMEM, old_mode, old_uid, old_gid, n, maj, min;
+
+	err = open_meta_file(path, humfs, &fh, NULL);
+	if(err)
+		goto out;
+
+	err = read_file(&fh, 0, buf, sizeof(buf) - 1);
+	if(err < 0)
+		goto out_close;
+
+	buf[err] = '\0';
+
+	n = sscanf(buf, "%d %d %d %c %d %d\n", &old_mode, &old_uid, &old_gid,
+		   &type, &maj, &min);
+	if((n != 3) && (n != 6)){
+		err = -EINVAL;
+		goto out_close;
+	}
+
+	if(mode == -1)
+		mode = old_mode;
+	if(uid == -1)
+		uid = old_uid;
+	if(gid == -1)
+		gid = old_gid;
+
+	if(n == 3)
+		sprintf(buf, "%d %d %d\n", mode & S_IRWXUGO, uid, gid);
+	else
+		sprintf(buf, "%d %d %d %c %d %d\n", mode & S_IRWXUGO, uid, gid,
+			type, maj, min);
+
+	err = write_file(&fh, 0, buf, strlen(buf));
+	if(err > 0)
+		err = 0;
+
+	err = truncate_file(&fh, strlen(buf));
+
+ out_close:
+	close_file(&fh);
+ out:
+	return err;
+}
+
+static int metafs_rename_file(const char *from, const char *to,
+			      struct humfs *humfs)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+	char *metadata_from, from_buf[HOSTFS_BUFSIZE];
+	char *metadata_to, to_buf[HOSTFS_BUFSIZE];
+	int err = -ENOMEM;
+
+	metadata_from = meta_file_name(from, meta, from_buf, sizeof(from_buf));
+	if(metadata_from == NULL)
+		goto out;
+
+	metadata_to = meta_file_name(to, meta, to_buf, sizeof(to_buf));
+	if(metadata_to == NULL)
+		goto out_from;
+
+	err = os_move_file(metadata_from, metadata_to);
+
+	free_path(metadata_to, to_buf);
+out_from:
+	free_path(metadata_from, from_buf);
+out:
+	return err;
+}
+
+static int metafs_sync_file(struct humfs_file *hf, int datasync)
+{
+	struct meta_file *meta = container_of(hf, struct meta_file, humfs);
+
+	return fsync_file(&meta->fh, datasync);
+}
+
+static struct humfs *metafs_init_mount(char *root)
+{
+	struct meta_fs *meta;
+	int err = -ENOMEM;
+
+	meta = kmalloc(sizeof(*meta), GFP_KERNEL);
+	if(meta == NULL)
+		goto out;
+
+	meta->root = uml_strdup(root);
+	if(meta->root == NULL)
+		goto out_free_meta;
+
+	return &meta->humfs;
+
+ out_free_meta:
+	kfree(meta);
+ out:
+	return ERR_PTR(err);
+}
+
+static void metafs_free_mount(struct humfs *humfs)
+{
+	struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs);
+
+	kfree(meta->root);
+	kfree(meta);
+}
+
+struct humfs_meta_ops hum_fs_meta_fs_ops = {
+	.list			= LIST_HEAD_INIT(hum_fs_meta_fs_ops.list),
+	.name			= "shadow_fs",
+	.init_file		= metafs_init_file,
+	.open_file		= metafs_open_file,
+	.close_file		= metafs_close_file,
+	.free_file		= metafs_free_file,
+	.ownerships		= metafs_ownerships,
+	.make_node		= metafs_make_node,
+	.create_file		= metafs_create_file,
+	.create_link		= metafs_create_link,
+	.remove_file		= metafs_remove_file,
+	.create_dir		= metafs_create_directory,
+	.remove_dir		= metafs_remove_directory,
+	.change_ownerships	= metafs_change_ownerships,
+	.rename_file		= metafs_rename_file,
+	.sync_file		= metafs_sync_file,
+	.invisible		= metafs_invisible,
+	.init_mount		= metafs_init_mount,
+	.free_mount		= metafs_free_mount,
+};
+
+static int __init init_meta_fs(void)
+{
+	register_meta(&hum_fs_meta_fs_ops);
+	return 0;
+}
+
+static void __exit exit_meta_fs(void)
+{
+	unregister_meta(&hum_fs_meta_fs_ops);
+}
+
+__initcall(init_meta_fs);
+__exitcall(exit_meta_fs);
Index: linux-2.6.17/fs/externfs/metadata.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.17/fs/externfs/metadata.h	2007-11-19 17:19:40.000000000 -0500
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2004 Piotr Neuman (sikkh@wp.pl) and
+ * Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_FS_METADATA
+#define __UM_FS_METADATA
+
+#include "linux/fs.h"
+#include "linux/list.h"
+#include "os.h"
+#include "hostfs.h"
+
+struct humfs {
+	struct externfs_data ext;
+	__u64 used;
+	__u64 total;
+	char *data;
+	int mmap;
+	int direct;
+	struct humfs_meta_ops *meta;
+};
+
+struct humfs_file {
+	struct humfs *mount;
+	struct file_handle data;
+	struct externfs_inode ext;
+};
+
+struct humfs_meta_ops {
+	struct list_head list;
+	char *name;
+	struct humfs_file *(*init_file)(struct humfs *humfs);
+	int (*open_file)(struct humfs_file *hf, const char *path,
+			 struct humfs *humfs);
+	int (*create_file)(const char *path, int mode, int uid, int gid,
+			   struct humfs *humfs);
+	void (*close_file)(struct humfs_file *humfs);
+	void (*free_file)(struct humfs_file *humfs);
+	int (*ownerships)(const char *path, int *mode_out, int *uid_out,
+			  int *gid_out, char *type_out, int *maj_out,
+			  int *min_out, struct humfs *humfs);
+	int (*make_node)(const char *path, int mode, int uid, int gid,
+			 int type, int major, int minor, struct humfs *humfs);
+	int (*create_link)(const char *to, const char *from,
+			   struct humfs *humfs);
+	int (*remove_file)(const char *path, struct humfs *humfs);
+	int (*create_dir)(const char *path, int mode, int uid, int gid,
+			  struct humfs *humfs);
+	int (*remove_dir)(const char *path, struct humfs *humfs);
+	int (*change_ownerships)(const char *path, int mode, int uid, int gid,
+				 struct humfs *humfs);
+	int (*rename_file)(const char *from, const char *to,
+			   struct humfs *humfs);
+	int (*sync_file)(struct humfs_file *hf, int datasync);
+	void (*invisible)(struct humfs_file *hf);
+	struct humfs *(*init_mount)(char *root);
+	void (*free_mount)(struct humfs *humfs);
+};
+
+extern void register_meta(struct humfs_meta_ops *ops);
+extern void unregister_meta(struct humfs_meta_ops *ops);
+
+extern char *humfs_name(struct inode *inode, char *prefix);
+extern struct humfs *inode_humfs_info(struct inode *inode);
+extern char *humfs_name_prefix(struct humfs_file *hf, char *prefix);
+
+#endif
Index: linux-2.6.17/arch/um/kernel/Makefile
===================================================================
--- linux-2.6.17.orig/arch/um/kernel/Makefile	2007-11-19 16:42:39.000000000 -0500
+++ linux-2.6.17/arch/um/kernel/Makefile	2007-11-19 17:19:40.000000000 -0500
@@ -6,7 +6,7 @@
 extra-y := vmlinux.lds
 clean-files :=
 
-obj-y = aio.o config.o exec.o exitcode.o init_task.o irq.o \
+obj-y = aio.o config.o exec.o exitcode.o filehandle.o init_task.o irq.o \
 	ksyms.o mem.o physmem.o process.o ptrace.o reboot.o sigio.o \
 	signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o uaccess.o \
 	um_arch.o umid.o skas/
Index: linux-2.6.17/arch/um/kernel/physmem.c
===================================================================
--- linux-2.6.17.orig/arch/um/kernel/physmem.c	2007-11-19 11:58:12.000000000 -0500
+++ linux-2.6.17/arch/um/kernel/physmem.c	2007-11-19 19:55:02.000000000 -0500
@@ -5,6 +5,7 @@
 
 #include "linux/bootmem.h"
 #include "linux/mm.h"
+#include "linux/module.h"
 #include "linux/pfn.h"
 #include "asm/page.h"
 #include "as-layout.h"
@@ -13,8 +14,229 @@
 #include "mem_user.h"
 #include "os.h"
 
+struct phys_desc {
+	struct rb_node rb;
+	int fd;
+	__u64 offset;
+	void *virt;
+	unsigned long phys;
+	struct list_head list;
+};
+
+static struct rb_root phys_mappings = RB_ROOT;
+
+static struct rb_node **find_rb(void *virt)
+{
+	struct rb_node **n = &phys_mappings.rb_node;
+	struct phys_desc *d;
+
+	while(*n != NULL){
+		d = rb_entry(*n, struct phys_desc, rb);
+		if(d->virt == virt)
+			return n;
+
+		if(d->virt > virt)
+			n = &(*n)->rb_left;
+		else
+			n = &(*n)->rb_right;
+	}
+
+	return n;
+}
+
+static struct phys_desc *find_phys_mapping(void *virt)
+{
+	struct rb_node **n = find_rb(virt);
+
+	if(*n == NULL)
+		return NULL;
+
+	return rb_entry(*n, struct phys_desc, rb);
+}
+
+static void insert_phys_mapping(struct phys_desc *desc)
+{
+	struct rb_node **n = find_rb(desc->virt);
+
+	if(*n != NULL)
+		panic("Physical remapping for %p already present",
+		      desc->virt);
+
+	rb_link_node(&desc->rb, rb_parent(*n), n);
+	rb_insert_color(&desc->rb, &phys_mappings);
+}
+
+LIST_HEAD(descriptor_mappings);
+
+struct desc_mapping {
+	int fd;
+	struct list_head list;
+	struct list_head pages;
+};
+
+static struct desc_mapping *find_mapping(int fd)
+{
+	struct desc_mapping *desc;
+	struct list_head *ele;
+
+	list_for_each(ele, &descriptor_mappings){
+		desc = list_entry(ele, struct desc_mapping, list);
+		if(desc->fd == fd)
+			return desc;
+	}
+
+	return NULL;
+}
+
+static struct desc_mapping *descriptor_mapping(int fd)
+{
+	struct desc_mapping *desc;
+
+	desc = find_mapping(fd);
+	if(desc != NULL)
+		return desc;
+
+	desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
+	if(desc == NULL)
+		return NULL;
+
+	*desc = ((struct desc_mapping)
+		{ .fd =		fd,
+		  .list =	LIST_HEAD_INIT(desc->list),
+		  .pages =	LIST_HEAD_INIT(desc->pages) });
+	list_add(&desc->list, &descriptor_mappings);
+
+	return desc;
+}
+
+int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
+{
+	struct desc_mapping *fd_maps;
+	struct phys_desc *desc;
+	unsigned long phys;
+	int err;
+
+	fd_maps = descriptor_mapping(fd);
+	if(fd_maps == NULL)
+		return -ENOMEM;
+
+	phys = __pa(virt);
+	desc = find_phys_mapping(virt);
+	if(desc != NULL)
+		panic("Address 0x%p is already substituted\n", virt);
+
+	err = -ENOMEM;
+	desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
+	if(desc == NULL)
+		goto out;
+
+	*desc = ((struct phys_desc)
+		{ .fd =			fd,
+		  .offset =		offset,
+		  .virt =		virt,
+		  .phys =		__pa(virt),
+		  .list = 		LIST_HEAD_INIT(desc->list) });
+	insert_phys_mapping(desc);
+
+	list_add(&desc->list, &fd_maps->pages);
+
+	virt = (void *) ((unsigned long) virt & PAGE_MASK);
+	err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
+	if(!err)
+		goto out;
+
+	rb_erase(&desc->rb, &phys_mappings);
+	kfree(desc);
+ out:
+	return err;
+}
+
 static int physmem_fd = -1;
 
+static void remove_phys_mapping(struct phys_desc *desc)
+{
+	void *virt = desc->virt;
+	int err;
+
+	rb_erase(&desc->rb, &phys_mappings);
+	list_del(&desc->list);
+	kfree(desc);
+
+	err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
+	if(err)
+		panic("Failed to unmap block device page from physical memory, "
+		      "errno = %d", -err);
+}
+
+int physmem_remove_mapping(void *virt)
+{
+	struct phys_desc *desc;
+
+	virt = (void *) ((unsigned long) virt & PAGE_MASK);
+	desc = find_phys_mapping(virt);
+	if(desc == NULL)
+		return 0;
+
+	remove_phys_mapping(desc);
+	return 1;
+}
+
+void physmem_forget_descriptor(int fd)
+{
+	struct desc_mapping *desc;
+	struct phys_desc *page;
+	struct list_head *ele, *next;
+	__u64 offset;
+	void *addr;
+	int err;
+
+	desc = find_mapping(fd);
+	if(desc == NULL)
+		return;
+
+	list_for_each_safe(ele, next, &desc->pages){
+		page = list_entry(ele, struct phys_desc, list);
+		offset = page->offset;
+		addr = page->virt;
+		remove_phys_mapping(page);
+		err = os_seek_file(fd, offset);
+		if(err)
+			panic("physmem_forget_descriptor - failed to seek "
+			      "to %lld in fd %d, error = %d\n",
+			      offset, fd, -err);
+		err = os_read_file(fd, addr, PAGE_SIZE);
+		if(err < 0)
+			panic("physmem_forget_descriptor - failed to read "
+			      "from fd %d to 0x%p, error = %d\n",
+			      fd, addr, -err);
+	}
+
+	list_del(&desc->list);
+	kfree(desc);
+}
+
+EXPORT_SYMBOL(physmem_forget_descriptor);
+EXPORT_SYMBOL(physmem_remove_mapping);
+EXPORT_SYMBOL(physmem_subst_mapping);
+
+void arch_free_page(struct page *page, int order)
+{
+	void *virt;
+	int i;
+
+	for(i = 0; i < (1 << order); i++){
+		virt = __va(page_to_phys(page + i));
+		physmem_remove_mapping(virt);
+	}
+}
+
+int is_remapped(void *virt)
+{
+ 	struct phys_desc *desc = find_phys_mapping(virt);
+
+	return desc != NULL;
+}
+
 /* Changed during early boot */
 unsigned long high_physmem;
 
@@ -119,9 +341,14 @@ void __init setup_physmem(unsigned long 
 
 int phys_mapping(unsigned long phys, unsigned long long *offset_out)
 {
+	struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK));
 	int fd = -1;
 
-	if (phys < physmem_size) {
+	if (desc != NULL) {
+		fd = desc->fd;
+		*offset_out = desc->offset;
+	}
+	else if (phys < physmem_size) {
 		fd = physmem_fd;
 		*offset_out = phys;
 	}
Index: linux-2.6.17/include/asm-um/page.h
===================================================================
--- linux-2.6.17.orig/include/asm-um/page.h	2007-11-19 11:58:12.000000000 -0500
+++ linux-2.6.17/include/asm-um/page.h	2007-11-19 17:19:40.000000000 -0500
@@ -115,6 +115,9 @@ extern unsigned long uml_physmem;
 extern struct page *arch_validate(struct page *page, gfp_t mask, int order);
 #define HAVE_ARCH_VALIDATE
 
+extern void arch_free_page(struct page *page, int order);
+#define HAVE_ARCH_FREE_PAGE
+
 #include <asm-generic/memory_model.h>
 #include <asm-generic/page.h>
 
Index: linux-2.6.17/arch/um/include/mem.h
===================================================================
--- linux-2.6.17.orig/arch/um/include/mem.h	2007-10-24 10:04:50.000000000 -0400
+++ linux-2.6.17/arch/um/include/mem.h	2007-11-19 19:57:11.000000000 -0500
@@ -7,6 +7,10 @@
 #define __MEM_H__
 
 extern int phys_mapping(unsigned long phys, unsigned long long *offset_out);
+extern int physmem_remove_mapping(void *virt);
+extern int physmem_subst_mapping(void *virt, int fd, unsigned long long offset,
+				 int w);
+extern void physmem_forget_descriptor(int fd);
 
 extern unsigned long uml_physmem;
 static inline unsigned long to_phys(void *virt)