diff options
author | jdike <jdike> | 2004-04-07 20:44:02 +0000 |
---|---|---|
committer | jdike <jdike> | 2004-04-07 20:44:02 +0000 |
commit | 9945ac8d1c5e4c70036663ae3b6b3ed2c5b72059 (patch) | |
tree | 8a72a3fa5e62706a797934f26dc748fdd535e1fa | |
parent | 6a7a2f74418628bcf9ba8021ccc6ac53ee16ff0e (diff) | |
download | uml-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.in | 7 | ||||
-rw-r--r-- | arch/um/fs/hostfs/Makefile | 10 | ||||
-rw-r--r-- | arch/um/fs/hostfs/host_file.c | 538 | ||||
-rw-r--r-- | arch/um/fs/hostfs/hostfs.h | 162 | ||||
-rw-r--r-- | arch/um/fs/hostfs/hostfs_kern.c | 549 | ||||
-rw-r--r-- | arch/um/fs/hostfs/hostfs_user.c | 461 | ||||
-rw-r--r-- | arch/um/fs/hostfs/humfs.c | 996 | ||||
-rw-r--r-- | include/asm-um/irq.h | 3 | ||||
-rw-r--r-- | include/linux/hostfs_fs_i.h | 4 |
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; }; |