summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjdike <jdike>2004-04-07 20:44:02 +0000
committerjdike <jdike>2004-04-07 20:44:02 +0000
commit9945ac8d1c5e4c70036663ae3b6b3ed2c5b72059 (patch)
tree8a72a3fa5e62706a797934f26dc748fdd535e1fa
parent6a7a2f74418628bcf9ba8021ccc6ac53ee16ff0e (diff)
downloaduml-history-9945ac8d1c5e4c70036663ae3b6b3ed2c5b72059.tar.gz
Added an abstract, pluggable interface between hostfs_kern and hostfs_user.
Added humfs, which plugs into that interface.
-rw-r--r--arch/um/config.in7
-rw-r--r--arch/um/fs/hostfs/Makefile10
-rw-r--r--arch/um/fs/hostfs/host_file.c538
-rw-r--r--arch/um/fs/hostfs/hostfs.h162
-rw-r--r--arch/um/fs/hostfs/hostfs_kern.c549
-rw-r--r--arch/um/fs/hostfs/hostfs_user.c461
-rw-r--r--arch/um/fs/hostfs/humfs.c996
-rw-r--r--include/asm-um/irq.h3
-rw-r--r--include/linux/hostfs_fs_i.h4
9 files changed, 2260 insertions, 470 deletions
diff --git a/arch/um/config.in b/arch/um/config.in
index a28534b..800b959 100644
--- a/arch/um/config.in
+++ b/arch/um/config.in
@@ -37,7 +37,14 @@ bool 'Sysctl support' CONFIG_SYSCTL
tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
+
tristate 'Host filesystem' CONFIG_HOSTFS
+tristate 'Usable host filesystem' CONFIG_HUMFS
+
+if [ "$CONFIG_HOSTFS" = "y" -o "$CONFIG_HUMFS" = "y" ]; then
+ define_tristate CONFIG_EXTERNFS y
+fi
+
tristate 'Honeypot proc filesystem' CONFIG_HPPFS
bool 'Management console' CONFIG_MCONSOLE
dep_bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ $CONFIG_MCONSOLE
diff --git a/arch/um/fs/hostfs/Makefile b/arch/um/fs/hostfs/Makefile
index eb2b530..7890f8b 100644
--- a/arch/um/fs/hostfs/Makefile
+++ b/arch/um/fs/hostfs/Makefile
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+# Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
# Licensed under the GPL
#
@@ -13,10 +13,14 @@ STAT64_INO_FIELD := $(shell grep -q __st_ino /usr/include/bits/stat.h && \
USER_CFLAGS := $(USER_CFLAGS) -DSTAT64_INO_FIELD=$(STAT64_INO_FIELD)
O_TARGET := hostfs.o
-obj-y = hostfs_kern.o hostfs_user.o
+
+obj-$(CONFIG_EXTERNFS) += hostfs_kern.o
+obj-$(CONFIG_HOSTFS) += hostfs_user.o host_file.o
+obj-$(CONFIG_HUMFS) += humfs.o host_file.o
+
obj-m = $(O_TARGET)
-USER_OBJS = $(filter %_user.o,$(obj-y))
+USER_OBJS = $(filter %_user.o,$(obj-y)) host_file.o
include $(TOPDIR)/Rules.make
diff --git a/arch/um/fs/hostfs/host_file.c b/arch/um/fs/hostfs/host_file.c
new file mode 100644
index 0000000..6f5b1c9
--- /dev/null
+++ b/arch/um/fs/hostfs/host_file.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dirent.h>
+#include <utime.h>
+#include <sys/vfs.h>
+#include "os.h"
+#include "user.h"
+#include "hostfs.h"
+
+extern int append;
+
+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 = um_kmalloc(new);
+ 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)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int mode = 0, fd;
+
+ if(r && !w)
+ mode = O_RDONLY;
+ else if(!r && w)
+ mode = O_WRONLY;
+ else if(r && w)
+ mode = O_RDWR;
+ else {
+ printk("Impossible mode in host_open_file - r = %d, w = %d",
+ r, w);
+ return(-EINVAL);
+ }
+
+ if(append)
+ mode |= O_APPEND;
+
+ fd = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ fd = open64(file, mode);
+ if(fd < 0)
+ fd = -errno;
+ out:
+ free_path(file, tmp);
+ return(fd);
+}
+
+void *host_open_dir(const char *path[], int *err_out)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ DIR *dir = NULL;
+
+ *err_out = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ dir = opendir(file);
+ *err_out = errno;
+ 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)
+{
+ DIR *dir = stream;
+ struct dirent *ent;
+
+ seekdir(dir, *pos);
+ ent = readdir(dir);
+ if(ent == NULL) return(NULL);
+ *len_out = strlen(ent->d_name);
+ *ino_out = ent->d_ino;
+ *pos = telldir(dir);
+ return(ent->d_name);
+}
+
+void host_close_dir(void *stream)
+{
+ closedir(stream);
+}
+
+int host_file_type(const char *path[], int *rdev)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ struct stat64 buf;
+ int ret;
+
+ ret = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ if(lstat64(file, &buf) < 0){
+ ret = -errno;
+ goto out;
+ }
+
+ if(rdev != NULL)
+ *rdev = buf.st_rdev;
+
+ if(S_ISDIR(buf.st_mode)) ret = OS_TYPE_DIR;
+ else if(S_ISLNK(buf.st_mode)) ret = OS_TYPE_SYMLINK;
+ else if(S_ISCHR(buf.st_mode)) ret = OS_TYPE_CHARDEV;
+ else if(S_ISBLK(buf.st_mode)) ret = OS_TYPE_BLOCKDEV;
+ else if(S_ISFIFO(buf.st_mode))ret = OS_TYPE_FIFO;
+ else if(S_ISSOCK(buf.st_mode))ret = OS_TYPE_SOCK;
+ else ret = OS_TYPE_FILE;
+ out:
+ free_path(file, tmp);
+ return(ret);
+}
+
+int host_file_create(const char *path[], int ur, int uw, int ux,
+ int gr, int gw, int gx, int or, int ow, int ox)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int mode = 0, fd;
+
+ mode |= ur ? S_IRUSR : 0;
+ mode |= uw ? S_IWUSR : 0;
+ mode |= ux ? S_IXUSR : 0;
+ mode |= gr ? S_IRGRP : 0;
+ mode |= gw ? S_IWGRP : 0;
+ mode |= gx ? S_IXGRP : 0;
+ mode |= or ? S_IROTH : 0;
+ mode |= ow ? S_IWOTH : 0;
+ mode |= ox ? S_IXOTH : 0;
+
+ fd = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ fd = open64(file, O_CREAT | O_RDWR, mode);
+ out:
+ free_path(file, tmp);
+ return(fd);
+}
+
+static int do_stat_file(const char *path, int *dev_out,
+ unsigned long long *inode_out, int *mode_out,
+ int *nlink_out, int *uid_out, int *gid_out,
+ unsigned long long *size_out, unsigned long *atime_out,
+ unsigned long *mtime_out, unsigned long *ctime_out,
+ int *blksize_out, unsigned long long *blocks_out)
+{
+ struct stat64 buf;
+
+ if(lstat64(path, &buf) < 0)
+ return(-errno);
+
+ if(dev_out != NULL) *dev_out = buf.st_dev;
+
+ /* See the Makefile for why STAT64_INO_FIELD is passed in
+ * by the build
+ */
+ if(inode_out != NULL) *inode_out = buf.STAT64_INO_FIELD;
+ if(mode_out != NULL) *mode_out = buf.st_mode;
+ if(nlink_out != NULL) *nlink_out = buf.st_nlink;
+ if(uid_out != NULL) *uid_out = buf.st_uid;
+ if(gid_out != NULL) *gid_out = buf.st_gid;
+ if(size_out != NULL) *size_out = buf.st_size;
+ if(atime_out != NULL) *atime_out = buf.st_atime;
+ if(mtime_out != NULL) *mtime_out = buf.st_mtime;
+ if(ctime_out != NULL) *ctime_out = buf.st_ctime;
+ if(blksize_out != NULL) *blksize_out = buf.st_blksize;
+ if(blocks_out != NULL) *blocks_out = buf.st_blocks;
+
+ return(0);
+}
+
+int host_stat_file(const char *path[], int *dev_out,
+ unsigned long long *inode_out, int *mode_out,
+ int *nlink_out, int *uid_out, int *gid_out,
+ unsigned long long *size_out, unsigned long *atime_out,
+ unsigned long *mtime_out, unsigned long *ctime_out,
+ int *blksize_out, unsigned long long *blocks_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, dev_out, inode_out, mode_out, nlink_out,
+ uid_out, gid_out, size_out, atime_out, mtime_out,
+ ctime_out, blksize_out, blocks_out);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+int host_set_attr(const char *path[], struct hostfs_iattr *attrs)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ struct utimbuf buf;
+ int err = 0, ma;
+
+ if(append && (attrs->ia_valid & HOSTFS_ATTR_SIZE))
+ return(-EPERM);
+
+ err = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ if(attrs->ia_valid & HOSTFS_ATTR_MODE){
+ if(chmod(file, attrs->ia_mode) != 0){
+ err = -errno;
+ goto out;
+ }
+ }
+ if(attrs->ia_valid & HOSTFS_ATTR_UID){
+ if(chown(file, attrs->ia_uid, -1)){
+ err = -errno;
+ goto out;
+ }
+ }
+ if(attrs->ia_valid & HOSTFS_ATTR_GID){
+ if(chown(file, -1, attrs->ia_gid)){
+ err = -errno;
+ goto out;
+ }
+ }
+ if(attrs->ia_valid & HOSTFS_ATTR_SIZE){
+ if(truncate(file, attrs->ia_size)){
+ err = -errno;
+ goto out;
+ }
+ }
+ ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET;
+ if((attrs->ia_valid & ma) == ma){
+ buf.actime = attrs->ia_atime;
+ buf.modtime = attrs->ia_mtime;
+ if(utime(file, &buf) != 0){
+ err = -errno;
+ goto out;
+ }
+ }
+ else {
+ if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){
+ err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, &buf.modtime,
+ NULL, NULL, NULL);
+ if(err != 0)
+ goto out;
+ buf.actime = attrs->ia_atime;
+ if(utime(file, &buf) != 0){
+ err = -errno;
+ goto out;
+ }
+ }
+ if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){
+ err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, &buf.actime, NULL,
+ NULL, NULL, NULL);
+ if(err != 0)
+ goto out;
+ buf.modtime = attrs->ia_mtime;
+ if(utime(file, &buf) != 0){
+ err = -errno;
+ goto out;
+ }
+ }
+ }
+ if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ;
+ if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){
+ err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, &attrs->ia_atime,
+ &attrs->ia_mtime, NULL, NULL, NULL);
+ if(err != 0)
+ goto out;
+ }
+
+ 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 = symlink(to, file);
+ if(err)
+ err = -errno;
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+int host_unlink_file(const char *path[])
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int err = -ENOMEM;
+
+ if(append)
+ return(-EPERM);
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = unlink(file);
+ if(err)
+ err = -errno;
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+int host_mkdir(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 = mkdir(file, mode);
+ if(err)
+ err = -errno;
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+int host_rmdir(const char *path[])
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int err = -ENOMEM;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = rmdir(file);
+ if(err)
+ err = -errno;
+ 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 = link(t, f);
+ if(err)
+ err = -errno;
+ out:
+ free_path(f, from_tmp);
+ free_path(t, to_tmp);
+ return(err);
+}
+
+int host_readlink(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 = readlink(file, buf, size);
+ if(n < 0)
+ n = -errno;
+ 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 = rename(f, t);
+ if(err < 0)
+ err = -errno;
+ out:
+ free_path(f, from_tmp);
+ free_path(t, to_tmp);
+ return(err);
+}
+
+int host_statfs(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;
+ struct statfs64 buf;
+ int err = -ENOMEM;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = statfs64(file, &buf);
+ if(err < 0){
+ err = -errno;
+ goto out;
+ }
+ *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];
+ 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_read_file(int fd, unsigned long long offset, char *buf,
+ int len, void *mount)
+{
+ return(host_read_file(fd, offset, buf, len));
+}
+
+int generic_host_write_file(int fd, unsigned long long offset, const char *buf,
+ int len, void *mount)
+{
+ return(host_write_file(fd, offset, buf, len));
+}
+
+void generic_host_close_file(void *stream, unsigned long long size, void *mount)
+{
+ return(host_close_file(stream));
+}
+
+void generic_host_close_dir(void *stream, void *mount)
+{
+ return(host_close_dir(stream));
+}
+
+int generic_host_truncate_file(int fd, __u64 size, void *m)
+{
+ return(os_truncate_fd(fd, size));
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/fs/hostfs/hostfs.h b/arch/um/fs/hostfs/hostfs.h
index 7cf3b5b..48fb0ff 100644
--- a/arch/um/fs/hostfs/hostfs.h
+++ b/arch/um/fs/hostfs/hostfs.h
@@ -1,3 +1,8 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
#ifndef __UM_FS_HOSTFS
#define __UM_FS_HOSTFS
@@ -31,39 +36,128 @@ struct hostfs_iattr {
unsigned int ia_attr_flags;
};
-extern int stat_file(const char *path, int *dev_out,
- unsigned long long *inode_out, int *mode_out,
- int *nlink_out, int *uid_out, int *gid_out,
- unsigned long long *size_out, unsigned long *atime_out,
- unsigned long *mtime_out, unsigned long *ctime_out,
- int *blksize_out, unsigned long long *blocks_out);
-extern int access_file(char *path, int r, int w, int x);
-extern int open_file(char *path, int r, int w, int append);
-extern int file_type(const char *path, int *rdev);
-extern void *open_dir(char *path, int *err_out);
-extern char *read_dir(void *stream, unsigned long long *pos,
- unsigned long long *ino_out, int *len_out);
-extern void close_file(void *stream);
-extern void close_dir(void *stream);
-extern int read_file(int fd, unsigned long long *offset, char *buf, int len);
-extern int write_file(int fd, unsigned long long *offset, const char *buf,
- int len);
-extern int lseek_file(int fd, long long offset, int whence);
-extern int file_create(char *name, int ur, int uw, int ux, int gr,
- int gw, int gx, int or, int ow, int ox);
-extern int set_attr(const char *file, struct hostfs_iattr *attrs);
-extern int make_symlink(const char *from, const char *to);
-extern int unlink_file(const char *file);
-extern int do_mkdir(const char *file, int mode);
-extern int do_rmdir(const char *file);
-extern int do_mknod(const char *file, int mode, int dev);
-extern int link_file(const char *from, const char *to);
-extern int do_readlink(char *file, char *buf, int size);
-extern int rename_file(char *from, char *to);
-extern int do_statfs(char *root, 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_file_ops {
+ int (*stat_file)(const char *path, void *mount, int *dev_out,
+ unsigned long long *inode_out, int *mode_out,
+ int *nlink_out, int *uid_out, int *gid_out,
+ unsigned long long *size_out,
+ unsigned long *atime_out, unsigned long *mtime_out,
+ unsigned long *ctime_out, int *blksize_out,
+ unsigned long long *blocks_out);
+ int (*file_type)(const char *path, int *rdev, void *mount);
+ int (*access_file)(char *path, int uid, int gid, int r, int w, int x,
+ void *mount);
+ int (*open_file)(char *path, int uid, int gid, int r, int w,
+ void *mount);
+ void *(*open_dir)(char *path, int uid, int gid, int *err_out,
+ void *mount);
+ char *(*read_dir)(void *stream, unsigned long long *pos,
+ unsigned long long *ino_out, int *len_out,
+ void *mount);
+ int (*read_file)(int fd, unsigned long long offset, char *buf,
+ int len, void (*completion)(char *, int, void *),
+ void *arg, void *mount);
+ int (*write_file)(int fd, unsigned long long offset, const char *buf,
+ int len, void *mount);
+ int (*map_file_page)(int fd, unsigned long long offset, char *buf,
+ int w, void *mount);
+ void (*close_file)(void *stream, unsigned long long size, void *mount);
+ void (*close_dir)(void *stream, void *mount);
+ int (*file_create)(char *path, int uid, int gid, int ur, int uw, int ux,
+ int gr, int gw, int gx, int or, int ow, int ox,
+ void *mount);
+ int (*set_attr)(const char *path, struct hostfs_iattr *attrs,
+ void *mount);
+ int (*make_symlink)(const char *from, const char *to, int uid, int gid,
+ void *mount);
+ int (*unlink_file)(const char *path, void *mount);
+ int (*mkdir)(const char *path, int mode, int uid, int gid, void *mount);
+ int (*rmdir)(const char *path, int uid, int gid, void *mount);
+ int (*mknod)(const char *path, int uid, int gid, int ur, int uw, int ux,
+ int gr, int gw, int gx, int or, int ow, int ox, int type,
+ int dev, void *mount);
+ int (*link_file)(const char *to, const char *from, int uid, int gid,
+ void *mount);
+ int (*readlink)(char *path, int uid, int gid, char *buf, int size,
+ void *mount);
+ int (*rename_file)(char *from, char *to, void *mount);
+ 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, void *mount);
+ int (*truncate_file)(int fd, __u64 size, void *m);
+};
+
+struct externfs_mount_ops {
+ struct externfs_file_ops *(*mount)(char *mount_arg,
+ void **mount_data_out);
+};
+
+#define HOSTFS_BUFSIZE 64
+
+extern int register_externfs(char *name, struct externfs_mount_ops *ops);
+extern void unregister_externfs(char *name);
+
+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_write_file(int fd, unsigned long long offset, const char *buf,
+ int len);
+extern int host_open_file(const char *path[], int r, int w);
+extern void *host_open_dir(const char *path[], int *err_out);
+extern char *host_read_dir(void *stream, unsigned long long *pos,
+ unsigned long long *ino_out, int *len_out);
+extern void host_close_dir(void *stream);
+extern int host_file_type(const char *path[], int *rdev);
+extern int host_root_filename(char *mount_arg, void **mount_data_out);
+extern char *get_path(const char *path[], char *buf, int size);
+extern void free_path(const char *buf, char *tmp);
+extern int host_file_create(const char *path[], int ur, int uw, int ux,
+ int gr, int gw, int gx, int or, int ow, int ox);
+extern int host_set_attr(const char *path[], struct hostfs_iattr *attrs);
+extern int host_make_symlink(const char *from[], const char *to);
+extern int host_unlink_file(const char *path[]);
+extern int host_mkdir(const char *path[], int mode);
+extern int host_rmdir(const char *path[]);
+extern int host_link_file(const char *to[], const char *from[]);
+extern int host_readlink(const char *path[], char *buf, int size);
+extern int host_rename_file(const char *from[], const char *to[]);
+extern int host_statfs(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[], int *dev_out,
+ unsigned long long *inode_out, int *mode_out,
+ int *nlink_out, int *uid_out, int *gid_out,
+ unsigned long long *size_out,
+ unsigned long *atime_out, unsigned long *mtime_out,
+ unsigned long *ctime_out, int *blksize_out,
+ unsigned long long *blocks_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 int generic_host_write_file(int fd, unsigned long long offset,
+ const char *buf, int len, void *mount);
+extern void generic_host_close_file(void *stream, unsigned long long size,
+ void *mount);
+extern void generic_host_close_dir(void *stream, void *mount);
+extern int generic_host_truncate_file(int fd, __u64 size, void *m);
#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/fs/hostfs/hostfs_kern.c b/arch/um/fs/hostfs/hostfs_kern.c
index 0dbe3cc..aa0a1bd 100644
--- a/arch/um/fs/hostfs/hostfs_kern.c
+++ b/arch/um/fs/hostfs/hostfs_kern.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
+ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
@@ -18,6 +18,13 @@
#include "user_util.h"
#include "2_5compat.h"
#include "init.h"
+#include "mem.h"
+
+struct externfs {
+ struct list_head list;
+ struct externfs_mount_ops *ops;
+ struct file_system_type type;
+};
#define file_hostfs_i(file) (&(file)->f_dentry->d_inode->u.hostfs_i)
@@ -30,59 +37,16 @@ struct dentry_operations hostfs_dentry_ops = {
.d_delete = hostfs_d_delete,
};
-#define DEFAULT_ROOT "/"
-
-/* Changed in hostfs_args before the kernel starts running */
-static char *jail_dir = NULL;
-static int append = 0;
-
#define HOSTFS_SUPER_MAGIC 0x00c0ffee
static struct inode_operations hostfs_iops;
static struct inode_operations hostfs_dir_iops;
static struct address_space_operations hostfs_link_aops;
-static int __init 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);
-}
-
-__uml_setup("hostfs=", hostfs_args,
-"hostfs=<root dir>,<flags>,...\n"
-" This is used to set hostfs parameters. The root directory argument\n"
-" is used to confine all hostfs mounts to within the specified directory\n"
-" tree on the host. If this isn't specified, then a user inside UML can\n"
-" mount anything on the host that's accessible to the user that's running\n"
-" it.\n"
-" The only flag currently supported is 'append', which specifies that all\n"
-" files opened by hostfs will be opened in append mode.\n\n"
-);
-
static char *dentry_name(struct dentry *dentry, int extra)
{
struct dentry *parent;
- char *root, *name;
+ char *name;
int len;
len = 0;
@@ -92,8 +56,6 @@ static char *dentry_name(struct dentry *dentry, int extra)
parent = parent->d_parent;
}
- root = parent->d_inode->u.hostfs_i.host_filename;
- len += strlen(root);
name = kmalloc(len + extra + 1, GFP_KERNEL);
if(name == NULL) return(NULL);
@@ -106,7 +68,7 @@ static char *dentry_name(struct dentry *dentry, int extra)
parent->d_name.len);
parent = parent->d_parent;
}
- strncpy(name, root, strlen(root));
+
return(name);
}
@@ -120,6 +82,7 @@ static char *inode_name(struct inode *ino, int extra)
static int read_name(struct inode *ino, char *name)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
/* The non-int inode fields are copied into ints by stat_file and
* then copied into the inode because passing the actual pointers
* in and having them treated as int * breaks on big-endian machines
@@ -129,9 +92,11 @@ static int read_name(struct inode *ino, char *name)
unsigned long long i_size;
unsigned long long i_ino;
unsigned long long i_blocks;
- err = stat_file(name, &i_dev, &i_ino, &i_mode, &i_nlink,
- &ino->i_uid, &ino->i_gid, &i_size, &ino->i_atime,
- &ino->i_mtime, &ino->i_ctime, &i_blksize, &i_blocks);
+
+ err = (*ops->stat_file)(name, ino->i_sb->u.generic_sbp, &i_dev, &i_ino,
+ &i_mode, &i_nlink, &ino->i_uid, &ino->i_gid,
+ &i_size, &ino->i_atime, &ino->i_mtime,
+ &ino->i_ctime, &i_blksize, &i_blocks);
if(err) return(err);
ino->i_ino = i_ino;
ino->i_dev = i_dev;
@@ -140,12 +105,13 @@ static int read_name(struct inode *ino, char *name)
ino->i_size = i_size;
ino->i_blksize = i_blksize;
ino->i_blocks = i_blocks;
- if(kdev_same(ino->i_sb->s_dev, ROOT_DEV) && (ino->i_uid == getuid()))
- ino->i_uid = 0;
return(0);
}
-static char *follow_link(char *link)
+static char *follow_link(char *link,
+ int (*do_readlink)(char *path, int uid, int gid,
+ char *buf, int size, void *mount),
+ int uid, int gid, void *mount)
{
int len, n;
char *name, *resolved, *end;
@@ -157,7 +123,7 @@ static char *follow_link(char *link)
if(name == NULL)
goto out;
- n = do_readlink(link, name, len);
+ n = (*do_readlink)(link, uid, gid, name, len, mount);
if(n < len)
break;
len *= 2;
@@ -195,16 +161,25 @@ static char *follow_link(char *link)
static int read_inode(struct inode *ino)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *name;
- int err;
+ int err, type;
err = -ENOMEM;
name = inode_name(ino, 0);
if(name == NULL)
goto out;
- if(file_type(name, NULL) == OS_TYPE_SYMLINK){
- name = follow_link(name);
+ type = (*ops->file_type)(name, NULL, mount);
+ if(type < 0){
+ err = type;
+ goto out;
+ }
+
+ if(type == OS_TYPE_SYMLINK){
+ name = follow_link(name, ops->readlink, current->fsuid,
+ current->fsgid, mount);
if(IS_ERR(name)){
err = PTR_ERR(name);
goto out;
@@ -219,12 +194,11 @@ static int read_inode(struct inode *ino)
void hostfs_delete_inode(struct inode *ino)
{
- if(ino->u.hostfs_i.host_filename)
- kfree(ino->u.hostfs_i.host_filename);
- ino->u.hostfs_i.host_filename = NULL;
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
if(ino->u.hostfs_i.fd != -1)
- close_file(&ino->u.hostfs_i.fd);
+ (*ops->close_file)(&ino->u.hostfs_i.fd, ino->i_size, mount);
ino->u.hostfs_i.mode = 0;
clear_inode(ino);
@@ -242,11 +216,15 @@ int hostfs_statfs(struct super_block *sb, struct statfs *sf)
long long f_bavail;
long long f_files;
long long f_ffree;
-
- err = do_statfs(sb->s_root->d_inode->u.hostfs_i.host_filename,
- &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);
+ struct inode *ino = sb->s_root->d_inode;
+ void *mount = sb->u.generic_sbp;
+
+ err = (*ino->u.hostfs_i.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,
+ mount);
if(err) return(err);
sf->f_blocks = f_blocks;
sf->f_bfree = f_bfree;
@@ -269,25 +247,30 @@ int hostfs_readdir(struct file *file, void *ent, filldir_t filldir)
char *name;
unsigned long long next, ino;
int error, len;
+ struct externfs_file_ops *ops = file_hostfs_i(file)->ops;
+ void *mount = file->f_dentry->d_inode->i_sb->u.generic_sbp;
name = dentry_name(file->f_dentry, 0);
if(name == NULL) return(-ENOMEM);
- dir = open_dir(name, &error);
+ dir = (*ops->open_dir)(name, current->fsuid, current->fsgid, &error,
+ mount);
kfree(name);
if(dir == NULL) return(-error);
next = file->f_pos;
- while((name = read_dir(dir, &next, &ino, &len)) != NULL){
+ while((name = (*ops->read_dir)(dir, &next, &ino, &len, mount)) != NULL){
error = (*filldir)(ent, name, len, file->f_pos,
ino, DT_UNKNOWN);
if(error) break;
file->f_pos = next;
}
- close_dir(dir);
+ (*ops->close_dir)(dir, mount);
return(0);
}
int hostfs_file_open(struct inode *ino, struct file *file)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *name;
int mode = 0, r = 0, w = 0, fd;
@@ -299,7 +282,7 @@ int hostfs_file_open(struct inode *ino, struct file *file)
* so this resets things and reopens the file with the new access.
*/
if(ino->u.hostfs_i.fd != -1){
- close_file(&ino->u.hostfs_i.fd);
+ (*ops->close_file)(&ino->u.hostfs_i.fd, ino->i_size, mount);
ino->u.hostfs_i.fd = -1;
}
@@ -315,7 +298,8 @@ int hostfs_file_open(struct inode *ino, struct file *file)
if(name == NULL)
return(-ENOMEM);
- fd = open_file(name, r, w, append);
+ fd = (*ops->open_file)(name, current->fsuid, current->fsgid, r, w,
+ mount);
kfree(name);
if(fd < 0) return(fd);
file_hostfs_i(file)->fd = fd;
@@ -360,6 +344,8 @@ int hostfs_writepage(struct page *page)
{
struct address_space *mapping = page->mapping;
struct inode *inode = mapping->host;
+ struct externfs_file_ops *ops = inode->u.hostfs_i.ops;
+ void *mount = inode->i_sb->u.generic_sbp;
char *buffer;
unsigned long long base;
int count = PAGE_CACHE_SIZE;
@@ -372,11 +358,13 @@ int hostfs_writepage(struct page *page)
buffer = kmap(page);
base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;
- err = write_file(inode->u.hostfs_i.fd, &base, buffer, count);
+ err = (*ops->write_file)(inode->u.hostfs_i.fd, base, buffer, count,
+ mount);
if(err != count){
ClearPageUptodate(page);
goto out;
}
+ base += count;
if (base > inode->i_size)
inode->i_size = base;
@@ -392,50 +380,117 @@ int hostfs_writepage(struct page *page)
return err;
}
-int hostfs_readpage(struct file *file, struct page *page)
+static void hostfs_finish_readpage(char *buffer, int res, void *arg)
{
- char *buffer;
- long long start;
- int err = 0;
+ struct page *page = arg;
- start = (long long) page->index << PAGE_CACHE_SHIFT;
- buffer = kmap(page);
- err = read_file(file_hostfs_i(file)->fd, &start, buffer,
- PAGE_CACHE_SIZE);
- if(err < 0) goto out;
+ if(res < 0){
+ SetPageError(page);
+ goto out;
+ }
- memset(&buffer[err], 0, PAGE_CACHE_SIZE - err);
+ memset(&buffer[res], 0, PAGE_CACHE_SIZE - res);
flush_dcache_page(page);
SetPageUptodate(page);
- if (PageError(page)) ClearPageError(page);
- err = 0;
+ if (PageError(page))
+ ClearPageError(page);
out:
kunmap(page);
UnlockPage(page);
+}
+
+static int hostfs_readpage(struct file *file, struct page *page)
+{
+ struct inode *ino = page->mapping->host;
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
+ 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_hostfs_i(file)->fd, start,
+ buffer, file->f_mode & FMODE_WRITE,
+ mount);
+ if(!err)
+ err = PAGE_CACHE_SIZE;
+ }
+ else err = (*ops->read_file)(file_hostfs_i(file)->fd, start, buffer,
+ PAGE_CACHE_SIZE, hostfs_finish_readpage,
+ page, mount);
+
+ if(err > 0)
+ err = 0;
return(err);
}
+struct writepage_info {
+ struct semaphore sem;
+ int res;
+};
+
+static void hostfs_finish_prepare(char *buffer, int res, void *arg)
+{
+ struct writepage_info *wp = arg;
+
+ wp->res = res;
+ up(&wp->sem);
+}
+
int hostfs_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 = inode->u.hostfs_i.ops;
+ void *mount = inode->i_sb->u.generic_sbp;
char *buffer;
- long long start, tmp;
+ long long start;
int err;
+ struct writepage_info wp;
start = (long long) page->index << PAGE_CACHE_SHIFT;
buffer = kmap(page);
+
+ if(ops->map_file_page != NULL){
+ err = (*ops->map_file_page)(file_hostfs_i(file)->fd, start,
+ buffer, file->f_mode & FMODE_WRITE,
+ mount);
+ goto out;
+
+ }
+
if(from != 0){
- tmp = start;
- err = read_file(file_hostfs_i(file)->fd, &tmp, buffer,
- from);
- if(err < 0) goto out;
+ init_MUTEX_LOCKED(&wp.sem);
+ err = (*ops->read_file)(file_hostfs_i(file)->fd, start, buffer,
+ from, hostfs_finish_prepare, &wp,
+ mount);
+ down(&wp.sem);
+ if(err < 0)
+ goto out;
+
+ err = wp.res;
+ if(err < 0)
+ goto out;
}
if(to != PAGE_CACHE_SIZE){
start += to;
- err = read_file(file_hostfs_i(file)->fd, &start, buffer + to,
- PAGE_CACHE_SIZE - to);
- if(err < 0) goto out;
+ init_MUTEX_LOCKED(&wp.sem);
+ err = (*ops->read_file)(file_hostfs_i(file)->fd, start,
+ buffer + to, PAGE_CACHE_SIZE - to,
+ hostfs_finish_prepare, &wp, mount);
+ down(&wp.sem);
+ if(err < 0)
+ goto out;
+
+ err = wp.res;
+ if(err < 0)
+ goto out;
}
err = 0;
out:
@@ -443,37 +498,57 @@ int hostfs_prepare_write(struct file *file, struct page *page,
return(err);
}
-int hostfs_commit_write(struct file *file, struct page *page, unsigned from,
- unsigned to)
+static int hostfs_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 = inode->u.hostfs_i.ops;
+ void *mount = inode->i_sb->u.generic_sbp;
char *buffer;
long long start;
- int err = 0;
+ int err;
start = (long long) (page->index << PAGE_CACHE_SHIFT) + from;
- buffer = kmap(page);
- err = write_file(file_hostfs_i(file)->fd, &start, buffer + from,
- to - from);
- if(err > 0) err = 0;
- if(!err && (start > inode->i_size))
+
+ if(ops->map_file_page != NULL)
+ err = to - from;
+ else {
+ buffer = kmap(page);
+ err = (*ops->write_file)(file_hostfs_i(file)->fd, start,
+ buffer + from, to - from, mount);
+ if(err < 0)
+ goto out;
+ }
+
+ start += err;
+ err = 0;
+ if(start > inode->i_size)
inode->i_size = start;
+ out:
kunmap(page);
return(err);
}
+static void hostfs_removepage(struct page *page)
+{
+ physmem_remove_mapping(page_address(page));
+}
+
static struct address_space_operations hostfs_aops = {
.writepage = hostfs_writepage,
.readpage = hostfs_readpage,
+ .removepage = hostfs_removepage,
/* .set_page_dirty = __set_page_dirty_nobuffers, */
.prepare_write = hostfs_prepare_write,
.commit_write = hostfs_commit_write
};
+static struct externfs *find_externfs(struct file_system_type *type);
+
static struct inode *get_inode(struct super_block *sb, struct dentry *dentry,
- int *error)
+ struct externfs_file_ops *ops, int *error)
{
struct inode *inode;
char *name;
@@ -483,9 +558,9 @@ static struct inode *get_inode(struct super_block *sb, struct dentry *dentry,
if(inode == NULL)
goto out;
- inode->u.hostfs_i.host_filename = NULL;
inode->u.hostfs_i.fd = -1;
inode->u.hostfs_i.mode = 0;
+ inode->u.hostfs_i.ops = ops;
insert_inode_hash(inode);
if(dentry){
name = dentry_name(dentry, 0);
@@ -493,7 +568,7 @@ static struct inode *get_inode(struct super_block *sb, struct dentry *dentry,
err = -ENOMEM;
goto out_put;
}
- type = file_type(name, &rdev);
+ type = (*ops->file_type)(name, &rdev, sb->u.generic_sbp);
kfree(name);
}
else type = OS_TYPE_DIR;
@@ -540,21 +615,25 @@ static struct inode *get_inode(struct super_block *sb, struct dentry *dentry,
int hostfs_create(struct inode *dir, struct dentry *dentry, int mode)
{
+ struct externfs_file_ops *ops = dir->u.hostfs_i.ops;
struct inode *inode;
+ void *mount = dir->i_sb->u.generic_sbp;
char *name;
int error, fd;
- inode = get_inode(dir->i_sb, dentry, &error);
+ inode = get_inode(dir->i_sb, dentry, ops, &error);
if(error) return(error);
name = dentry_name(dentry, 0);
if(name == NULL){
iput(inode);
return(-ENOMEM);
}
- fd = file_create(name,
- mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR,
- mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP,
- mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH);
+ fd = (*ops->file_create)(name, current->fsuid, current->fsuid,
+ mode & S_IRUSR, mode & S_IWUSR,
+ mode & S_IXUSR, mode & S_IRGRP,
+ mode & S_IWGRP, mode & S_IXGRP,
+ mode & S_IROTH, mode & S_IWOTH,
+ mode & S_IXOTH, mount);
if(fd < 0)
error = fd;
else error = read_name(inode, name);
@@ -573,10 +652,11 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode)
struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry)
{
struct inode *inode;
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
char *name;
int error;
- inode = get_inode(ino->i_sb, dentry, &error);
+ inode = get_inode(ino->i_sb, dentry, ops, &error);
if(error != 0) return(ERR_PTR(error));
name = dentry_name(dentry, 0);
if(name == NULL) return(ERR_PTR(-ENOMEM));
@@ -608,6 +688,8 @@ static char *inode_dentry_name(struct inode *ino, struct dentry *dentry)
int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *from_name, *to_name;
int err;
@@ -619,7 +701,8 @@ int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
kfree(from_name);
return(-ENOMEM);
}
- err = link_file(to_name, from_name);
+ err = (*ops->link_file)(to_name, from_name, current->fsuid,
+ current->fsgid, mount);
kfree(from_name);
kfree(to_name);
return(err);
@@ -627,71 +710,90 @@ int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
int hostfs_unlink(struct inode *ino, struct dentry *dentry)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *file;
int err;
file = inode_dentry_name(ino, dentry);
- if(file == NULL) return(-ENOMEM);
- if(append)
- return(-EPERM);
+ if(file == NULL)
+ return(-ENOMEM);
- err = unlink_file(file);
+ err = (*ops->unlink_file)(file, mount);
kfree(file);
return(err);
}
int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *file;
int err;
file = inode_dentry_name(ino, dentry);
- if(file == NULL) return(-ENOMEM);
- err = make_symlink(file, to);
+ if(file == NULL)
+ return(-ENOMEM);
+ err = (*ops->make_symlink)(file, to, current->fsuid, current->fsgid,
+ mount);
kfree(file);
return(err);
}
int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *file;
int err;
file = inode_dentry_name(ino, dentry);
- if(file == NULL) return(-ENOMEM);
- err = do_mkdir(file, mode);
+ if(file == NULL)
+ return(-ENOMEM);
+ err = (*ops->mkdir)(file, mode, current->fsuid, current->fsgid, mount);
kfree(file);
return(err);
}
int hostfs_rmdir(struct inode *ino, struct dentry *dentry)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *file;
int err;
file = inode_dentry_name(ino, dentry);
if(file == NULL) return(-ENOMEM);
- err = do_rmdir(file);
+ err = (*ops->rmdir)(file, current->fsuid, current->fsgid, mount);
kfree(file);
return(err);
}
int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
{
+ struct externfs_file_ops *ops = dir->u.hostfs_i.ops;
+ void *mount = dir->i_sb->u.generic_sbp;
struct inode *inode;
char *name;
int error;
- inode = get_inode(dir->i_sb, dentry, &error);
- if(error) return(error);
+ inode = get_inode(dir->i_sb, dentry, ops, &error);
+ if(error)
+ return(error);
name = dentry_name(dentry, 0);
if(name == NULL){
iput(inode);
return(-ENOMEM);
}
init_special_inode(inode, mode, dev);
- error = do_mknod(name, mode, dev);
- if(!error) error = read_name(inode, name);
+ error = (*ops->mknod)(name, current->fsuid, current->fsgid,
+ mode & S_IRUSR, mode & S_IWUSR,
+ mode & S_IXUSR, mode & S_IRGRP,
+ mode & S_IWGRP, mode & S_IXGRP,
+ mode & S_IROTH, mode & S_IWOTH,
+ mode & S_IXOTH, mode & S_IFMT, dev, mount);
+ if(!error)
+ error = read_name(inode, name);
kfree(name);
if(error){
iput(inode);
@@ -704,6 +806,8 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
int hostfs_rename(struct inode *from_ino, struct dentry *from,
struct inode *to_ino, struct dentry *to)
{
+ struct externfs_file_ops *ops = from_ino->u.hostfs_i.ops;
+ void *mount = from_ino->i_sb->u.generic_sbp;
char *from_name, *to_name;
int err;
@@ -715,7 +819,7 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
kfree(from_name);
return(-ENOMEM);
}
- err = rename_file(from_name, to_name);
+ err = (*ops->rename_file)(from_name, to_name, mount);
kfree(from_name);
kfree(to_name);
return(err);
@@ -723,50 +827,56 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
void hostfs_truncate(struct inode *ino)
{
- not_implemented();
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
+
+ (*ops->truncate_file)(ino->u.hostfs_i.fd, ino->i_size, mount);
}
int hostfs_permission(struct inode *ino, int desired)
{
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *name;
int r = 0, w = 0, x = 0, err;
+ if(ops->access_file == NULL)
+ return(vfs_permission(ino, desired));
+
if(desired & MAY_READ) r = 1;
if(desired & MAY_WRITE) w = 1;
if(desired & MAY_EXEC) x = 1;
name = inode_name(ino, 0);
- if(name == NULL) return(-ENOMEM);
- err = access_file(name, r, w, x);
+ if(name == NULL)
+ return(-ENOMEM);
+
+ err = (*ops->access_file)(name, r, w, x, current->fsuid, current->fsgid,
+ mount);
kfree(name);
- if(!err) err = vfs_permission(ino, desired);
+
+ if(!err)
+ err = vfs_permission(ino, desired);
return(err);
}
int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
{
+ struct externfs_file_ops *ops = dentry->d_inode->u.hostfs_i.ops;
+ void *mount = dentry->d_inode->i_sb->u.generic_sbp;
struct hostfs_iattr attrs;
char *name;
int err;
- if(append)
- attr->ia_valid &= ~ATTR_SIZE;
-
attrs.ia_valid = 0;
if(attr->ia_valid & ATTR_MODE){
attrs.ia_valid |= HOSTFS_ATTR_MODE;
attrs.ia_mode = attr->ia_mode;
}
if(attr->ia_valid & ATTR_UID){
- if(kdev_same(dentry->d_inode->i_sb->s_dev, ROOT_DEV) &&
- (attr->ia_uid == 0))
- attr->ia_uid = getuid();
attrs.ia_valid |= HOSTFS_ATTR_UID;
attrs.ia_uid = attr->ia_uid;
}
if(attr->ia_valid & ATTR_GID){
- if(kdev_same(dentry->d_inode->i_sb->s_dev, ROOT_DEV) &&
- (attr->ia_gid == 0))
- attr->ia_gid = getuid();
attrs.ia_valid |= HOSTFS_ATTR_GID;
attrs.ia_gid = attr->ia_gid;
}
@@ -793,8 +903,9 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET;
}
name = dentry_name(dentry, 0);
- if(name == NULL) return(-ENOMEM);
- err = set_attr(name, &attrs);
+ if(name == NULL)
+ return(-ENOMEM);
+ err = (*ops->set_attr)(name, &attrs, mount);
kfree(name);
if(err)
return(err);
@@ -841,15 +952,19 @@ static struct inode_operations hostfs_dir_iops = {
int hostfs_link_readpage(struct file *file, struct page *page)
{
+ struct inode *ino = page->mapping->host;
+ struct externfs_file_ops *ops = ino->u.hostfs_i.ops;
+ void *mount = ino->i_sb->u.generic_sbp;
char *buffer, *name;
long long start;
int err;
start = page->index << PAGE_CACHE_SHIFT;
buffer = kmap(page);
- name = inode_name(page->mapping->host, 0);
+ name = inode_name(ino, 0);
if(name == NULL) return(-ENOMEM);
- err = do_readlink(name, buffer, PAGE_CACHE_SIZE);
+ err = (*ops->readlink)(name, current->fsuid, current->fsgid, buffer,
+ PAGE_CACHE_SIZE, mount);
kfree(name);
if(err == PAGE_CACHE_SIZE)
err = -E2BIG;
@@ -866,70 +981,110 @@ int hostfs_link_readpage(struct file *file, struct page *page)
static struct address_space_operations hostfs_link_aops = {
.readpage = hostfs_link_readpage,
+ .removepage = hostfs_removepage,
};
-static char *get_root(char *mount_arg)
+DECLARE_MUTEX(externfs_sem);
+struct list_head externfses = LIST_HEAD_INIT(externfses);
+
+static struct externfs *find_externfs(struct file_system_type *type)
{
- char *root, *slash = "";
- int len = 0;
+ struct list_head *ele;
+ struct externfs *fs;
- if(jail_dir != NULL){
- len += strlen(jail_dir);
- if((*jail_dir == '\0') ||
- (jail_dir[strlen(jail_dir) - 1] != '/'))
- slash = "/";
- len += strlen(slash);
+ 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 "/"
+
+int host_root_filename(char *mount_arg, void **mount_data_out)
+{
+ *mount_data_out = NULL;
if((mount_arg == NULL) || (*mount_arg == '\0'))
- mount_arg = DEFAULT_ROOT;
+ *mount_data_out = DEFAULT_ROOT;
+ else *mount_data_out = mount_arg;
- len += strlen(mount_arg) + 1;
+ return(0);
+}
- root = kmalloc(len, GFP_KERNEL);
- if(root == NULL)
- return(NULL);
+int host_read_file(int fd, unsigned long long offset, char *buf, int len)
+{
+ int err;
- if(jail_dir != NULL)
- sprintf(root, "%s%s%s", jail_dir, slash, mount_arg);
- else
- strcpy(root, mount_arg);
+ err = os_seek_file(fd, offset);
+ if(err)
+ return(err);
- return(root);
+ return(os_read_file(fd, buf, len));
+}
+
+int host_write_file(int fd, unsigned long long offset, const char *buf,
+ int len)
+{
+ int err;
+
+ err = os_seek_file(fd, offset);
+ if(err)
+ return(err);
+
+ return(os_write_file(fd, buf, len));
+}
+
+void host_close_file(void *stream)
+{
+ os_close_file(*((int *) stream));
}
struct super_block *hostfs_read_super(struct super_block *sb, void *data,
int silent)
{
+ struct externfs *fs;
struct inode *root_inode;
- char *root_dir;
+ struct externfs_file_ops *file_ops;
sb->s_blocksize = 1024;
sb->s_blocksize_bits = 10;
sb->s_magic = HOSTFS_SUPER_MAGIC;
sb->s_op = &hostfs_sbops;
- root_inode = get_inode(sb, NULL, NULL);
- if(root_inode == NULL)
+ fs = find_externfs(sb->s_type);
+ if(fs == NULL){
+ printk("Couldn't find externfs for filesystem '%s'\n",
+ sb->s_type->name);
goto out;
+ }
- root_dir = get_root(data);
- if(root_dir == NULL)
- goto out_put;
+ file_ops = (*fs->ops->mount)(data, &sb->u.generic_sbp);
+ if(file_ops == NULL)
+ goto out;
+
+ root_inode = get_inode(sb, NULL, file_ops, NULL);
+ if(root_inode == NULL)
+ goto out;
+ root_inode->u.hostfs_i.ops = file_ops;
- root_inode->u.hostfs_i.host_filename = root_dir;
sb->s_root = d_alloc_root(root_inode);
if(sb->s_root == NULL)
- goto out_free;
+ goto out_put;
if(read_inode(root_inode))
goto out_dput;
return(sb);
out_dput:
+ /* dput frees the inode */
dput(sb->s_root);
- out_free:
- kfree(root_dir);
+ return(NULL);
out_put:
make_bad_inode(root_inode);
iput(root_inode);
@@ -937,22 +1092,54 @@ struct super_block *hostfs_read_super(struct super_block *sb, void *data,
return(NULL);
}
-DECLARE_FSTYPE(hostfs_type, "hostfs", hostfs_read_super, 0);
-
-static int __init init_hostfs(void)
+int register_externfs(char *name, struct externfs_mount_ops *ops)
{
- return(register_filesystem(&hostfs_type));
+ 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),
+ .ops = ops,
+ .type = { .name = name,
+ .read_super = hostfs_read_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);
}
-static void __exit exit_hostfs(void)
+void unregister_externfs(char *name)
{
- unregister_filesystem(&hostfs_type);
+ 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);
+ up(&externfs_sem);
+ return;
+ }
+ }
+ up(&externfs_sem);
+ printk("Unregister_externfs - filesystem '%s' not found\n", name);
}
-module_init(init_hostfs)
-module_exit(exit_hostfs)
-MODULE_LICENSE("GPL");
-
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
diff --git a/arch/um/fs/hostfs/hostfs_user.c b/arch/um/fs/hostfs/hostfs_user.c
index eba3b6a..3e59096 100644
--- a/arch/um/fs/hostfs/hostfs_user.c
+++ b/arch/um/fs/hostfs/hostfs_user.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
@@ -16,337 +16,298 @@
#include "hostfs.h"
#include "kern_util.h"
#include "user.h"
+#include "init.h"
-int stat_file(const char *path, int *dev_out, unsigned long long *inode_out,
- int *mode_out, int *nlink_out, int *uid_out, int *gid_out,
- unsigned long long *size_out, unsigned long *atime_out,
- unsigned long *mtime_out, unsigned long *ctime_out,
- int *blksize_out, unsigned long long *blocks_out)
-{
- struct stat64 buf;
-
- if(lstat64(path, &buf) < 0)
- return(-errno);
- if(dev_out != NULL) *dev_out = buf.st_dev;
-
- /* See the Makefile for why STAT64_INO_FIELD is passed in
- * by the build
- */
- if(inode_out != NULL) *inode_out = buf.STAT64_INO_FIELD;
- if(mode_out != NULL) *mode_out = buf.st_mode;
- if(nlink_out != NULL) *nlink_out = buf.st_nlink;
- if(uid_out != NULL) *uid_out = buf.st_uid;
- if(gid_out != NULL) *gid_out = buf.st_gid;
- if(size_out != NULL) *size_out = buf.st_size;
- if(atime_out != NULL) *atime_out = buf.st_atime;
- if(mtime_out != NULL) *mtime_out = buf.st_mtime;
- if(ctime_out != NULL) *ctime_out = buf.st_ctime;
- if(blksize_out != NULL) *blksize_out = buf.st_blksize;
- if(blocks_out != NULL) *blocks_out = buf.st_blocks;
- return(0);
-}
+/* Changed in hostfs_args before the kernel starts running */
+static char *jail_dir = "/";
+int append = 0;
-int file_type(const char *path, int *rdev)
+static int __init hostfs_args(char *options, int *add)
{
- struct stat64 buf;
-
- if(lstat64(path, &buf) < 0)
- return(-errno);
- if(rdev != NULL)
- *rdev = buf.st_rdev;
-
- if(S_ISDIR(buf.st_mode)) return(OS_TYPE_DIR);
- else if(S_ISLNK(buf.st_mode)) return(OS_TYPE_SYMLINK);
- else if(S_ISCHR(buf.st_mode)) return(OS_TYPE_CHARDEV);
- else if(S_ISBLK(buf.st_mode)) return(OS_TYPE_BLOCKDEV);
- else if(S_ISFIFO(buf.st_mode))return(OS_TYPE_FIFO);
- else if(S_ISSOCK(buf.st_mode))return(OS_TYPE_SOCK);
- else return(OS_TYPE_FILE);
+ 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);
}
-int access_file(char *path, int r, int w, int x)
+__uml_setup("hostfs=", hostfs_args,
+"hostfs=<root dir>,<flags>,...\n"
+" This is used to set hostfs parameters. The root directory argument\n"
+" is used to confine all hostfs mounts to within the specified directory\n"
+" tree on the host. If this isn't specified, then a user inside UML can\n"
+" mount anything on the host that's accessible to the user that's running\n"
+" it.\n"
+" The only flag currently supported is 'append', which specifies that all\n"
+" files opened by hostfs will be opened in append mode.\n\n"
+);
+
+static int access_file(char *file, int uid, int gid, int r, int w, int x,
+ void *mount)
{
- int mode = 0;
+ const char *path[] = { jail_dir, file, NULL };
+ char tmp[HOSTFS_BUFSIZE];
+ int err, mode = 0;
if(r) mode = R_OK;
if(w) mode |= W_OK;
if(x) mode |= X_OK;
- if(access(path, mode) != 0) return(-errno);
- else return(0);
+
+ err = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = 0;
+ if(access(file, mode) != 0)
+ err = -errno;
+
+ free_path(file, tmp);
+ out:
+ return(err);
}
-int open_file(char *path, int r, int w, int append)
+static int mk_nod(const char *file, int uid, int gid, int ur, int uw, int ux,
+ int gr, int gw, int gx, int or, int ow, int ox,
+ int type, int dev, void *mount)
{
- int mode = 0, fd;
-
- if(r && !w)
- mode = O_RDONLY;
- else if(!r && w)
- mode = O_WRONLY;
- else if(r && w)
- mode = O_RDWR;
- else panic("Impossible mode in open_file");
-
- if(append)
- mode |= O_APPEND;
- fd = open64(path, mode);
- if(fd < 0) return(-errno);
- else return(fd);
+ const char *path[] = { jail_dir, file, NULL };
+ char tmp[HOSTFS_BUFSIZE];
+ int err = -ENOMEM;
+ int mode = 0;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ mode |= ur ? S_IRUSR : 0;
+ mode |= uw ? S_IWUSR : 0;
+ mode |= ux ? S_IXUSR : 0;
+ mode |= gr ? S_IRGRP : 0;
+ mode |= gw ? S_IWGRP : 0;
+ mode |= gx ? S_IXGRP : 0;
+ mode |= or ? S_IROTH : 0;
+ mode |= ow ? S_IWOTH : 0;
+ mode |= ox ? S_IXOTH : 0;
+
+ /* XXX Pass type in an OS-independent way */
+ mode |= type;
+
+ err = mknod(file, mode, dev);
+ if(err)
+ err = -errno;
+
+ free_path(file, tmp);
+ out:
+ return(err);
}
-void *open_dir(char *path, int *err_out)
+static int stat_file(const char *file, void *mount, int *dev_out,
+ unsigned long long *inode_out, int *mode_out,
+ int *nlink_out, int *uid_out, int *gid_out,
+ unsigned long long *size_out,
+ unsigned long *atime_out, unsigned long *mtime_out,
+ unsigned long *ctime_out, int *blksize_out,
+ unsigned long long *blocks_out)
{
- DIR *dir;
+ const char *path[] = { jail_dir, file, NULL };
- dir = opendir(path);
- *err_out = errno;
- if(dir == NULL) return(NULL);
- return(dir);
+ return(host_stat_file(path, dev_out, inode_out, mode_out, nlink_out,
+ uid_out, gid_out, size_out, atime_out, mtime_out,
+ ctime_out, blksize_out, blocks_out));
}
-char *read_dir(void *stream, unsigned long long *pos,
- unsigned long long *ino_out, int *len_out)
+static int file_type(const char *file, int *rdev, void *mount)
{
- DIR *dir = stream;
- struct dirent *ent;
-
- seekdir(dir, *pos);
- ent = readdir(dir);
- if(ent == NULL) return(NULL);
- *len_out = strlen(ent->d_name);
- *ino_out = ent->d_ino;
- *pos = telldir(dir);
- return(ent->d_name);
+ const char *path[] = { jail_dir, file, NULL };
+
+ return(host_file_type(path, rdev));
}
-int read_file(int fd, unsigned long long *offset, char *buf, int len)
+static int open_file(char *file, int uid, int gid, int r, int w, void *mount)
{
- int n, err;
+ const char *path[] = { jail_dir, file, NULL };
- err = os_seek_file(fd, *offset);
- if(err)
- return(err);
+ return(host_open_file(path, r, w));
+}
- n = os_read_file(fd, buf, len);
- if(n < 0)
- return(n);
+static void *open_dir(char *file, int uid, int gid, int *err_out, void *mount)
+{
+ const char *path[] = { jail_dir, file, NULL };
- *offset += n;
- return(n);
+ return(host_open_dir(path, err_out));
}
-int write_file(int fd, unsigned long long *offset, const char *buf, int len)
+static int read_file(int fd, unsigned long long offset, char *buf, int len,
+ void (*completion)(char *, int, void *), void *arg,
+ void *mount)
{
- int n, err;
+ int err = host_read_file(fd, offset, buf, len);
- err = os_seek_file(fd, *offset);
- if(err)
- return(err);
+ (*completion)(buf, err, arg);
- n = os_write_file(fd, buf, len);
- if(n < 0)
- return(n);
-
- *offset += n;
- return(n);
+ return(err);
}
-int lseek_file(int fd, long long offset, int whence)
+static int file_create(char *file, int uid, int gid, int ur, int uw, int ux,
+ int gr, int gw, int gx, int or, int ow, int ox,
+ void *mount)
{
- int ret;
+ const char *path[] = { jail_dir, file, NULL };
- ret = lseek64(fd, offset, whence);
- if(ret < 0) return(-errno);
- return(0);
+ return(host_file_create(path, ur, uw, ux, gr, gw, gx, or, ow, ox));
}
-void close_file(void *stream)
+static int set_attr(const char *file, struct hostfs_iattr *attrs,
+ void *mount)
{
- close(*((int *) stream));
-}
+ const char *path[] = { jail_dir, file, NULL };
-void close_dir(void *stream)
-{
- closedir(stream);
+ return(host_set_attr(path, attrs));
}
-int file_create(char *name, int ur, int uw, int ux, int gr,
- int gw, int gx, int or, int ow, int ox)
+static int make_symlink(const char *from, const char *to, int uid, int gid,
+ void *mount)
{
- int mode, fd;
+ const char *path[] = { jail_dir, from, NULL };
- mode = 0;
- mode |= ur ? S_IRUSR : 0;
- mode |= uw ? S_IWUSR : 0;
- mode |= ux ? S_IXUSR : 0;
- mode |= gr ? S_IRGRP : 0;
- mode |= gw ? S_IWGRP : 0;
- mode |= gx ? S_IXGRP : 0;
- mode |= or ? S_IROTH : 0;
- mode |= ow ? S_IWOTH : 0;
- mode |= ox ? S_IXOTH : 0;
- fd = open64(name, O_CREAT | O_RDWR, mode);
- if(fd < 0)
- return(-errno);
- return(fd);
+ return(host_make_symlink(path, to));
}
-int set_attr(const char *file, struct hostfs_iattr *attrs)
+static int unlink_file(const char *file, void *mount)
{
- struct utimbuf buf;
- int err, ma;
+ const char *path[] = { jail_dir, file, NULL };
- if(attrs->ia_valid & HOSTFS_ATTR_MODE){
- if(chmod(file, attrs->ia_mode) != 0) return(-errno);
- }
- if(attrs->ia_valid & HOSTFS_ATTR_UID){
- if(chown(file, attrs->ia_uid, -1)) return(-errno);
- }
- if(attrs->ia_valid & HOSTFS_ATTR_GID){
- if(chown(file, -1, attrs->ia_gid)) return(-errno);
- }
- if(attrs->ia_valid & HOSTFS_ATTR_SIZE){
- if(truncate(file, attrs->ia_size)) return(-errno);
- }
- ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET;
- if((attrs->ia_valid & ma) == ma){
- buf.actime = attrs->ia_atime;
- buf.modtime = attrs->ia_mtime;
- if(utime(file, &buf) != 0) return(-errno);
- }
- else {
- if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){
- err = stat_file(file, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, &buf.modtime, NULL,
- NULL, NULL);
- if(err != 0) return(err);
- buf.actime = attrs->ia_atime;
- if(utime(file, &buf) != 0) return(-errno);
- }
- if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){
- err = stat_file(file, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, &buf.actime, NULL, NULL,
- NULL, NULL);
- if(err != 0) return(err);
- buf.modtime = attrs->ia_mtime;
- if(utime(file, &buf) != 0) return(-errno);
- }
- }
- if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ;
- if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){
- err = stat_file(file, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, &attrs->ia_atime, &attrs->ia_mtime,
- NULL, NULL, NULL);
- if(err != 0) return(err);
- }
- return(0);
+ return(host_unlink_file(path));
}
-int make_symlink(const char *from, const char *to)
+static int mk_dir(const char *file, int mode, int uid, int gid, void *mount)
{
- int err;
+ const char *path[] = { jail_dir, file, NULL };
- err = symlink(to, from);
- if(err) return(-errno);
- return(0);
+ return(host_mkdir(path, mode));
}
-int unlink_file(const char *file)
+static int rm_dir(const char *file, int uid, int gid, void *mount)
{
- int err;
+ const char *path[] = { jail_dir, file, NULL };
- err = unlink(file);
- if(err) return(-errno);
- return(0);
+ return(host_rmdir(path));
}
-int do_mkdir(const char *file, int mode)
+static int link_file(const char *to, const char *from, int uid, int gid,
+ void *mount)
{
- int err;
+ const char *to_path[] = { jail_dir, to, NULL };
+ const char *from_path[] = { jail_dir, from, NULL };
- err = mkdir(file, mode);
- if(err) return(-errno);
- return(0);
+ return(host_link_file(to_path, from_path));
}
-int do_rmdir(const char *file)
+static int read_link(char *file, int uid, int gid, char *buf, int size,
+ void *mount)
{
- int err;
+ const char *path[] = { jail_dir, file, NULL };
- err = rmdir(file);
- if(err) return(-errno);
- return(0);
+ return(host_readlink(path, buf, size));
}
-int do_mknod(const char *file, int mode, int dev)
+static int rename_file(char *from, char *to, void *mount)
{
- int err;
+ const char *to_path[] = { jail_dir, to, NULL };
+ const char *from_path[] = { jail_dir, from, NULL };
- err = mknod(file, mode, dev);
- if(err) return(-errno);
- return(0);
+ return(host_rename_file(to_path, from_path));
}
-int link_file(const char *to, const char *from)
+static int 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, void *mount)
{
- int err;
+ const char *path[] = { jail_dir, mount, NULL };
- err = link(to, from);
- if(err) return(-errno);
- return(0);
+ return(host_statfs(path, bsize_out, blocks_out, bfree_out, bavail_out,
+ files_out, ffree_out, fsid_out, fsid_size,
+ namelen_out, spare_out));
}
-int do_readlink(char *file, char *buf, int size)
+static struct externfs_file_ops hostfs_file_ops = {
+ .stat_file = stat_file,
+ .file_type = file_type,
+ .access_file = access_file,
+ .open_file = open_file,
+ .open_dir = open_dir,
+ .read_dir = generic_host_read_dir,
+ .read_file = read_file,
+ .write_file = generic_host_write_file,
+ .map_file_page = NULL,
+ .close_file = generic_host_close_file,
+ .close_dir = generic_host_close_dir,
+ .file_create = file_create,
+ .set_attr = set_attr,
+ .make_symlink = make_symlink,
+ .unlink_file = unlink_file,
+ .mkdir = mk_dir,
+ .rmdir = rm_dir,
+ .mknod = mk_nod,
+ .link_file = link_file,
+ .readlink = read_link,
+ .rename_file = rename_file,
+ .statfs = stat_fs,
+ .truncate_file = generic_host_truncate_file
+};
+
+static struct externfs_file_ops *mount_fs(char *mount_arg,
+ void **mount_data_out)
{
- int n;
-
- n = readlink(file, buf, size);
- if(n < 0)
- return(-errno);
- if(n < size)
- buf[n] = '\0';
- return(n);
+ if(host_root_filename(mount_arg, mount_data_out))
+ return(NULL);
+
+ return(&hostfs_file_ops);
}
-int rename_file(char *from, char *to)
-{
- int err;
+static struct externfs_mount_ops hostfs_mount_ops = {
+ .mount = mount_fs,
+};
- err = rename(from, to);
- if(err < 0) return(-errno);
- return(0);
+static int __init init_hostfs(void)
+{
+ return(register_externfs("hostfs", &hostfs_mount_ops));
}
-int do_statfs(char *root, 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)
+static void __exit exit_hostfs(void)
{
- struct statfs64 buf;
- int err;
-
- err = statfs64(root, &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);
+ unregister_externfs("hostfs");
}
+__initcall(init_hostfs);
+__exitcall(exit_hostfs);
+
+#if 0
+module_init(init_hostfs)
+module_exit(exit_hostfs)
+MODULE_LICENSE("GPL");
+#endif
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
diff --git a/arch/um/fs/hostfs/humfs.c b/arch/um/fs/hostfs/humfs.c
new file mode 100644
index 0000000..5eabfd2
--- /dev/null
+++ b/arch/um/fs/hostfs/humfs.c
@@ -0,0 +1,996 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/tqueue.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/kdev_t.h>
+#include <asm/irq.h>
+#include "hostfs.h"
+#include "user.h"
+#include "mem.h"
+#include "os.h"
+#include "mode.h"
+#include "aio.h"
+#include "irq_user.h"
+#include "irq_kern.h"
+
+#define HUMFS_VERSION 1
+
+struct humfs {
+ __u64 used;
+ __u64 total;
+ char *data;
+ char *metadata;
+ int mmap;
+};
+
+static int open_meta_file(const char *path[])
+{
+ char tmp[HOSTFS_BUFSIZE];
+ char *file = get_path(path, tmp, sizeof(tmp));
+ int fd = -ENOMEM;
+
+ if(file == NULL)
+ goto out;
+
+ /* of_rdwr makes the open fail if it's a directory, otherwise it
+ * would fail at the first read, which is less convenient.
+ */
+ fd = os_open_file(file, of_rdwr(OPENFLAGS()), 0);
+ if(fd == -EISDIR){
+ const char *dir_path[] = { file, "metadata", NULL };
+ char dir_tmp[HOSTFS_BUFSIZE], *dir_file;
+
+ dir_file = get_path(dir_path, dir_tmp, sizeof(dir_tmp));
+ fd = os_open_file(dir_file, of_rdwr(OPENFLAGS()), 0);
+ free_path(dir_file, dir_tmp);
+ }
+ else if(fd < 0)
+ goto out;
+
+ free_path(file, tmp);
+ out:
+ return(fd);
+}
+
+static int ownerships(const char *path[], int *uid_out, int *gid_out,
+ char *type_out, int *maj_out, int *min_out)
+{
+ char buf[sizeof("nnnnnnnnnn mmmmmmmmmm x nnn mmm\n")];
+ int err = -ENOMEM, fd, n, maj, min;
+ char type;
+
+ fd = open_meta_file(path);
+ if(fd < 0){
+ err = fd;
+ goto out;
+ }
+
+ err = os_read_file(fd, buf, sizeof(buf) - 1);
+ if(err < 0)
+ goto out_close;
+
+ buf[err] = '\0';
+ err = 0;
+
+ n = sscanf(buf, "%d %d %c %d %d", uid_out, gid_out, &type, &maj, &min);
+ if(n == 2){
+ maj = -1;
+ min = -1;
+ type = 0;
+ err = 0;
+ }
+ else if(n != 5)
+ err = -EINVAL;
+
+ if(type_out != NULL)
+ *type_out = type;
+ if(maj_out != NULL)
+ *maj_out = maj;
+ if(min_out != NULL)
+ *min_out = min;
+
+ out_close:
+ os_close_file(fd);
+ out:
+ return(err);
+}
+
+static int stat_file(const char *path, void *m, int *dev_out,
+ unsigned long long *inode_out, int *mode_out,
+ int *nlink_out, int *uid_out, int *gid_out,
+ unsigned long long *size_out, unsigned long *atime_out,
+ unsigned long *mtime_out, unsigned long *ctime_out,
+ int *blksize_out, unsigned long long *blocks_out)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+ const char *metadata_path[3] = { mount->metadata, path, NULL };
+ int err, mode;
+ char type;
+
+ err = host_stat_file(data_path, dev_out, inode_out, mode_out,
+ nlink_out, uid_out, gid_out, size_out,
+ atime_out, mtime_out, ctime_out, blksize_out,
+ blocks_out);
+ if(err)
+ return(err);
+
+ err = ownerships(metadata_path, uid_out, gid_out, &type, NULL, NULL);
+ if(err)
+ return(err);
+
+ mode = 0;
+ switch(type){
+ case 'c':
+ *mode_out = S_IFCHR;
+ break;
+ case 'b':
+ mode = S_IFBLK;
+ break;
+ case 's':
+ mode = S_IFSOCK;
+ break;
+ default:
+ break;
+ }
+
+ if(mode != 0)
+ *mode_out = (*mode_out & ~S_IFMT) | mode;
+
+ return(0);
+}
+
+static int meta_type(const char *path[], int *dev_out)
+{
+ int err, uid, gid, type, maj, min;
+ char c;
+
+ err = ownerships(path, &uid, &gid, &c, &maj, &min);
+ if(err)
+ return(err);
+
+ if(c == 0)
+ return(0);
+
+ *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 file_type(const char *path, int *dev_out, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+ const char *metadata_path[3] = { mount->metadata, path, NULL };
+ int type;
+
+ type = meta_type(metadata_path, dev_out);
+ if(type != 0)
+ return(type);
+
+ return(host_file_type(data_path, dev_out));
+}
+
+static int open_file(char *path, int uid, int gid, int r, int w, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+
+ return(host_open_file(data_path, r, 1));
+}
+
+static void *open_dir(char *path, int uid, int gid, int *err_out, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+
+ return(host_open_dir(data_path, err_out));
+}
+
+LIST_HEAD(humfs_replies);
+
+struct humfs_aio {
+ struct aio_context aio;
+ struct list_head list;
+ void (*completion)(char *, int, void *);
+ char *buf;
+ int err;
+ void *data;
+};
+
+static int humfs_reply_fd = -1;
+
+void humfs_task_proc(void *unused)
+{
+ struct humfs_aio *aio;
+ unsigned long flags;
+ int done;
+
+ do {
+ save_flags(flags);
+ aio = list_entry(humfs_replies.next, struct humfs_aio, list);
+ list_del(&aio->list);
+ done = list_empty(&humfs_replies);
+ restore_flags(flags);
+
+ (*aio->completion)(aio->buf, aio->err, aio->data);
+ kfree(aio);
+ } while(!done);
+}
+
+struct tq_struct humfs_task = {
+ .routine = humfs_task_proc,
+ .data = NULL
+};
+
+static void humfs_interrupt(int irq, void *dev_id, struct pt_regs *unused)
+{
+ struct aio_thread_reply reply;
+ struct humfs_aio *aio;
+ int err, fd = (int) dev_id;
+
+ while(1){
+ err = os_read_file(fd, &reply, sizeof(reply));
+ if(err < 0){
+ if(err == -EAGAIN)
+ break;
+ printk("humfs_interrupt - read returned err %d\n",
+ -err);
+ return;
+ }
+ aio = reply.data;
+ aio->err = reply.err;
+ list_add(&aio->list, &humfs_replies);
+ }
+
+ if(!list_empty(&humfs_replies))
+ schedule_task(&humfs_task);
+ reactivate_fd(fd, HUMFS_IRQ);
+}
+
+static int init_humfs_aio(void)
+{
+ int fds[2], err;
+
+ err = os_pipe(fds, 1, 1);
+ if(err){
+ printk("init_humfs_aio - pipe failed, err = %d\n", -err);
+ goto out;
+ }
+
+ err = um_request_irq(HUMFS_IRQ, fds[0], IRQ_READ, humfs_interrupt,
+ SA_INTERRUPT | SA_SAMPLE_RANDOM, "humfs",
+ (void *) fds[0]);
+ if(err){
+ printk("init_humfs_aio - : um_request_irq failed, err = %d\n",
+ err);
+ goto out_close;
+ }
+
+ humfs_reply_fd = fds[1];
+ goto out;
+
+ out_close:
+ os_close_file(fds[0]);
+ os_close_file(fds[1]);
+ out:
+ return(0);
+}
+
+__initcall(init_humfs_aio);
+
+static int read_file(int fd, unsigned long long offset, char *buf, int len,
+ void (*completion)(char *, int, void *), void *arg,
+ void *mount)
+{
+ struct humfs_aio *aio;
+ int err = -ENOMEM;
+
+ aio = um_kmalloc(sizeof(aio));
+ if(aio == NULL)
+ goto out;
+ *aio = ((struct humfs_aio) { .completion = completion,
+ .buf = buf,
+ .data = arg });
+
+ err = submit_aio(AIO_READ, fd, buf, len, offset, humfs_reply_fd, aio);
+ if(err)
+ (*completion)(buf, err, arg);
+
+ out:
+ return(err);
+}
+
+static int map_file_page(int fd, unsigned long long offset, char *buf, int w,
+ void *m)
+{
+ unsigned long long size, need;
+ int err;
+
+ err = os_fd_size(fd, &size);
+ if(err)
+ return(err);
+
+ need = offset + PAGE_SIZE;
+ if(size < need){
+ err = os_ftruncate(fd, need);
+ if(err)
+ return(err);
+ }
+
+ return(physmem_subst_mapping(buf, fd, offset, w));
+}
+
+static void close_file(void *stream, unsigned long long size, void *m)
+{
+ int fd = *((int *) stream);
+
+ physmem_forget_descriptor(fd);
+ os_ftruncate(fd, size);
+ os_close_file(fd);
+}
+
+static int create_shadow_file(const char *path[], int uid, int gid)
+{
+ char tmp[HOSTFS_BUFSIZE];
+ char *file = get_path(path, tmp, sizeof(tmp));
+ char buf[sizeof("nnnnnnnnnn mmmmmmmmmm")];
+ int fd, err = -ENOMEM;
+
+ if(file == NULL)
+ goto out;
+
+ fd = os_open_file(file, of_write(of_create(OPENFLAGS())), 0644);
+ if(fd < 0){
+ err = fd;
+ goto out;
+ }
+
+ sprintf(buf, "%d %d\n", uid, gid);
+ err = os_write_file(fd, buf, strlen(buf));
+ if(err > 0)
+ err = 0;
+
+ os_close_file(fd);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+static int remove_shadow_file(const char *path[])
+{
+ char tmp[HOSTFS_BUFSIZE];
+ char *file = get_path(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 create_shadow_directory(const char *path[], int uid, int gid)
+{
+ char dir_tmp[HOSTFS_BUFSIZE], file_tmp[HOSTFS_BUFSIZE];
+ const char *metadata_path[] = { NULL, "metadata", NULL };
+ char *file, dir_meta[sizeof("mmmmmmmmm nnnnnnnnnn\n")];
+ int err, fd;
+
+ err = host_mkdir(path, 0755);
+ if(err)
+ return(err);
+
+ metadata_path[0] = get_path(path, dir_tmp, sizeof(dir_tmp));
+ if(metadata_path[0] == NULL)
+ return(-ENOMEM);
+
+ err = -ENOMEM;
+ file = get_path(metadata_path, file_tmp, sizeof(file_tmp));
+ if(file == NULL)
+ goto out;
+
+ fd = os_open_file(file, of_create(of_rdwr(OPENFLAGS())), 0644);
+ if(fd < 0){
+ err = fd;
+ goto out_free;
+ }
+
+ sprintf(dir_meta, "%d %d\n", 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, file_tmp);
+ out:
+ free_path(metadata_path[0], dir_tmp);
+ return(err);
+}
+
+static int remove_shadow_directory(const char *path[])
+{
+ char dir_tmp[HOSTFS_BUFSIZE], file_tmp[HOSTFS_BUFSIZE], *file;
+ const char *metadata_path[] = { NULL, "metadata", NULL };
+ int err;
+
+ metadata_path[0] = get_path(path, dir_tmp, sizeof(dir_tmp));
+ if(metadata_path[0] == NULL)
+ return(-ENOMEM);
+
+ err = -ENOMEM;
+ file = get_path(metadata_path, file_tmp, sizeof(file_tmp));
+ if(file == NULL)
+ goto out;
+
+ err = os_remove_file(file);
+ if(err)
+ goto out_free;
+
+ err = os_remove_dir(metadata_path[0]);
+
+ out_free:
+ free_path(file, file_tmp);
+ out:
+ free_path(metadata_path[0], dir_tmp);
+ return(err);
+}
+
+static int file_create(char *path, int uid, int gid, int ur, int uw, int ux,
+ int gr, int gw, int gx, int or, int ow, int ox, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+ const char *metadata_path[3] = { mount->metadata, path, NULL };
+ int err, fd;
+
+ err = create_shadow_file(metadata_path, uid, gid);
+ if(err)
+ return(err);
+
+ fd = host_file_create(data_path, ur, uw, ux, gr, gw, gx, or, ow, ox);
+ if(fd < 0)
+ remove_shadow_file(metadata_path);
+
+ return(fd);
+}
+
+static int change_shadow_ownerships(const char *path[], int uid, int gid)
+{
+ char type;
+ char buf[sizeof("nnnnnnnnnn mmmmmmmmmm x nnn mmm\n")];
+ int err = -ENOMEM, old_uid, old_gid, fd, n, maj, min;
+
+ fd = open_meta_file(path);
+ if(fd < 0){
+ err = fd;
+ goto out;
+ }
+
+ err = os_read_file(fd, buf, sizeof(buf) - 1);
+ if(err < 0)
+ goto out_close;
+
+ buf[err] = '\0';
+
+ n = sscanf(buf, "%d %d %c %d %d\n", &old_uid, &old_gid, &type,
+ &maj, &min);
+ if((n != 2) && (n != 5)){
+ err = -EINVAL;
+ goto out_close;
+ }
+
+ if(uid == -1)
+ uid = old_uid;
+ if(gid == -1)
+ gid = old_gid;
+
+ if(n == 2)
+ sprintf(buf, "%d %d\n", uid, gid);
+ else
+ sprintf(buf, "%d %d %c %d %d\n", uid, gid, type, maj, min);
+
+ err = os_seek_file(fd, 0);
+ if(err < 0)
+ goto out_close;
+
+ err = os_write_file(fd, buf, strlen(buf));
+ if(err > 0)
+ err = 0;
+
+ err = os_truncate_fd(fd, strlen(buf));
+
+ out_close:
+ os_close_file(fd);
+ out:
+ return(err);
+}
+
+static int set_attr(const char *path, struct hostfs_iattr *attrs, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+ const char *metadata_path[3] = { mount->metadata, path, NULL };
+ int err;
+
+ if(attrs->ia_valid & HOSTFS_ATTR_UID){
+ err = change_shadow_ownerships(metadata_path,
+ attrs->ia_uid, -1);
+ if(err)
+ return(err);
+ }
+ if(attrs->ia_valid & HOSTFS_ATTR_GID){
+ err = change_shadow_ownerships(metadata_path,
+ -1, attrs->ia_gid);
+ if(err)
+ return(err);
+ }
+
+ attrs->ia_valid &= ~(HOSTFS_ATTR_UID | HOSTFS_ATTR_GID);
+
+ return(host_set_attr(data_path, attrs));
+}
+
+static int make_symlink(const char *from, const char *to, int uid, int gid,
+ void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, from, NULL };
+ const char *metadata_path[3] = { mount->metadata, from, NULL };
+ int err;
+
+ err = create_shadow_file(metadata_path, uid, gid);
+ if(err)
+ return(err);
+
+ err = host_make_symlink(data_path, to);
+ if(err)
+ remove_shadow_file(metadata_path);
+
+ return(err);
+}
+
+static int unlink_file(const char *path, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+ const char *metadata_path[3] = { mount->metadata, path, NULL };
+
+ remove_shadow_file(metadata_path);
+
+ return(host_unlink_file(data_path));
+}
+
+static int make_dir(const char *path, int mode, int uid, int gid, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+ const char *metadata_path[3] = { mount->metadata, path, NULL };
+ int err;
+
+ err = create_shadow_directory(metadata_path, uid, gid);
+ if(err)
+ return(err);
+
+ err = host_mkdir(data_path, mode);
+ if(err)
+ remove_shadow_directory(metadata_path);
+
+ return(err);
+}
+
+static int rm_dir(const char *path, int uid, int gid, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+ const char *metadata_path[3] = { mount->metadata, path, NULL };
+
+ remove_shadow_directory(metadata_path);
+
+ return(host_rmdir(data_path));
+}
+
+static int mk_nod(const char *path, int uid, int gid, int ur, int uw, int ux,
+ int gr, int gw, int gx, int or, int ow, int ox, int type,
+ int dev, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, path, NULL };
+ const char *metadata_path[3] = { mount->metadata, path, NULL };
+ int err, fd, maj = 0, min = 0;
+ char t, buf[sizeof("nnnnnnnnnn mmmmmmmmmm x nnn mmm\n")], *file;
+ char tmp[HOSTFS_BUFSIZE];
+
+ err = host_file_create(data_path, ur, uw, ux, gr, gw, gx, or, ow, ox);
+ if(err)
+ goto out;
+
+ switch(type){
+ case S_IFCHR:
+ t = 'c';
+ maj = MAJOR(dev);
+ min = MINOR(dev);
+ break;
+ case S_IFBLK:
+ t = 'b';
+ maj = MAJOR(dev);
+ min = MINOR(dev);
+ break;
+ case S_IFIFO:
+ t = 'p';
+ break;
+ case S_IFSOCK:
+ t = 's';
+ break;
+ default:
+ err = -EINVAL;
+ printk("do_mknod - bad node type : %d\n", type);
+ goto out_rm_host;
+ }
+
+ sprintf(buf, "%d %d %c %d %d\n", uid, gid, t, maj, min);
+
+ err = -ENOMEM;
+ file = get_path(metadata_path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out_rm_host;
+
+ fd = os_open_file(file, of_create(of_rdwr(OPENFLAGS())), 0644);
+ if(fd < 0){
+ err = fd;
+ goto out_rm_host;
+ }
+ err = os_write_file(fd, buf, strlen(buf));
+ if(err > 0)
+ err = 0;
+
+ os_close_file(fd);
+
+ if(!err)
+ return(0);
+
+ remove_shadow_file(metadata_path);
+ free_path(file, tmp);
+ out_rm_host:
+ host_unlink_file(data_path);
+ out:
+ return(err);
+}
+
+static int link_file(const char *to, const char *from, int uid, int gid,
+ void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path_from[3] = { mount->data, from, NULL };
+ const char *data_path_to[3] = { mount->data, to, NULL };
+ const char *metadata_path_from[3] = { mount->metadata, from, NULL };
+ int err;
+
+ err = create_shadow_file(metadata_path_from, uid, gid);
+ if(err)
+ return(err);
+
+ err = host_link_file(data_path_to, data_path_from);
+ if(err)
+ remove_shadow_file(metadata_path_from);
+
+ return(err);
+}
+
+static int read_link(char *file, int uid, int gid, char *buf, int size, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, file, NULL };
+
+ return(host_readlink(data_path, buf, size));
+}
+
+static int rename_file(char *from, char *to, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path_from[3] = { mount->data, from, NULL };
+ const char *data_path_to[3] = { mount->data, to, NULL };
+ const char *metadata_path_from[3] = { mount->metadata, from, NULL };
+ const char *metadata_path_to[3] = { mount->metadata, to, NULL };
+ int err;
+
+ err = host_rename_file(metadata_path_from, metadata_path_to);
+ if(err)
+ return(err);
+
+ err = host_rename_file(data_path_from, data_path_to);
+ if(err)
+ host_rename_file(metadata_path_to, metadata_path_from);
+
+ return(err);
+}
+
+static int 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, void *m)
+{
+ struct humfs *mount = m;
+ const char *data_path[3] = { mount->data, NULL };
+ int err;
+
+ /* XXX Needs to maintain this info as metadata */
+ err = host_statfs(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 char *path(char *dir, char *file)
+{
+ int need_slash, len = strlen(dir) + strlen(file);
+ char *new;
+
+ need_slash = (dir[strlen(dir)] != '/');
+ if(need_slash)
+ len++;
+
+ new = um_kmalloc(len + 1);
+ if(new == NULL)
+ return(NULL);
+
+ strcpy(new, dir);
+ if(need_slash)
+ strcat(new, "/");
+ strcat(new, file);
+
+ return(new);
+}
+
+static int read_superblock(char *root, __u64 *used_out, __u64 *total_out)
+{
+ const char *path[] = { root, "superblock", NULL };
+ char line[HOSTFS_BUFSIZE], *newline;
+ int version, i, n, fd = host_open_file(path, 1, 0);
+
+ if(fd < 0)
+ return(fd);
+
+ *used_out = 0;
+ *total_out = 0;
+ i = 0;
+ while(1){
+ n = os_read_file(fd, &line[i], sizeof(line) - i - 1);
+ if((n == 0) && (i == 0))
+ break;
+ if(n < 0)
+ return(n);
+
+ if(n > 0)
+ line[n + i] = '\0';
+
+ newline = strchr(line, '\n');
+ if(newline == NULL){
+ printk("read_superblock - line too long : '%s'\n",
+ line);
+ return(-EINVAL);
+ }
+ 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);
+ return(-EINVAL);
+ }
+ }
+ else if(sscanf(line, "used %Lu\n", used_out) == 1) ;
+ else if(sscanf(line, "total %Lu\n", total_out) == 1) ;
+ else {
+ printk("read_superblock - bogus line : '%s'\n", line);
+ return(-EINVAL);
+ }
+
+ i = newline - line;
+ memmove(line, newline, sizeof(line) - i);
+ i = strlen(line);
+ }
+
+ if(*used_out == 0){
+ printk("read_superblock - used not specified or set to zero\n");
+ return(-EINVAL);
+ }
+ if(*total_out == 0){
+ printk("read_superblock - total not specified or set to "
+ "zero\n");
+ return(-EINVAL);
+ }
+ if(*used_out > *total_out){
+ printk("read_superblock - used is greater than total\n");
+ return(-EINVAL);
+ }
+
+ return(0);
+}
+
+struct externfs_file_ops humfs_no_mmap_file_ops = {
+ .stat_file = stat_file,
+ .file_type = file_type,
+ .access_file = NULL,
+ .open_file = open_file,
+ .open_dir = open_dir,
+ .read_dir = generic_host_read_dir,
+ .read_file = read_file,
+ .write_file = generic_host_write_file,
+ .map_file_page = NULL,
+ .close_file = close_file,
+ .close_dir = generic_host_close_dir,
+ .file_create = file_create,
+ .set_attr = set_attr,
+ .make_symlink = make_symlink,
+ .unlink_file = unlink_file,
+ .mkdir = make_dir,
+ .rmdir = rm_dir,
+ .mknod = mk_nod,
+ .link_file = link_file,
+ .readlink = read_link,
+ .rename_file = rename_file,
+ .statfs = stat_fs,
+ .truncate_file = generic_host_truncate_file
+};
+
+struct externfs_file_ops humfs_mmap_file_ops = {
+ .stat_file = stat_file,
+ .file_type = file_type,
+ .access_file = NULL,
+ .open_file = open_file,
+ .open_dir = open_dir,
+ .read_dir = generic_host_read_dir,
+ .read_file = read_file,
+ .write_file = generic_host_write_file,
+ .map_file_page = map_file_page,
+ .close_file = close_file,
+ .close_dir = generic_host_close_dir,
+ .file_create = file_create,
+ .set_attr = set_attr,
+ .make_symlink = make_symlink,
+ .unlink_file = unlink_file,
+ .mkdir = make_dir,
+ .rmdir = rm_dir,
+ .mknod = mk_nod,
+ .link_file = link_file,
+ .readlink = read_link,
+ .rename_file = rename_file,
+ .statfs = stat_fs,
+ .truncate_file = generic_host_truncate_file
+};
+
+static struct externfs_file_ops *mount_fs(char *mount_arg,
+ void **mount_data_out)
+{
+ char *root, *data, *metadata, *flags;
+ struct humfs *mount;
+ __u64 used, total;
+ int err, do_mmap = 0;
+
+ if(mount_arg == NULL){
+ printk("humfs - no host directory specified\n");
+ return(NULL);
+ }
+
+ mount = um_kmalloc(sizeof(mount));
+ if(mount == NULL)
+ goto err;
+
+ 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 = host_root_filename(mount_arg, (void **) &root);
+ if(err)
+ goto err_free_mount;
+
+ err = read_superblock(root, &used, &total);
+ if(err)
+ goto err_free_mount;
+
+ data = path(root, "data");
+ if(data == NULL)
+ goto err_free_root;
+
+ metadata = path(root, "metadata");
+ if(metadata == NULL)
+ goto err_free_data;
+
+ *mount = ((struct humfs) { .total = total,
+ .used = used,
+ .data = data,
+ .metadata = metadata,
+ .mmap = do_mmap });
+
+ if(CHOOSE_MODE(do_mmap, 0)){
+ printk("humfs doesn't support mmap in tt mode\n");
+ do_mmap = 0;
+ }
+
+ *mount_data_out = mount;
+ return(do_mmap ? &humfs_mmap_file_ops : &humfs_no_mmap_file_ops);
+
+ err_free_data:
+ kfree(data);
+ err_free_root:
+ kfree(root);
+ err_free_mount:
+ kfree(mount);
+ err:
+ return(NULL);
+}
+
+struct externfs_mount_ops humfs_mount_ops = {
+ .mount = mount_fs,
+};
+
+static int __init init_humfs(void)
+{
+ return(register_externfs("humfs", &humfs_mount_ops));
+}
+
+static void __exit exit_humfs(void)
+{
+ unregister_externfs("humfs");
+}
+
+__initcall(init_humfs);
+__exitcall(exit_humfs);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/include/asm-um/irq.h b/include/asm-um/irq.h
index de389a4..6b9001d 100644
--- a/include/asm-um/irq.h
+++ b/include/asm-um/irq.h
@@ -15,8 +15,9 @@
#define SIGIO_WRITE_IRQ 11
#define TELNETD_IRQ 12
#define XTERM_IRQ 13
+#define HUMFS_IRQ 14
-#define LAST_IRQ XTERM_IRQ
+#define LAST_IRQ HUMFS_IRQ
#define NR_IRQS (LAST_IRQ + 1)
#endif
diff --git a/include/linux/hostfs_fs_i.h b/include/linux/hostfs_fs_i.h
index c930cf2..24efffa 100644
--- a/include/linux/hostfs_fs_i.h
+++ b/include/linux/hostfs_fs_i.h
@@ -1,8 +1,10 @@
#ifndef _HOSTFS_FS_I
#define _HOSTFS_FS_I
+struct externfs_file_ops;
+
struct hostfs_inode_info {
- char *host_filename;
+ struct externfs_file_ops *ops;
int fd;
int mode;
};