# 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 #include #include +#include +#include +#include #include #include #include @@ -15,6 +18,7 @@ #include #include #include +#include #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 + . + + 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 + . + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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=,,...\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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#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 #include 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)