# This is humfs, a host-based filesystem which maintains its own metadata # separate from the host files. This enables it to be independent of the # host's permissions, and, for example, create root-owned files. This plugs # into the externfs interface. # Included is one metadata format, meta_fs, which stores metadata in a pair # of file hierarchies which parallel the data hierarchy. # *** Like hostfs, this sort of works, but is easy to crash right now *** Index: um/arch/um/Kconfig =================================================================== --- um.orig/arch/um/Kconfig 2004-08-12 17:02:13.000000000 -0400 +++ um/arch/um/Kconfig 2004-08-12 17:03:10.000000000 -0400 @@ -102,6 +102,10 @@ If you'd like to be able to work with files stored on the host, say Y or M here; otherwise say N. +config HUMFS + tristate 'Usable host filesystem' + depends on EXTERNFS + config HPPFS tristate "HoneyPot ProcFS" help Index: um/arch/um/include/os.h =================================================================== --- um.orig/arch/um/include/os.h 2004-08-12 17:02:13.000000000 -0400 +++ um/arch/um/include/os.h 2004-08-12 17:03:10.000000000 -0400 @@ -55,10 +55,12 @@ unsigned int a : 1; /* O_APPEND */ unsigned int e : 1; /* O_EXCL */ unsigned int cl : 1; /* FD_CLOEXEC */ + unsigned int d : 1; /* O_DIRECT */ }; #define OPENFLAGS() ((struct openflags) { .r = 0, .w = 0, .s = 0, .c = 0, \ - .t = 0, .a = 0, .e = 0, .cl = 0 }) + .t = 0, .a = 0, .e = 0, .cl = 0, \ + .d = 0 }) static inline struct openflags of_read(struct openflags flags) { @@ -120,6 +122,12 @@ return(flags); } +static inline struct openflags of_direct(struct openflags flags) +{ + flags.d = 1; + return(flags); +} + extern int os_stat_file(const char *file_name, struct uml_stat *buf); extern int os_lstat_file(const char *file_name, struct uml_stat *ubuf); extern int os_stat_fd(const int fd, struct uml_stat *buf); @@ -155,6 +163,7 @@ extern int os_read_file(int fd, void *buf, int len); extern int os_write_file(int fd, const void *buf, int count); extern int os_file_size(char *file, long long *size_out); +extern int os_fd_size(int fd, long long *size_out); extern int os_file_modtime(char *file, unsigned long *modtime); extern int os_pipe(int *fd, int stream, int close_on_exec); extern int os_set_fd_async(int fd, int owner); Index: um/arch/um/os-Linux/file.c =================================================================== --- um.orig/arch/um/os-Linux/file.c 2004-08-12 17:02:13.000000000 -0400 +++ um/arch/um/os-Linux/file.c 2004-08-12 17:03:10.000000000 -0400 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "os.h" #include "user.h" @@ -325,6 +326,7 @@ if(flags.c) f |= O_CREAT; if(flags.t) f |= O_TRUNC; if(flags.e) f |= O_EXCL; + if(flags.d) f |= O_DIRECT; fd = open64(file, f, mode); if(fd < 0) @@ -546,6 +548,19 @@ return(0); } +int os_fd_size(int fd, long long *size_out) +{ + struct stat buf; + int err; + + err = fstat(fd, &buf); + if(err) + return(-errno); + + *size_out = buf.st_size; + return(0); +} + int os_file_modtime(char *file, unsigned long *modtime) { struct uml_stat buf; Index: um/fs/hostfs/Makefile =================================================================== --- um.orig/fs/hostfs/Makefile 2004-08-12 17:02:13.000000000 -0400 +++ um/fs/hostfs/Makefile 2004-08-12 17:03:10.000000000 -0400 @@ -6,6 +6,7 @@ obj-y = obj-$(CONFIG_EXTERNFS) += externfs.o obj-$(CONFIG_HOSTFS) += host_fs.o host_file.o +obj-$(CONFIG_HUMFS) += humfs.o meta_fs.o SINGLE_OBJS = $(foreach f,$(patsubst %.o,%,$(obj-y) $(obj-m)),$($(f)-objs)) Index: um/fs/hostfs/humfs.c =================================================================== --- um.orig/fs/hostfs/humfs.c 2003-09-15 09:40:47.000000000 -0400 +++ um/fs/hostfs/humfs.c 2004-08-12 17:03:10.000000000 -0400 @@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/init.h" +#include "linux/workqueue.h" +#include +#include "hostfs.h" +#include "mem.h" +#include "os.h" +#include "mode.h" +#include "aio.h" +#include "irq_user.h" +#include "irq_kern.h" +#include "filehandle.h" +#include "metadata.h" + +#define HUMFS_VERSION 2 + +static int humfs_stat_file(const char *path, struct externfs_data *ed, + dev_t *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 = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err, mode, perms, major, minor; + char type; + + err = host_stat_file(data_path, NULL, inode_out, mode_out, + nlink_out, NULL, NULL, size_out, atime_out, + mtime_out, ctime_out, blksize_out, blocks_out); + if(err) + return(err); + + err = (*mount->meta->ownerships)(path, &perms, uid_out, gid_out, + &type, &major, &minor, mount); + if(err) + return(err); + + *mode_out = (*mode_out & ~S_IRWXUGO) | perms; + + mode = 0; + switch(type){ + case 'c': + mode = S_IFCHR; + *dev_out = MKDEV(major, minor); + break; + case 'b': + mode = S_IFBLK; + *dev_out = MKDEV(major, minor); + 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, void *m) +{ + struct humfs *mount = m; + int err, type, maj, min; + char c; + + err = (*mount->meta->ownerships)(path, NULL, NULL, NULL, &c, &maj, + &min, mount); + if(err) + return(err); + + if(c == 0) + return(0); + + if(dev_out) + *dev_out = MKDEV(maj, min); + + switch(c){ + case 'c': + type = OS_TYPE_CHARDEV; + break; + case 'b': + type = OS_TYPE_BLOCKDEV; + break; + case 'p': + type = OS_TYPE_FIFO; + break; + case 's': + type = OS_TYPE_SOCK; + break; + default: + type = -EINVAL; + break; + } + + return(type); +} + +static int humfs_file_type(const char *path, int *dev_out, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int type; + + type = meta_type(path, dev_out, mount); + if(type != 0) + return(type); + + return(host_file_type(data_path, dev_out)); +} + +static char *humfs_data_name(struct inode *inode) +{ + struct externfs_data *ed = inode_externfs_info(inode); + struct humfs *mount = container_of(ed, struct humfs, ext); + + return(inode_name_prefix(inode, mount->data)); +} + +static struct externfs_inode *humfs_init_file(struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf; + + hf = (*mount->meta->init_file)(); + if(hf == NULL) + return(NULL); + + hf->data.fd = -1; + return(&hf->ext); +} + +static int humfs_open_file(struct externfs_inode *ext, char *path, int uid, + int gid, struct inode *inode, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + const char *data_path[3] = { mount->data, path, NULL }; + struct openflags flags; + char tmp[HOSTFS_BUFSIZE], *file; + int err = -ENOMEM; + + file = get_path(data_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + flags = of_rdwr(OPENFLAGS()); + if(mount->direct) + flags = of_direct(flags); + + if(path == NULL) + path = ""; + err = (*mount->meta->open_file)(hf, path, inode, mount); + if(err) + goto out_free; + + err = open_filehandle(file, flags, 0, &hf->data); + if(err == -EISDIR) + goto out; + else if(err == -EPERM){ + flags = of_set_rw(flags, 1, 0); + err = open_filehandle(file, flags, 0, &hf->data); + } + + if(err) + goto out_close; + + hf->mount = mount; + is_reclaimable(&hf->data, humfs_data_name, inode); + + out_free: + free_path(file, tmp); + out: + return(err); + + out_close: + (*mount->meta->close_file)(hf); + goto out_free; +} + +static void *humfs_open_dir(char *path, int uid, int gid, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + + return(host_open_dir(data_path)); +} + +static void humfs_close_dir(void *stream, struct externfs_data *ed) +{ + os_close_dir(stream); +} + +static char *humfs_read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + + return(generic_host_read_dir(stream, pos, ino_out, len_out, mount)); +} + +LIST_HEAD(humfs_replies); + +struct humfs_aio { + struct aio_context aio; + struct list_head list; + void (*completion)(char *, int, void *); + char *buf; + int real_len; + int err; + void *data; +}; + +static int humfs_reply_fd = -1; + +struct humfs_aio last_task_aio, last_intr_aio; +struct humfs_aio *last_task_aio_ptr, *last_intr_aio_ptr; + +void humfs_work_proc(void *unused) +{ + struct humfs_aio *aio; + unsigned long flags; + + while(!list_empty(&humfs_replies)){ + local_irq_save(flags); + aio = list_entry(humfs_replies.next, struct humfs_aio, list); + + last_task_aio = *aio; + last_task_aio_ptr = aio; + + list_del(&aio->list); + local_irq_restore(flags); + + if(aio->err >= 0) + aio->err = aio->real_len; + (*aio->completion)(aio->buf, aio->err, aio->data); + kfree(aio); + } +} + +DECLARE_WORK(humfs_work, humfs_work_proc, NULL); + +static irqreturn_t 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(IRQ_HANDLED); + } + aio = reply.data; + aio->err = reply.err; + list_add(&aio->list, &humfs_replies); + last_intr_aio = *aio; + last_intr_aio_ptr = aio; + } + + if(!list_empty(&humfs_replies)) + schedule_work(&humfs_work); + reactivate_fd(fd, HUMFS_IRQ); + return(IRQ_HANDLED); +} + +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 humfs_aio(enum aio_type type, int fd, unsigned long long offset, + char *buf, int len, int real_len, + void (*completion)(char *, int, void *), void *arg) +{ + struct humfs_aio *aio; + int err = -ENOMEM; + + aio = kmalloc(sizeof(*aio), GFP_KERNEL); + if(aio == NULL) + goto out; + *aio = ((struct humfs_aio) { .aio = INIT_AIO_CONTEXT, + .list = LIST_HEAD_INIT(aio->list), + .completion= completion, + .buf = buf, + .err = 0, + .real_len = real_len, + .data = arg }); + + err = submit_aio(type, fd, buf, len, offset, humfs_reply_fd, aio); + if(err) + (*completion)(buf, err, arg); + + out: + return(err); +} + +static int humfs_read_file(struct externfs_inode *ext, + unsigned long long offset, char *buf, int len, + int ignore_start, int ignore_end, + void (*completion)(char *, int, void *), void *arg, + struct externfs_data *ed) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + int fd = filehandle_fd(&hf->data); + + if(fd < 0){ + (*completion)(buf, fd, arg); + return(fd); + } + + return(humfs_aio(AIO_READ, fd, offset, buf, len, len, completion, + arg)); +} + +static int humfs_write_file(struct externfs_inode *ext, + unsigned long long offset, + const char *buf, int start, int len, + void (*completion)(char *, int, void *), void *arg, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + int err, orig_len = len, fd = filehandle_fd(&hf->data); + + if(fd < 0){ + (*completion)((char *) buf, fd, arg); + return(fd); + } + + if(mount->direct) + len = PAGE_SIZE; + else { + offset += start; + buf += start; + } + + err = humfs_aio(AIO_WRITE, fd, offset, (char *) buf, len, orig_len, + completion, arg); + + if(err < 0) + return(err); + + if(mount->direct) + err = orig_len; + + return(err); +} + +static int humfs_map_file_page(struct externfs_inode *ext, + unsigned long long offset, char *buf, int w, + struct externfs_data *ed) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + unsigned long long size, need; + int err, fd = filehandle_fd(&hf->data); + + if(fd < 0) + return(fd); + + err = os_fd_size(fd, &size); + if(err) + return(err); + + need = offset + PAGE_SIZE; + if(size < need){ + err = os_truncate_fd(fd, need); + if(err) + return(err); + } + + return(physmem_subst_mapping(buf, fd, offset, w)); +} + +static void humfs_close_file(struct externfs_inode *ext, + unsigned long long size) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + int fd; + + if(hf->data.fd == -1) + return; + + fd = filehandle_fd(&hf->data); + physmem_forget_descriptor(fd); + truncate_file(&hf->data, size); + close_file(&hf->data); + + (*hf->mount->meta->close_file)(hf); +} + +/* XXX Assumes that you can't make a normal file */ + +static int humfs_make_node(const char *path, int mode, int uid, int gid, + int type, int major, int minor, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct file_handle fh; + const char *data_path[3] = { mount->data, path, NULL }; + int err; + char t; + + err = host_create_file(data_path, S_IRWXUGO, &fh); + if(err) + goto out; + + close_file(&fh); + + switch(type){ + case S_IFCHR: + t = 'c'; + break; + case S_IFBLK: + t = 'b'; + break; + case S_IFIFO: + t = 'p'; + break; + case S_IFSOCK: + t = 's'; + break; + default: + err = -EINVAL; + printk("make_node - bad node type : %d\n", type); + goto out_rm; + } + + err = (*mount->meta->make_node)(path, mode, uid, gid, t, major, minor, + mount); + if(err) + goto out_rm; + + out: + return(err); + + out_rm: + host_unlink_file(data_path); + goto out; +} + +static int humfs_create_file(struct externfs_inode *ext, char *path, int mode, + int uid, int gid, struct inode *inode, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err; + + err = (*mount->meta->create_file)(hf, path, mode, uid, gid, inode, + mount); + if(err) + goto out; + + err = host_create_file(data_path, S_IRWXUGO, &hf->data); + if(err) + goto out_rm; + + + is_reclaimable(&hf->data, humfs_data_name, inode); + + return(0); + + out_rm: + (*mount->meta->remove_file)(path, mount); + (*mount->meta->close_file)(hf); + out: + return(err); +} + +static int humfs_set_attr(const char *path, struct externfs_iattr *attrs, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int (*chown)(const char *, int, int, int, struct humfs *); + int err; + + chown = mount->meta->change_ownerships; + if(attrs->ia_valid & EXTERNFS_ATTR_MODE){ + err = (*chown)(path, attrs->ia_mode, -1, -1, mount); + if(err) + return(err); + } + if(attrs->ia_valid & EXTERNFS_ATTR_UID){ + err = (*chown)(path, -1, attrs->ia_uid, -1, mount); + if(err) + return(err); + } + if(attrs->ia_valid & EXTERNFS_ATTR_GID){ + err = (*chown)(path, -1, -1, attrs->ia_gid, mount); + if(err) + return(err); + } + + attrs->ia_valid &= ~(EXTERNFS_ATTR_MODE | EXTERNFS_ATTR_UID | + EXTERNFS_ATTR_GID); + + return(host_set_attr(data_path, attrs)); +} + +static int humfs_make_symlink(const char *from, const char *to, int uid, + int gid, struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf; + const char *data_path[3] = { mount->data, from, NULL }; + int err = -ENOMEM; + + hf = (*mount->meta->init_file)(); + if(hf == NULL) + goto out; + + err = (*mount->meta->create_file)(hf, from, S_IRWXUGO, uid, gid, NULL, + mount); + if(err) + goto out_close; + + err = host_make_symlink(data_path, to); + if(err) + (*mount->meta->remove_file)(from, mount); + + out_close: + (*mount->meta->close_file)(hf); + out: + return(err); +} + +static int humfs_link_file(const char *to, const char *from, int uid, int gid, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path_from[3] = { mount->data, from, NULL }; + const char *data_path_to[3] = { mount->data, to, NULL }; + int err; + + err = (*mount->meta->create_link)(to, from, mount); + if(err) + return(err); + + err = host_link_file(data_path_to, data_path_from); + if(err) + (*mount->meta->remove_file)(from, mount); + + return(err); +} + +static int humfs_unlink_file(const char *path, struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err; + + err = (*mount->meta->remove_file)(path, mount); + if (err) + return err; + + (*mount->meta->remove_file)(path, mount); + return(host_unlink_file(data_path)); +} + +static void humfs_invisible(struct externfs_inode *ext) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + struct humfs *mount = hf->mount; + + (*mount->meta->invisible)(hf); + not_reclaimable(&hf->data); +} + +static int humfs_make_dir(const char *path, int mode, int uid, int gid, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err; + + err = (*mount->meta->create_dir)(path, mode, uid, gid, mount); + if(err) + return(err); + + err = host_make_dir(data_path, S_IRWXUGO); + if(err) + (*mount->meta->remove_dir)(path, mount); + + return(err); +} + +static int humfs_remove_dir(const char *path, int uid, int gid, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err; + + err = host_remove_dir(data_path); + if (err) + return err; + + (*mount->meta->remove_dir)(path, mount); + + return(err); +} + +static int humfs_read_link(char *file, int uid, int gid, char *buf, int size, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, file, NULL }; + + return(host_read_link(data_path, buf, size)); +} + +struct humfs *inode_humfs_info(struct inode *inode) +{ + return(container_of(inode_externfs_info(inode), struct humfs, ext)); +} + +static int humfs_rename_file(char *from, char *to, struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path_from[3] = { mount->data, from, NULL }; + const char *data_path_to[3] = { mount->data, to, NULL }; + int err; + + err = (*mount->meta->rename_file)(from, to, mount); + if(err) + return(err); + + err = host_rename_file(data_path_from, data_path_to); + if(err) + (*mount->meta->rename_file)(to, from, mount); + + return(err); +} + +static int humfs_stat_fs(long *bsize_out, long long *blocks_out, + long long *bfree_out, long long *bavail_out, + long long *files_out, long long *ffree_out, + void *fsid_out, int fsid_size, long *namelen_out, + long *spare_out, struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, NULL }; + int err; + + /* XXX Needs to maintain this info as metadata */ + err = host_stat_fs(data_path, bsize_out, blocks_out, bfree_out, + bavail_out, files_out, ffree_out, fsid_out, + fsid_size, namelen_out, spare_out); + if(err) + return(err); + + *blocks_out = mount->total / *bsize_out; + *bfree_out = (mount->total - mount->used) / *bsize_out; + *bavail_out = (mount->total - mount->used) / *bsize_out; + return(0); +} + +int humfs_truncate_file(struct externfs_inode *ext, __u64 size, + struct externfs_data *ed) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + + return(truncate_file(&hf->data, size)); +} + +char *humfs_path(char *dir, char *file) +{ + int need_slash, len = strlen(dir) + strlen(file); + char *new; + + need_slash = (dir[strlen(dir) - 1] != '/'); + if(need_slash) + len++; + + new = kmalloc(len + 1, GFP_KERNEL); + if(new == NULL) + return(NULL); + + strcpy(new, dir); + if(need_slash) + strcat(new, "/"); + strcat(new, file); + + return(new); +} + +DECLARE_MUTEX(meta_sem); +struct list_head metas = LIST_HEAD_INIT(metas); + +static struct humfs_meta_ops *find_meta(const char *name) +{ + struct list_head *ele; + struct humfs_meta_ops *m; + + down(&meta_sem); + list_for_each(ele, &metas){ + m = list_entry(ele, struct humfs_meta_ops, list); + if(!strcmp(m->name, name)) + goto out; + } + m = NULL; + out: + up(&meta_sem); + return(m); +} + +void register_meta(struct humfs_meta_ops *ops) +{ + down(&meta_sem); + list_add(&ops->list, &metas); + up(&meta_sem); +} + +void unregister_meta(struct humfs_meta_ops *ops) +{ + down(&meta_sem); + list_del(&ops->list); + up(&meta_sem); +} + +static struct humfs *read_superblock(char *root) +{ + struct humfs *mount; + struct humfs_meta_ops *meta = NULL; + struct file_handle *fh; + const char *path[] = { root, "superblock", NULL }; + u64 used, total; + char meta_buf[33], line[HOSTFS_BUFSIZE], *newline; + unsigned long long pos; + int version, i, n, err; + + fh = kmalloc(sizeof(*fh), GFP_KERNEL); + if(fh == NULL) + return(ERR_PTR(-ENOMEM)); + + err = host_open_file(path, 1, 0, fh); + if(err){ + printk("Failed to open %s/%s, errno = %d\n", path[0], + path[1], err); + return(ERR_PTR(err)); + } + + used = 0; + total = 0; + pos = 0; + i = 0; + while(1){ + n = read_file(fh, pos, &line[i], sizeof(line) - i - 1); + if((n == 0) && (i == 0)) + break; + if(n < 0) + return(ERR_PTR(n)); + + pos += n; + if(n > 0) + line[n + i] = '\0'; + + newline = strchr(line, '\n'); + if(newline == NULL){ + printk("read_superblock - line too long : '%s'\n", + line); + return(ERR_PTR(-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(ERR_PTR(-EINVAL)); + } + } + else if(sscanf(line, "used %Lu\n", &used) == 1) ; + else if(sscanf(line, "total %Lu\n", &total) == 1) ; + else if(sscanf(line, "metadata %32s\n", meta_buf) == 1){ + meta = find_meta(meta_buf); + if(meta == NULL){ + printk("read_superblock - meta api \"%s\" not " + "registered\n", meta_buf); + return(ERR_PTR(-EINVAL)); + } + } + + else { + printk("read_superblock - bogus line : '%s'\n", line); + return(ERR_PTR(-EINVAL)); + } + + i = newline - line; + memmove(line, newline, sizeof(line) - i); + i = strlen(line); + } + + if(used == 0){ + printk("read_superblock - used not specified or set to " + "zero\n"); + return(ERR_PTR(-EINVAL)); + } + if(total == 0){ + printk("read_superblock - total not specified or set to " + "zero\n"); + return(ERR_PTR(-EINVAL)); + } + if(used > total){ + printk("read_superblock - used is greater than total\n"); + return(ERR_PTR(-EINVAL)); + } + + if(meta == NULL){ + meta = find_meta("shadow_fs"); + } + + if(meta == NULL){ + printk("read_superblock - valid meta api was not specified\n"); + return(ERR_PTR(-EINVAL)); + } + + mount = (*meta->init_mount)(root); + if(IS_ERR(mount)) + return(mount); + + *mount = ((struct humfs) { .total = total, + .used = used, + .meta = meta }); + return(mount); +} + +struct externfs_file_ops humfs_no_mmap_file_ops = { + .stat_file = humfs_stat_file, + .file_type = humfs_file_type, + .access_file = NULL, + .open_file = humfs_open_file, + .open_dir = humfs_open_dir, + .read_dir = humfs_read_dir, + .read_file = humfs_read_file, + .write_file = humfs_write_file, + .map_file_page = NULL, + .close_file = humfs_close_file, + .close_dir = humfs_close_dir, + .invisible = humfs_invisible, + .create_file = humfs_create_file, + .set_attr = humfs_set_attr, + .make_symlink = humfs_make_symlink, + .unlink_file = humfs_unlink_file, + .make_dir = humfs_make_dir, + .remove_dir = humfs_remove_dir, + .make_node = humfs_make_node, + .link_file = humfs_link_file, + .read_link = humfs_read_link, + .rename_file = humfs_rename_file, + .statfs = humfs_stat_fs, + .truncate_file = humfs_truncate_file +}; + +struct externfs_file_ops humfs_mmap_file_ops = { + .stat_file = humfs_stat_file, + .file_type = humfs_file_type, + .access_file = NULL, + .open_file = humfs_open_file, + .open_dir = humfs_open_dir, + .invisible = humfs_invisible, + .read_dir = humfs_read_dir, + .read_file = humfs_read_file, + .write_file = humfs_write_file, + .map_file_page = humfs_map_file_page, + .close_file = humfs_close_file, + .close_dir = humfs_close_dir, + .create_file = humfs_create_file, + .set_attr = humfs_set_attr, + .make_symlink = humfs_make_symlink, + .unlink_file = humfs_unlink_file, + .make_dir = humfs_make_dir, + .remove_dir = humfs_remove_dir, + .make_node = humfs_make_node, + .link_file = humfs_link_file, + .read_link = humfs_read_link, + .rename_file = humfs_rename_file, + .statfs = humfs_stat_fs, + .truncate_file = humfs_truncate_file +}; + +static struct externfs_data *mount_fs(char *mount_arg) +{ + char *root, *data, *flags; + struct humfs *mount; + struct externfs_file_ops *file_ops; + int err, do_mmap = 0; + + if(mount_arg == NULL){ + printk("humfs - no host directory specified\n"); + return(NULL); + } + + flags = strchr((char *) mount_arg, ','); + if(flags != NULL){ + do { + *flags++ = '\0'; + + if(!strcmp(flags, "mmap")) + do_mmap = 1; + + flags = strchr(flags, ','); + } while(flags != NULL); + } + + err = -ENOMEM; + root = host_root_filename(mount_arg); + if(root == NULL) + goto err; + + mount = read_superblock(root); + if(IS_ERR(mount)){ + err = PTR_ERR(mount); + goto err_free_root; + } + + data = humfs_path(root, "data/"); + if(data == NULL) + goto err_free_mount; + + if(CHOOSE_MODE(do_mmap, 0)){ + printk("humfs doesn't support mmap in tt mode\n"); + do_mmap = 0; + } + + mount->data = data; + mount->mmap = do_mmap; + + file_ops = do_mmap ? &humfs_mmap_file_ops : &humfs_no_mmap_file_ops; + init_externfs(&mount->ext, file_ops); + + return(&mount->ext); + + err_free_mount: + kfree(mount); + err_free_root: + kfree(root); + err: + return(NULL); +} + +struct externfs_mount_ops humfs_mount_ops = { + .init_file = humfs_init_file, + .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: + */ Index: um/fs/hostfs/meta_fs.c =================================================================== --- um.orig/fs/hostfs/meta_fs.c 2003-09-15 09:40:47.000000000 -0400 +++ um/fs/hostfs/meta_fs.c 2004-08-12 17:03:10.000000000 -0400 @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include +#include +#include "hostfs.h" +#include "metadata.h" +#include "kern_util.h" + +#define METADATA_FILE_PATH(meta) (meta)->root, "file_metadata" +#define METADATA_DIR_PATH(meta) (meta)->root, "dir_metadata" + +struct meta_fs { + struct humfs humfs; + char *root; +}; + +struct meta_file { + struct humfs_file humfs; + struct file_handle fh; +}; + +static int meta_file_path(const char *path, struct meta_fs *meta, + const char *path_out[]) +{ + const char *data_path[] = { meta->root, "data", path, NULL }; + char data_tmp[HOSTFS_BUFSIZE]; + char *data_file = get_path(data_path, data_tmp, sizeof(data_tmp)); + + if(data_file == NULL) + return(-ENOMEM); + + path_out[0] = meta->root; + path_out[2] = path; + if(os_file_type(data_file) == OS_TYPE_DIR){ + path_out[1] = "dir_metadata"; + path_out[3] = "metadata"; + path_out[4] = NULL; + } + else { + path_out[1] = "file_metadata"; + path_out[3] = NULL; + } + + return(0); +} + +static int open_meta_file(const char *path, struct humfs *humfs, + struct file_handle *fh) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + const char *meta_path[5]; + char meta_tmp[HOSTFS_BUFSIZE]; + char *meta_file; + int err; + + err = meta_file_path(path, meta, meta_path); + if(err) + goto out; + + meta_file = get_path(meta_path, meta_tmp, sizeof(meta_tmp)); + if(meta_file == NULL) + goto out; + + err = open_filehandle(meta_file, of_rdwr(OPENFLAGS()), 0, fh); + + out: + return(err); +} + +static char *meta_fs_name(struct inode *inode) +{ + struct humfs *mount = inode_humfs_info(inode); + struct meta_fs *meta = container_of(mount, struct meta_fs, humfs); + const char *metadata_path[5]; + char tmp[HOSTFS_BUFSIZE], *name, *file; + + if(meta_file_path("", meta, metadata_path)) + return(NULL); + + file = get_path(metadata_path, tmp, sizeof(tmp)); + if(file == NULL) + return(NULL); + + name = inode_name_prefix(inode, file); + + free_path(file, tmp); + return(name); +} + +static void metafs_invisible(struct humfs_file *hf) +{ + struct meta_file *mf = container_of(hf, struct meta_file, humfs); + + not_reclaimable(&mf->fh); +} + +static struct humfs_file *metafs_init_file(void) +{ + struct meta_file *mf; + int err = -ENOMEM; + + mf = kmalloc(sizeof(*mf), GFP_KERNEL); + if(mf == NULL) + return(ERR_PTR(err)); + + return(&mf->humfs); +} + +static int metafs_open_file(struct humfs_file *hf, const char *path, + struct inode *inode, struct humfs *humfs) +{ + struct meta_file *mf = container_of(hf, struct meta_file, humfs); + int err; + + err = open_meta_file(path, humfs, &mf->fh); + if(err) + return(err); + + is_reclaimable(&mf->fh, meta_fs_name, inode); + + return(0); +} + +static void metafs_close_file(struct humfs_file *hf) +{ + struct meta_file *meta = container_of(hf, struct meta_file, humfs); + + close_file(&meta->fh); + kfree(meta); +} + +static int metafs_create_file(struct humfs_file *hf, const char *path, + int mode, int uid, int gid, struct inode *inode, + struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + struct meta_file *mf = container_of(hf, struct meta_file, humfs); + char tmp[HOSTFS_BUFSIZE]; + const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL }; + char *file = get_path(metadata_path, tmp, sizeof(tmp)); + char buf[sizeof("mmmm uuuuuuuuuu gggggggggg")]; + int err = -ENOMEM; + + if(file == NULL) + goto out; + + err = open_filehandle(file, of_write(of_create(OPENFLAGS())), 0644, + &mf->fh); + if(err) + goto out_free_path; + + if(inode != NULL) + is_reclaimable(&mf->fh, meta_fs_name, inode); + + sprintf(buf, "%d %d %d\n", mode & S_IRWXUGO, uid, gid); + err = write_file(&mf->fh, 0, buf, strlen(buf)); + if(err < 0) + goto out_rm; + + free_path(file, tmp); + return(0); + + out_rm: + close_file(&mf->fh); + os_remove_file(file); + out_free_path: + free_path(file, tmp); + out: + return(err); +} + +static int metafs_create_link(const char *to, const char *from, + struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + const char *path_to[] = { METADATA_FILE_PATH(meta), to, NULL }; + const char *path_from[] = { METADATA_FILE_PATH(meta), from, NULL }; + + return(host_link_file(path_to, path_from)); +} + +static int metafs_remove_file(const char *path, struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + char tmp[HOSTFS_BUFSIZE]; + const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL }; + char *file = get_path(metadata_path, tmp, sizeof(tmp)); + int err = -ENOMEM; + + if(file == NULL) + goto out; + + err = os_remove_file(file); + + out: + free_path(file, tmp); + return(err); +} + +static int metafs_create_directory(const char *path, int mode, int uid, + int gid, struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + char tmp[HOSTFS_BUFSIZE]; + const char *dir_path[] = { METADATA_DIR_PATH(meta), path, NULL, NULL }; + const char *file_path[] = { METADATA_FILE_PATH(meta), path, NULL, + NULL }; + char *file, dir_meta[sizeof("mmmm uuuuuuuuuu gggggggggg\n")]; + int err, fd; + + err = host_make_dir(dir_path, 0755); + if(err) + goto out; + + err = host_make_dir(file_path, 0755); + if(err) + goto out_rm; + + /* This to make the index independent of the number of elements in + * METADATA_DIR_PATH(). + */ + dir_path[sizeof(dir_path) / sizeof(dir_path[0]) - 2] = "metadata"; + + err = -ENOMEM; + file = get_path(dir_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + fd = os_open_file(file, of_create(of_rdwr(OPENFLAGS())), 0644); + if(fd < 0){ + err = fd; + goto out_free; + } + + sprintf(dir_meta, "%d %d %d\n", mode & S_IRWXUGO, uid, gid); + err = os_write_file(fd, dir_meta, strlen(dir_meta)); + if(err > 0) + err = 0; + + os_close_file(fd); + + out_free: + free_path(file, tmp); + out_rm: + host_remove_dir(dir_path); + out: + return(err); +} + +static int metafs_remove_directory(const char *path, struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + char tmp[HOSTFS_BUFSIZE], *file; + const char *dir_path[] = { METADATA_DIR_PATH(meta), path, "metadata", + NULL }; + const char *file_path[] = { METADATA_FILE_PATH(meta), path, NULL }; + char *slash; + int err; + + err = -ENOMEM; + file = get_path(dir_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = os_remove_file(file); + if(err) + goto out_free; + + slash = strrchr(file, '/'); + if(slash == NULL){ + printk("remove_shadow_directory failed to find last slash\n"); + goto out_free; + } + *slash = '\0'; + err = os_remove_dir(file); + free_path(file, tmp); + + file = get_path(file_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = os_remove_dir(file); + if(err) + goto out_free; + + out: + return(err); + out_free: + free_path(file, tmp); + goto out; +} + +static int metafs_make_node(const char *path, int mode, int uid, int gid, + int type, int maj, int min, struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + struct file_handle fh; + char tmp[HOSTFS_BUFSIZE]; + const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL }; + int err; + char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")], *file; + + sprintf(buf, "%d %d %d %c %d %d\n", mode & S_IRWXUGO, uid, gid, type, + maj, min); + + err = -ENOMEM; + file = get_path(metadata_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = open_filehandle(file, + of_create(of_rdwr(OPENFLAGS())), 0644, &fh); + if(err) + goto out_free; + + err = write_file(&fh, 0, buf, strlen(buf)); + if(err > 0) + err = 0; + + close_file(&fh); + + out_free: + free_path(file, tmp); + out: + return(err); +} + +static int metafs_ownerships(const char *path, int *mode_out, int *uid_out, + int *gid_out, char *type_out, int *maj_out, + int *min_out, struct humfs *humfs) +{ + struct file_handle fh; + char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")]; + int err, n, mode, uid, gid, maj, min; + char type; + + err = open_meta_file(path, humfs, &fh); + if(err) + goto out; + + err = os_read_file(fh.fd, buf, sizeof(buf) - 1); + if(err < 0) + goto out_close; + + buf[err] = '\0'; + err = 0; + + n = sscanf(buf, "%d %d %d %c %d %d", &mode, &uid, &gid, &type, &maj, + &min); + if(n == 3){ + maj = -1; + min = -1; + type = 0; + err = 0; + } + else if(n != 6) + err = -EINVAL; + + if(mode_out != NULL) + *mode_out = mode; + if(uid_out != NULL) + *uid_out = uid; + if(gid_out != NULL) + *gid_out = uid; + if(type_out != NULL) + *type_out = type; + if(maj_out != NULL) + *maj_out = maj; + if(min_out != NULL) + *min_out = min; + + out_close: + close_file(&fh); + out: + return(err); +} + +static int metafs_change_ownerships(const char *path, int mode, int uid, + int gid, struct humfs *humfs) +{ + struct file_handle fh; + char type; + char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")]; + int err = -ENOMEM, old_mode, old_uid, old_gid, n, maj, min; + + err = open_meta_file(path, humfs, &fh); + if(err) + goto out; + + err = read_file(&fh, 0, buf, sizeof(buf) - 1); + if(err < 0) + goto out_close; + + buf[err] = '\0'; + + n = sscanf(buf, "%d %d %d %c %d %d\n", &old_mode, &old_uid, &old_gid, + &type, &maj, &min); + if((n != 3) && (n != 6)){ + err = -EINVAL; + goto out_close; + } + + if(mode == -1) + mode = old_mode; + if(uid == -1) + uid = old_uid; + if(gid == -1) + gid = old_gid; + + if(n == 3) + sprintf(buf, "%d %d %d\n", mode & S_IRWXUGO, uid, gid); + else + sprintf(buf, "%d %d %d %c %d %d\n", mode & S_IRWXUGO, uid, gid, + type, maj, min); + + err = write_file(&fh, 0, buf, strlen(buf)); + if(err > 0) + err = 0; + + err = truncate_file(&fh, strlen(buf)); + + out_close: + close_file(&fh); + out: + return(err); +} + +static int metafs_rename_file(const char *from, const char *to, + struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + const char *metadata_path_from[5], *metadata_path_to[5]; + int err; + + err = meta_file_path(from, meta, metadata_path_from); + if(err) + return(err); + + err = meta_file_path(to, meta, metadata_path_to); + if(err) + return(err); + + return(host_rename_file(metadata_path_from, metadata_path_to)); +} + +static struct humfs *metafs_init_mount(char *root) +{ + struct meta_fs *meta; + int err = -ENOMEM; + + meta = kmalloc(sizeof(*meta), GFP_KERNEL); + if(meta == NULL) + goto out; + + meta->root = uml_strdup(root); + if(meta->root == NULL) + goto out_free_meta; + + return(&meta->humfs); + + out_free_meta: + kfree(meta); + out: + return(ERR_PTR(err)); +} + +static void metafs_free_mount(struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + + kfree(meta); +} + +struct humfs_meta_ops hum_fs_meta_fs_ops = { + .list = LIST_HEAD_INIT(hum_fs_meta_fs_ops.list), + .name = "shadow_fs", + .init_file = metafs_init_file, + .open_file = metafs_open_file, + .close_file = metafs_close_file, + .ownerships = metafs_ownerships, + .make_node = metafs_make_node, + .create_file = metafs_create_file, + .create_link = metafs_create_link, + .remove_file = metafs_remove_file, + .create_dir = metafs_create_directory, + .remove_dir = metafs_remove_directory, + .change_ownerships = metafs_change_ownerships, + .rename_file = metafs_rename_file, + .invisible = metafs_invisible, + .init_mount = metafs_init_mount, + .free_mount = metafs_free_mount, +}; + +static int __init init_meta_fs(void) +{ + register_meta(&hum_fs_meta_fs_ops); + return(0); +} + +static void __exit exit_meta_fs(void) +{ + unregister_meta(&hum_fs_meta_fs_ops); +} + +__initcall(init_meta_fs); +__exitcall(exit_meta_fs); + +/* + * 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: + */ Index: um/fs/hostfs/metadata.h =================================================================== --- um.orig/fs/hostfs/metadata.h 2003-09-15 09:40:47.000000000 -0400 +++ um/fs/hostfs/metadata.h 2004-08-12 17:03:10.000000000 -0400 @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2004 Piotr Neuman (sikkh@wp.pl) and + * Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#ifndef __UM_FS_METADATA +#define __UM_FS_METADATA + +#include "linux/fs.h" +#include "linux/list.h" +#include "os.h" +#include "hostfs.h" + +struct humfs { + struct externfs_data ext; + __u64 used; + __u64 total; + char *data; + int mmap; + int direct; + struct humfs_meta_ops *meta; +}; + +struct humfs_file { + struct humfs *mount; + struct file_handle data; + struct externfs_inode ext; +}; + +struct humfs_meta_ops { + struct list_head list; + char *name; + struct humfs_file *(*init_file)(void); + int (*open_file)(struct humfs_file *hf, const char *path, + struct inode *inode, struct humfs *humfs); + int (*create_file)(struct humfs_file *hf, const char *path, int mode, + int uid, int gid, struct inode *inode, + struct humfs *humfs); + void (*close_file)(struct humfs_file *humfs); + int (*ownerships)(const char *path, int *mode_out, int *uid_out, + int *gid_out, char *type_out, int *maj_out, + int *min_out, struct humfs *humfs); + int (*make_node)(const char *path, int mode, int uid, int gid, + int type, int major, int minor, struct humfs *humfs); + int (*create_link)(const char *to, const char *from, + struct humfs *humfs); + int (*remove_file)(const char *path, struct humfs *humfs); + int (*create_dir)(const char *path, int mode, int uid, int gid, + struct humfs *humfs); + int (*remove_dir)(const char *path, struct humfs *humfs); + int (*change_ownerships)(const char *path, int mode, int uid, int gid, + struct humfs *humfs); + int (*rename_file)(const char *from, const char *to, + struct humfs *humfs); + void (*invisible)(struct humfs_file *hf); + struct humfs *(*init_mount)(char *root); + void (*free_mount)(struct humfs *humfs); +}; + +extern void register_meta(struct humfs_meta_ops *ops); +extern void unregister_meta(struct humfs_meta_ops *ops); + +extern char *humfs_path(char *dir, char *file); +extern char *humfs_name(struct inode *inode, char *prefix); +extern struct humfs *inode_humfs_info(struct inode *inode); + +#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: + */ Index: um/include/asm-um/irq.h =================================================================== --- um.orig/include/asm-um/irq.h 2004-08-12 17:02:13.000000000 -0400 +++ um/include/asm-um/irq.h 2004-08-12 17:03:10.000000000 -0400 @@ -15,8 +15,9 @@ #define SIGIO_WRITE_IRQ 11 #define TELNETD_IRQ 12 #define XTERM_IRQ 13 - -#define LAST_IRQ XTERM_IRQ +#define HUMFS_IRQ 14 + +#define LAST_IRQ HUMFS_IRQ #define NR_IRQS (LAST_IRQ + 1) #endif