# This is the start of the FUSE server support, which will export the UML # filesystem to the host as a FUSE filesystem. Index: linux-2.6.17/arch/um/Kconfig =================================================================== --- linux-2.6.17.orig/arch/um/Kconfig 2007-11-19 10:56:56.000000000 -0500 +++ linux-2.6.17/arch/um/Kconfig 2007-11-19 11:32:36.000000000 -0500 @@ -257,6 +257,13 @@ config KERNEL_STACK_ORDER be 1 << order pages. The default is OK unless you're running Valgrind on UML, in which case, set this to 3. +config FUSE_SERVER + bool "FUSE server" + default n + help + This enables the UML FUSE (http://fuse.sourceforge.net/) server, which + allows you to mount a UML filesystem on the host. + endmenu source "init/Kconfig" Index: linux-2.6.17/arch/um/Makefile =================================================================== --- linux-2.6.17.orig/arch/um/Makefile 2007-11-19 11:12:16.000000000 -0500 +++ linux-2.6.17/arch/um/Makefile 2007-11-19 11:32:36.000000000 -0500 @@ -136,6 +136,10 @@ ifeq ($(CONFIG_X11_FB),y) UML_LIBS += -L/usr/X11R6/lib -lX11 -lXext endif +ifeq ($(CONFIG_FUSE_SERVER),y) +UML_LIBS += -L/usr/local/lib -lfuse +endif + CFLAGS_vmlinux := $(LINK-y) $(LINK_WRAPS) define cmd_vmlinux__ $(CC) $(CFLAGS_vmlinux) -o $@ \ Index: linux-2.6.17/arch/um/os-Linux/Makefile =================================================================== --- linux-2.6.17.orig/arch/um/os-Linux/Makefile 2007-10-24 10:04:50.000000000 -0400 +++ linux-2.6.17/arch/um/os-Linux/Makefile 2007-11-19 11:32:36.000000000 -0500 @@ -10,6 +10,10 @@ obj-y = aio.o elf_aux.o execvp.o file.o obj-$(CONFIG_TTY_LOG) += tty_log.o user-objs-$(CONFIG_TTY_LOG) += tty_log.o +obj-$(CONFIG_FUSE_SERVER) += fuse.o +user-objs-$(CONFIG_FUSE_SERVER) += fuse.o +CFLAGS_fuse.o += -I/usr/local/include + USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \ main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \ trap.o tty.o tls.o uaccess.o umid.o util.o Index: linux-2.6.17/arch/um/os-Linux/fuse.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17/arch/um/os-Linux/fuse.c 2007-11-19 11:32:36.000000000 -0500 @@ -0,0 +1,641 @@ +#include +#include +#include +#include +#include +#include +#include +#include "user.h" +#include "uml-fuse.h" +#include "kern_constants.h" +#include "os.h" +#define FUSE_USE_VERSION 25 +#include + +struct fuse_data { + unsigned long long root_ino; +}; + +static void uml_fuse_init(void *userdata) +{ + struct fuse_data *data = userdata; + + data->root_ino = fuse_vfs_lookup_root(); +} + +static void uml_fuse_destroy(void *userdata) +{ + struct fuse_data *data = userdata; + + fuse_vfs_forget(data->root_ino, 1); +} + +static void fill_stat(struct stat *stat, unsigned long long inode, + unsigned long mode, unsigned long nlink, + unsigned long uid, unsigned long gid, unsigned long rdev, + unsigned long long size, unsigned long blksize, + unsigned long long blocks, unsigned long long atime, + unsigned long long mtime, unsigned long long ctime) +{ + stat->st_ino = inode; + stat->st_mode = mode; + stat->st_nlink = nlink; + stat->st_uid = uid; + stat->st_uid = uid; + stat->st_gid = gid; + stat->st_rdev = rdev; + stat->st_size = size; + stat->st_blksize = blksize; + stat->st_blocks = blocks; + stat->st_ctime = ctime; + stat->st_mtime = mtime; + stat->st_atime = atime; +} + +static void fill_fuse_entry(struct fuse_entry_param *e, + unsigned long long inode, unsigned long mode, + unsigned long nlink, unsigned long uid, + unsigned long gid, unsigned long rdev, + unsigned long long size, unsigned long blksize, + unsigned long long blocks, unsigned long long atime, + unsigned long long mtime, unsigned long long ctime) +{ + struct stat *stbuf; + + memset(e, 0, sizeof(*e)); + stbuf = &e->attr; + fill_stat(stbuf, inode, mode, nlink, uid, gid, rdev, size, blksize, + blocks, atime, mtime, ctime); + + e->ino = stbuf->st_ino; + e->attr_timeout = 1.0; + e->entry_timeout = 1.0; +} + +static void uml_fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct fuse_entry_param e; + unsigned long long inode, size, blocks, atime, ctime, mtime; + unsigned long mode, nlink, uid, gid, rdev, blksize; + int err; + + if(parent == FUSE_ROOT_ID){ + struct fuse_data *data = fuse_req_userdata(req); + + parent = data->root_ino; + } + + err = fuse_vfs_lookup(parent, name, &inode, &mode, &nlink, &uid, &gid, + &rdev, &size, &blksize, &blocks, &atime, &mtime, + &ctime); + if(err){ + fuse_reply_err(req, -err); + return; + } + + fill_fuse_entry(&e, inode, mode, nlink, uid, gid, rdev, size, blksize, + blocks, atime, mtime, ctime); + fuse_reply_entry(req, &e); +} + +static void uml_fuse_forget(fuse_req_t req, fuse_ino_t ino, + unsigned long nlookup) +{ + fuse_vfs_forget(ino, nlookup); + fuse_reply_none(req); +} + +static void uml_fuse_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct stat stbuf; + unsigned long long inode, size, blocks, atime, ctime, mtime; + unsigned long mode, nlink, uid, gid, rdev, blksize; + int err; + + if(ino == FUSE_ROOT_ID){ + struct fuse_data *data = fuse_req_userdata(req); + + ino = data->root_ino; + } + + memset(&stbuf, 0, sizeof(stbuf)); + err = fuse_vfs_stat(ino, &inode, &mode, &nlink, &uid, &gid, &rdev, + &size, &blksize, &blocks, &atime, &mtime, &ctime); + if(err) + fuse_reply_err(req, -err); + else { + fill_stat(&stbuf, inode, mode, nlink, uid, gid, rdev, size, + blksize, blocks, atime, mtime, ctime); + fuse_reply_attr(req, &stbuf, 1.0); + } +} + +void uml_fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi) +{ + unsigned long long inode, size, blocks, atime, ctime, mtime; + unsigned long mode, nlink, uid, gid, rdev, blksize; + int mask, err; + + if(ino == FUSE_ROOT_ID){ + struct fuse_data *data = fuse_req_userdata(req); + + ino = data->root_ino; + } + + log_info("uml_fuse_setattr - mask = 0x%x ino = %ld\n", to_set, ino); + + mask = 0; + if(to_set & FUSE_SET_ATTR_MODE){ + mask |= UML_FUSE_MODE; + mode = attr->st_mode; + } + if(to_set & FUSE_SET_ATTR_UID){ + mask |= UML_FUSE_UID; + uid = attr->st_uid; + } + if(to_set & FUSE_SET_ATTR_GID){ + mask |= UML_FUSE_GID; + gid = attr->st_gid; + } + if(to_set & FUSE_SET_ATTR_SIZE){ + mask |= UML_FUSE_SIZE; + size = attr->st_size; + } + if(to_set & FUSE_SET_ATTR_ATIME){ + mask |= UML_FUSE_ATIME; + atime = attr->st_atim.tv_sec * BILLION + attr->st_atim.tv_nsec; + } + if(to_set & FUSE_SET_ATTR_MTIME){ + mask |= UML_FUSE_MTIME; + mtime = attr->st_mtim.tv_sec * BILLION + attr->st_mtim.tv_nsec; + } + + err = fuse_vfs_setattr(ino, mask, &inode, &mode, &nlink, &uid, &gid, + &rdev, &size, &blksize, &blocks, &atime, &mtime, + &ctime); + log_info("uml_fuse_setattr returns %d\n", err); + if(err) + fuse_reply_err(req, -err); + else { + fill_stat(attr, inode, mode, nlink, uid, gid, rdev, size, + blksize, blocks, atime, mtime, ctime); + fuse_reply_attr(req, attr, 1.0); + } +} + +static void uml_fuse_readlink(fuse_req_t req, fuse_ino_t ino) +{ + char buf[64], *ptr = buf; + int len = sizeof(buf), err; + +again: + err = fuse_vfs_readlink(ino, ptr, len); + if(err == len){ + if(ptr != buf) + kfree(ptr); + + len *= 2; + ptr = um_kmalloc(len); + if(ptr != NULL) + goto again; + + err = -ENOMEM; + } + + if(err < 0) + fuse_reply_err(req, -err); + else fuse_reply_readlink(req, ptr); + + if((ptr != NULL) && (ptr != buf)) + kfree(ptr); +} + +void uml_fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev) +{ +} + +void uml_fuse_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode) +{ +} + +void uml_fuse_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) +{ +} + +void uml_fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) +{ +} + +void uml_fuse_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, + const char *name) +{ +} + +void uml_fuse_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_ino_t newparent, const char *newname) +{ +} + +void uml_fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname) +{ +} + +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 + +static const int flag_bits[] = { [10] = OPEN_APPEND, + [11] = OPEN_NONBLOCK, + [12] = OPEN_SYNC, + [13] = OPEN_FASYNC, + [14] = OPEN_DIRECT, + [16] = OPEN_DIRECTORY, + [17] = OPEN_NOFOLLOW, + [18] = OPEN_NOATIME }; + +static int convert_mode(int flags) +{ + int i, mode = 0; + + for(i = 0; i < sizeof(mode) * 8; i++){ + if(flags & (1 << i)) + mode |= (1 << flag_bits[i]); + } + switch(flags & O_ACCMODE){ + case O_RDONLY: + mode |= (1 << OPEN_READ); break; + case O_WRONLY: + mode |= (1 << OPEN_WRITE); break; + case O_RDWR: + mode |= (1 << OPEN_READ) | (1 << OPEN_WRITE); break; + default: + printk("uml_fuse_open - bad mode %d\n", mode); break; + } + + return mode; +} + +void uml_fuse_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + void *file; + unsigned long err; + + err = fuse_vfs_open(ino, convert_mode(fi->flags), &file); + if(err) + fuse_reply_err(req, -err); + else { + fi->fh = (unsigned long) file; + fuse_reply_open(req, fi); + } +} + +static void uml_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t flags, struct fuse_file_info *fi) +{ + struct fuse_entry_param e; + unsigned long long inode, size, blocks, atime, ctime, mtime; + unsigned long mode, nlink, uid, gid, rdev, blksize; + void *file; + int err = 0; + + err = fuse_vfs_create(parent, name, convert_mode(fi->flags), flags, + &file, &inode, &mode, &nlink, &uid, &gid, &rdev, + &size, &blksize, &blocks, &atime, &mtime, &ctime); + if(err){ + fuse_reply_err(req, -err); + return; + } + + fi->fh = (unsigned long) file; + fill_fuse_entry(&e, inode, mode, nlink, uid, gid, rdev, size, blksize, + blocks, atime, mtime, ctime); + fuse_reply_create(req, &e, fi); +} + +#define min(x, y) ((x) < (y) ? (x) : (y)) + +static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, + off_t off, size_t maxsize) +{ + if (off < bufsize) + return fuse_reply_buf(req, buf + off, + min(bufsize - off, maxsize)); + else + return fuse_reply_buf(req, NULL, 0); +} + +void uml_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi) +{ + void *buf; + int n; + + buf = um_kmalloc(size); + if(buf == NULL){ + fuse_reply_err(req, ENOMEM); + return; + } + + n = fuse_vfs_read((void *) (unsigned long) fi->fh, off, buf, size); + if(n < 0) + fuse_reply_err(req, -n); + else fuse_reply_buf(req, buf, n); + + kfree(buf); +} + +void uml_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) +{ + int n; + + n = fuse_vfs_write((void *) (unsigned long) fi->fh, off, buf, size); + if(n < 0) + fuse_reply_err(req, -n); + else fuse_reply_write(req, n); +} + +void uml_fuse_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ +} + +void uml_fuse_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ +} + +static void uml_fuse_opendir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + void *file; + int err; + + if(ino == FUSE_ROOT_ID){ + struct fuse_data *data = fuse_req_userdata(req); + + ino = data->root_ino; + } + + err = fuse_vfs_opendir(ino, &file); + if(err) + fuse_reply_err(req, -err); + else { + fi->fh = (uint64_t) (unsigned long) file; + fuse_reply_open(req, fi); + } +} + + +static inline int max(int a, int b) +{ + return a < b ? b : a; +} + +int dirbuf_add(struct dirbuf *b, char *name, int namelen, + unsigned long long ino, unsigned long mode) +{ + struct stat stbuf; + int new_size = b->size + fuse_dirent_size(namelen); + char save; + + if(new_size > b->max) + return -EINVAL; + + if(new_size > b->allocated){ + int need = max(new_size, b->allocated + UM_KERN_PAGE_SIZE); + void *new = um_kmalloc(need); + + if(new == NULL) + return -ENOMEM; + + memcpy(new, b->p, b->size); + kfree(b->p); + b->p = new; + b->allocated = need; + } + + stbuf.st_mode = mode; + stbuf.st_ino = ino; + + /* fuse_add_dirent wants a NULL-terminated string, which name might + * not be. So, we temporarily NULL-terminate it and restore the old + * character in case it was precious. + */ + save = name[namelen]; + name[namelen] = '\0'; + fuse_add_dirent(b->p + b->size, name, &stbuf, b->size); + name[namelen] = save; + + b->size = new_size; + + return 0; +} + +static void uml_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) +{ + struct dirbuf b; + int err; + + (void) fi; + + b.allocated = UM_KERN_PAGE_SIZE; + err = -ENOMEM; + b.p = um_kmalloc(b.allocated); + if(b.p == NULL){ + fuse_reply_err(req, -err); + return; + } + + b.max = size; + b.size = 0; + + fuse_vfs_readdir((void *) (unsigned long) fi->fh, &b); + + reply_buf_limited(req, b.p, b.size, off, size); + kfree(b.p); +} + +/* Used as both release and releasedir methods, since all they ultimately + * need to do is an fput. + */ +static void uml_fuse_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + fuse_vfs_release((void *) (unsigned long) fi->fh); + fuse_reply_err(req, 0); +} + +void uml_fuse_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ +} + +void uml_fuse_statfs(fuse_req_t req) +{ +} + +void uml_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) +{ +} + +void uml_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) +{ +} + +void uml_fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) +{ +} + +void uml_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) +{ +} + +void uml_fuse_access(fuse_req_t req, fuse_ino_t ino, int mask) +{ +} + +const struct fuse_lowlevel_ops uml_fuse_ops = { + .init = uml_fuse_init, + .destroy = uml_fuse_destroy, + .lookup = uml_fuse_lookup, + .forget = uml_fuse_forget, + .getattr = uml_fuse_getattr, + .setattr = uml_fuse_setattr, + .readlink = uml_fuse_readlink, + .release = uml_fuse_release, + .opendir = uml_fuse_opendir, + .readdir = uml_fuse_readdir, + .releasedir = uml_fuse_release, + .open = uml_fuse_open, + .read = uml_fuse_read, + .write = uml_fuse_write, + .release = uml_fuse_release, + .create = uml_fuse_create, +#if 0 + .init = uml_fuse_init, + .destroy = uml_fuse_destroy, + .lookup = uml_fuse_lookup, + .forget = uml_fuse_forget, + .getattr = uml_fuse_getattr, + .setattr = uml_fuse_setattr, + .readlink = uml_fuse_readlink, + .mknod = uml_fuse_mknod, + .mkdir = uml_fuse_mkdir, + .unlink = uml_fuse_unlink, + .rmdir = uml_fuse_rmdir, + .symlink = uml_fuse_symlink, + .rename = uml_fuse_rename, + .link = uml_fuse_link, + .open = uml_fuse_open, + .read = uml_fuse_read, + .write = uml_fuse_write, + .release = uml_fuse_release, + .flush = uml_fuse_flush, + .fsync = uml_fuse_fsync, + .opendir = uml_fuse_opendir, + .readdir = uml_fuse_readdir, + .releasedir = uml_fuse_releasedir, + .fsyncdir = uml_fuse_fsyncdir, + .statfs = uml_fuse_statfs, + .setxattr = uml_fuse_setxattr, + .getxattr = uml_fuse_getxattr, + .listxattr = uml_fuse_listxattr, + .removexattr = uml_fuse_removexattr, + .access = uml_fuse_access, + .create = uml_fuse_create, +#endif +}; + +/* Percpu, so no locking needed as long as worker threads are bound to CPUs */ +static void *req_bufs[UM_NR_CPUS]; + +/* This happens in process context, in a work_proc */ +int uml_fuse_request(void *se) +{ + struct fuse_session *session = se; + struct fuse_chan *ch = fuse_session_next_chan(session, NULL); + int res, fd = fuse_chan_fd(ch), c = cpu(); + void *req_buf = req_bufs[c]; + + while(1){ + if(fuse_session_exited(session)) + return fd; + + res = fuse_chan_receive(ch, req_buf, fuse_chan_bufsize(ch)); + if(res == -EAGAIN) + break; + + if(res == -1){ + fuse_session_reset(session); + return -1; + } + + if(res == 0) + break; + + fuse_session_process(session, req_buf, res, ch); + } + + return fd; +} + +void *uml_fuse_new(int fd) +{ + char *argv = NULL; + int size; + struct fuse_args args = FUSE_ARGS_INIT(0, &argv); + struct fuse_session *se; + struct fuse_chan *ch; + struct fuse_data *data; + + data = um_kmalloc(sizeof(*data)); + if(data == NULL) + goto out; + + se = fuse_lowlevel_new(&args, ¨_fuse_ops, sizeof(uml_fuse_ops), + data); + if (se == NULL) + goto out; + + ch = fuse_kern_chan_new(fd); + if (ch == NULL) + goto out_session; + + fuse_session_add_chan(se, ch); + + size = fuse_chan_bufsize(ch); + req_buf = um_vmalloc(fuse_chan_bufsize(ch)); + if(req_buf == NULL){ + printk("uml_fuse_new failed to allocate buf %d bytes\n", + fuse_chan_bufsize(ch)); + goto out_session; + } + /* The purpose of this is really to fault in req_buf, since it's + * vmalloced, and not populated until it's referenced. If this isn't + * done, when FUSE tries to read into it, it will get -EFAULT from the + * host. + */ + memset(req_buf, 0, size); + + return se; + +out_session: + fuse_session_destroy(se); +out: + close(fd); + return NULL; +} + +void uml_fuse_free(void *session) +{ + fuse_session_destroy(session); +} Index: linux-2.6.17/arch/um/drivers/mconsole_kern.c =================================================================== --- linux-2.6.17.orig/arch/um/drivers/mconsole_kern.c 2007-10-24 10:04:50.000000000 -0400 +++ linux-2.6.17/arch/um/drivers/mconsole_kern.c 2007-11-19 11:33:46.000000000 -0500 @@ -773,6 +773,100 @@ void mconsole_stack(struct mc_request *r with_console(req, stack_proc, to); } +#ifdef CONFIG_FUSE_SERVER +#include "uml-fuse.h" + +static struct list_head fuse_requests = LIST_HEAD_INIT(fuse_requests); + +struct fuse_dev { + struct list_head list; + void *session; +}; + +static void fuse_work_proc(void *unused) +{ + struct fuse_dev *dev; + unsigned long flags; + int fd; + + while(!list_empty(&fuse_requests)){ + local_save_flags(flags); + dev = list_entry(fuse_requests.next, struct fuse_dev, list); + list_del_init(&dev->list); + local_irq_restore(flags); + fd = uml_fuse_request(dev->session); + if(fd >= 0) + reactivate_fd(fd, FUSE_IRQ); + } +} + +static DECLARE_WORK(fuse_work, fuse_work_proc, NULL); + +static irqreturn_t fuse_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct fuse_dev *dev = dev_id; + + if(list_empty(&dev->list)){ + list_add(&dev->list, &fuse_requests); + schedule_work(&fuse_work); + } + + return IRQ_HANDLED; +} + +void mconsole_fuse(struct mc_request *req) +{ + int fd, err; + char *error; + struct fuse_dev *dev; + + fd = req->ancillary; + if(req->anc_len != sizeof(req->ancillary)){ + error = "Failed to receive FUSE file descriptor"; + goto err; + } + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if(dev == NULL){ + error = "Failed to allocate device id"; + goto err_close; + } + + INIT_LIST_HEAD(&dev->list); + dev->session = uml_fuse_new(fd); + if(dev->session == NULL){ + error = "uml_fuse_init failed"; + goto err_free; + } + + err = um_request_irq(FUSE_IRQ, fd, IRQ_READ, fuse_intr, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, + "umlfs", dev); + if(err){ + printk("Failed to allocate FUSE irq, err = %d\n", err); + error = "Failed to allocate FUSE irq"; + goto err_session; + } + + mconsole_reply(req, "", 0, 0); + return; + +err_session: + uml_fuse_free(dev->session); +err_free: + kfree(dev); +err_close: + os_close_file(fd); +err: + mconsole_reply(req, error, 1, 0); +} +#else +void mconsole_fuse(struct mc_request *req) +{ + mconsole_reply(req, "CONFIG_FUSE_SERVER is not enabled", 1, 0); +} +#endif + /* * Changed by mconsole_setup, which is __setup, and called before SMP is * active. Index: linux-2.6.17/arch/um/drivers/mconsole_user.c =================================================================== --- linux-2.6.17.orig/arch/um/drivers/mconsole_user.c 2007-10-24 10:04:50.000000000 -0400 +++ linux-2.6.17/arch/um/drivers/mconsole_user.c 2007-11-19 11:36:23.000000000 -0500 @@ -34,6 +34,7 @@ static struct mconsole_command commands[ { "log", mconsole_log, MCONSOLE_INTR }, { "proc", mconsole_proc, MCONSOLE_PROC }, { "stack", mconsole_stack, MCONSOLE_INTR }, + { "umlfs", mconsole_fuse, MCONSOLE_PROC }, }; /* Initialized in mconsole_init, which is an initcall */ @@ -80,17 +81,43 @@ static struct mconsole_command *mconsole int mconsole_get_request(int fd, struct mc_request *req) { + int data; + char buf[CMSG_SPACE(sizeof(data))]; + struct iovec iov; + struct msghdr hdr; + struct cmsghdr *cmsg; int len; req->originlen = sizeof(req->origin); - req->len = recvfrom(fd, &req->request, sizeof(req->request), - MSG_DONTWAIT, (struct sockaddr *) req->origin, - &req->originlen); - if (req->len < 0) + iov = ((struct iovec) { .iov_base = &req->request, + .iov_len = sizeof(req->request) + }); + hdr = ((struct msghdr) { .msg_name = + (struct sockaddr *) req->origin, + .msg_namelen = req->originlen, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = buf, + .msg_controllen = sizeof(buf), + .msg_flags = 0 }); + req->len = recvmsg(fd, &hdr, 0); + req->originlen = hdr.msg_namelen; + if (req->len < 0) { + if(errno != EAGAIN) + printk("mconsole_get_request - rcvmsg failed, " + "errno = %d\n", errno); return 0; + } req->originating_fd = fd; + req->anc_len = 0; + cmsg = CMSG_FIRSTHDR(&hdr); + if (cmsg != NULL) { + req->ancillary = *((int *) CMSG_DATA(cmsg)); + req->anc_len = sizeof(req->ancillary); + } + if (req->request.magic != MCONSOLE_MAGIC) { /* Unversioned request */ len = MIN(sizeof(req->request.data) - 1, Index: linux-2.6.17/arch/um/drivers/port_kern.c =================================================================== --- linux-2.6.17.orig/arch/um/drivers/port_kern.c 2007-10-24 10:04:50.000000000 -0400 +++ linux-2.6.17/arch/um/drivers/port_kern.c 2007-11-19 11:37:47.000000000 -0500 @@ -44,7 +44,8 @@ static irqreturn_t pipe_interrupt(int ir struct connection *conn = data; int fd; - fd = os_rcv_fd(conn->socket[0], &conn->helper_pid); + fd = os_rcv_fd(conn->socket[0], &conn->helper_pid, + sizeof(conn->helper_pid)); if (fd < 0) { if (fd == -EAGAIN) return IRQ_NONE; Index: linux-2.6.17/arch/um/drivers/xterm_kern.c =================================================================== --- linux-2.6.17.orig/arch/um/drivers/xterm_kern.c 2007-10-17 12:11:50.000000000 -0400 +++ linux-2.6.17/arch/um/drivers/xterm_kern.c 2007-11-19 11:32:36.000000000 -0500 @@ -22,7 +22,7 @@ static irqreturn_t xterm_interrupt(int i struct xterm_wait *xterm = data; int fd; - fd = os_rcv_fd(xterm->fd, &xterm->pid); + fd = os_rcv_fd(xterm->fd, &xterm->pid, sizeof(xterm->pid)); if (fd == -EAGAIN) return IRQ_NONE; Index: linux-2.6.17/arch/um/include/mconsole.h =================================================================== --- linux-2.6.17.orig/arch/um/include/mconsole.h 2007-10-24 10:04:50.000000000 -0400 +++ linux-2.6.17/arch/um/include/mconsole.h 2007-11-19 11:38:13.000000000 -0500 @@ -64,6 +64,8 @@ struct mc_request struct mconsole_request request; struct mconsole_command *cmd; struct uml_pt_regs regs; + int ancillary; + int anc_len; }; extern char mconsole_socket_name[]; @@ -87,6 +89,7 @@ extern void mconsole_go(struct mc_reques extern void mconsole_log(struct mc_request *req); extern void mconsole_proc(struct mc_request *req); extern void mconsole_stack(struct mc_request *req); +extern void mconsole_fuse(struct mc_request *req); extern int mconsole_get_request(int fd, struct mc_request *req); extern int mconsole_notify(char *sock_name, int type, const void *data, Index: linux-2.6.17/arch/um/include/os.h =================================================================== --- linux-2.6.17.orig/arch/um/include/os.h 2007-11-19 11:31:59.000000000 -0500 +++ linux-2.6.17/arch/um/include/os.h 2007-11-19 11:32:36.000000000 -0500 @@ -185,7 +185,7 @@ extern int os_remove_dir(const char *dir extern int os_make_dev(const char *name, int mode, int major, int minor); extern int os_shutdown_socket(int fd, int r, int w); extern void os_close_file(int fd); -extern int os_rcv_fd(int fd, int *helper_pid_out); +extern int os_rcv_fd(int fd, void *msg, int msg_len); extern int create_unix_socket(char *file, int len, int close_on_exec); extern int os_connect_socket(char *name); extern int os_file_type(char *file); Index: linux-2.6.17/arch/um/os-Linux/file.c =================================================================== --- linux-2.6.17.orig/arch/um/os-Linux/file.c 2007-11-19 11:31:59.000000000 -0500 +++ linux-2.6.17/arch/um/os-Linux/file.c 2007-11-19 11:39:26.000000000 -0500 @@ -670,31 +670,31 @@ int os_shutdown_socket(int fd, int r, in return 0; } -int os_rcv_fd(int fd, int *helper_pid_out) +int os_rcv_fd(int fd, void *msg, int msg_len) { int new, n; char buf[CMSG_SPACE(sizeof(new))]; - struct msghdr msg; + struct msghdr hdr; struct cmsghdr *cmsg; struct iovec iov; - msg.msg_name = NULL; - msg.msg_namelen = 0; - iov = ((struct iovec) { .iov_base = helper_pid_out, - .iov_len = sizeof(*helper_pid_out) }); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = buf; - msg.msg_controllen = sizeof(buf); - msg.msg_flags = 0; + hdr.msg_name = NULL; + hdr.msg_namelen = 0; + iov = ((struct iovec) { .iov_base = msg, + .iov_len = msg_len }); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = buf; + hdr.msg_controllen = sizeof(buf); + hdr.msg_flags = 0; - n = recvmsg(fd, &msg, 0); + n = recvmsg(fd, &hdr, 0); if(n < 0) return -errno; else if(n != iov.iov_len) - *helper_pid_out = -1; + return -EINVAL; - cmsg = CMSG_FIRSTHDR(&msg); + cmsg = CMSG_FIRSTHDR(&hdr); if(cmsg == NULL){ printk("rcv_fd didn't receive anything, error = %d\n", errno); return -1; Index: linux-2.6.17/include/asm-um/irq.h =================================================================== --- linux-2.6.17.orig/include/asm-um/irq.h 2007-11-19 11:22:17.000000000 -0500 +++ linux-2.6.17/include/asm-um/irq.h 2007-11-19 11:32:36.000000000 -0500 @@ -16,7 +16,8 @@ #define XTERM_IRQ 12 #define AIO_IRQ 13 #define X11_IRQ 14 -#define LAST_IRQ X11_IRQ +#define FUSE_IRQ 15 +#define LAST_IRQ FUSE_IRQ #define NR_IRQS (LAST_IRQ + 1) Index: linux-2.6.17/arch/um/include/uml-fuse.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17/arch/um/include/uml-fuse.h 2007-11-19 11:32:36.000000000 -0500 @@ -0,0 +1,87 @@ +#ifndef __FUSE_H___ +#define __FUSE_H___ + +extern void *uml_fuse_new(int fd); +extern void uml_fuse_free(void *session); +extern int uml_fuse_request(void *se); + +extern int fuse_vfs_stat(unsigned long long file, unsigned long long *inode, + unsigned long *mode, unsigned long *nlink, + unsigned long *uid, unsigned long *gid, + unsigned long *rdev, unsigned long long *size, + unsigned long *blksize, unsigned long long *blocks, + unsigned long long *atime, unsigned long long *mtime, + unsigned long long *ctime); +extern int fuse_vfs_lookup(unsigned long long parent, const char *file, + unsigned long long *inode, unsigned long *mode, + unsigned long *nlink, unsigned long *uid, + unsigned long *gid, unsigned long *rdev, + unsigned long long *size, unsigned long *blksize, + unsigned long long *blocks, + unsigned long long *atime, + unsigned long long *mtime, + unsigned long long *ctime); +extern int fuse_vfs_opendir(unsigned long long inode, void **file_out); +extern int fuse_vfs_readdir(void *f, void *data); +extern void fuse_vfs_release(void *f); +extern unsigned long long fuse_vfs_lookup_root(void); +extern void fuse_vfs_forget(unsigned long long inode, int nforgets); +extern int fuse_vfs_readlink(unsigned long long ino, char *buf, int bufsize); + +#define OPEN_READ 0 +#define OPEN_WRITE 1 +#define OPEN_APPEND 2 +#define OPEN_NONBLOCK 3 +#define OPEN_SYNC 4 +#define OPEN_FASYNC 5 +#define OPEN_DIRECT 6 +#define OPEN_LARGEFILE 7 +#define OPEN_DIRECTORY 8 +#define OPEN_NOFOLLOW 9 +#define OPEN_NOATIME 10 + +extern unsigned long fuse_vfs_open(unsigned long long ino, + unsigned long uflags, void **file_out); +extern int fuse_vfs_read(void *f, unsigned long long off, void *buf, + unsigned long size); +extern int fuse_vfs_write(void *f, unsigned long long off, const void *buf, + unsigned long size); +extern int fuse_vfs_create(unsigned long long parent, const char *name, + int flags, int perms, void **file_out, + unsigned long long *inode, unsigned long *mode, + unsigned long *nlink, unsigned long *uid, + unsigned long *gid, unsigned long *rdev, + unsigned long long *size, unsigned long *blksize, + unsigned long long *blocks, + unsigned long long *atime, + unsigned long long *mtime, + unsigned long long *ctime); + +#define UML_FUSE_MODE (1 << 0) +#define UML_FUSE_UID (1 << 1) +#define UML_FUSE_GID (1 << 2) +#define UML_FUSE_SIZE (1 << 3) +#define UML_FUSE_ATIME (1 << 4) +#define UML_FUSE_MTIME (1 << 5) + +extern int fuse_vfs_setattr(unsigned long long file, int mask, + unsigned long long *inode, unsigned long *mode, + unsigned long *nlink, unsigned long *uid, + unsigned long *gid, unsigned long *rdev, + unsigned long long *size, unsigned long *blksize, + unsigned long long *blocks, + unsigned long long *atime, + unsigned long long *mtime, + unsigned long long *ctime); + +struct dirbuf { + char *p; + size_t size; + size_t max; + size_t allocated; +}; + +extern int dirbuf_add(struct dirbuf *b, char *name, int namelen, + unsigned long long ino, unsigned long mode); + +#endif Index: linux-2.6.17/arch/um/kernel/Makefile =================================================================== --- linux-2.6.17.orig/arch/um/kernel/Makefile 2007-11-19 10:52:01.000000000 -0500 +++ linux-2.6.17/arch/um/kernel/Makefile 2007-11-19 11:32:36.000000000 -0500 @@ -14,6 +14,7 @@ obj-y = aio.o config.o exec.o exitcode.o obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o obj-$(CONFIG_GPROF) += gprof_syms.o obj-$(CONFIG_GCOV) += gmon_syms.o +obj-$(CONFIG_FUSE_SERVER) += fuse.o USER_OBJS := config.o Index: linux-2.6.17/arch/um/kernel/fuse.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17/arch/um/kernel/fuse.c 2007-11-19 11:32:36.000000000 -0500 @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/stat.h" +#include "linux/fs.h" +#include "linux/namei.h" +#include "linux/file.h" +#include "linux/mount.h" +#include "linux/rbtree.h" +#include "linux/security.h" +#include "linux/spinlock.h" +#include "uml-fuse.h" +#include "os.h" + +static int do_stat(struct dentry *dentry, struct vfsmount *mnt, + unsigned long long *inode, unsigned long *mode, + unsigned long *nlink, unsigned long *uid, unsigned long *gid, + unsigned long *rdev, unsigned long long *size, + unsigned long *blksize, unsigned long long *blocks, + unsigned long long *atime, unsigned long long *mtime, + unsigned long long *ctime) +{ + struct kstat stat; + int error; + + error = vfs_getattr(mnt, dentry, &stat); + if(error) + goto err; + + *inode = stat.ino; + *mode = stat.mode; + *nlink = stat.nlink; + *uid = stat.uid; + *gid = stat.gid; + *rdev = stat.rdev; + *size = stat.size; + *blksize = stat.blksize; + *blocks = stat.blocks; + *atime = stat.atime.tv_sec; + *ctime = stat.ctime.tv_sec; + *mtime = stat.mtime.tv_sec; +err: + return error; +} + +struct dentry_tree { + struct rb_node rb; + atomic_t count; + struct vfsmount *mnt; + struct dentry *dentry; +}; + +/* Protects the rb_tree structure */ +static SPINLOCK(dentry_spinlock); +static struct rb_root dentry_root = RB_ROOT; + +static struct dentry_tree *rb_add_dentry(struct dentry_tree *new) +{ + struct rb_node **node = &dentry_root.rb_node; + struct rb_node * parent = NULL; + struct dentry_tree *tree; + unsigned long long inode = new->dentry->d_inode->i_ino; + + spin_lock(&dentry_spinlock); + + node = &dentry_root.rb_node; + while(*node != NULL){ + unsigned long long this; + + parent = *node; + tree = rb_entry(*node, struct dentry_tree, rb); + this = tree->dentry->d_inode->i_ino; + if(this == inode) + goto out; + else if(this < inode) + node = &(*node)->rb_left; + else node = &(*node)->rb_right; + } + + rb_link_node(&new->rb, parent, node); + rb_insert_color(&new->rb, &dentry_root); + tree = NULL; +out: + spin_unlock(&dentry_spinlock); + + return tree; +} + +static struct dentry_tree *rb_find_dentry(unsigned long long inode) +{ + struct rb_node *node = dentry_root.rb_node; + struct dentry_tree *tree; + + spin_lock(&dentry_spinlock); + + node = &dentry_root.rb_node; + while(node != NULL){ + unsigned long long this; + + tree = rb_entry(node, struct dentry_tree, rb); + this = tree->dentry->d_inode->i_ino; + if(this == inode) + goto out; + else if(this < inode) + node = node->rb_left; + else node = node->rb_right; + } + tree = NULL; +out: + spin_unlock(&dentry_spinlock); + + return tree; +} + +static struct dentry_tree *remember_dentry(struct dentry *dentry, + struct vfsmount *mnt) +{ + struct dentry_tree *ret, *rb = kmalloc(sizeof(*rb), GFP_KERNEL); + + if(rb == NULL) + return ERR_PTR(-ENOMEM); + + *rb = ((struct dentry_tree) + { .count = ATOMIC_INIT(1), + .mnt = mntget(mnt), + .dentry = dget(dentry) }); + + ret = rb_add_dentry(rb); + if(ret != NULL){ + /* + * Another CPU added the entry, so delete what we just + * allocated because it's not being used. + */ + kfree(rb); + } + return ret; +} + +static struct dentry_tree *save_dentry(struct dentry *dentry, + struct vfsmount *mnt) +{ + struct dentry_tree *rb = rb_find_dentry(dentry->d_inode->i_ino); + + if(rb != NULL) + return rb; + + return remember_dentry(dentry, mnt); +} + +unsigned long long fuse_vfs_lookup_root(void) +{ + if(IS_ERR(remember_dentry(current->fs->root, current->fs->rootmnt))) + printk("Failed to look up root\n"); + + return(current->fs->root->d_inode->i_ino); +} + +static struct dentry *do_vfs_lookup(unsigned long long parent, const char *file, + struct vfsmount **mnt_out) +{ + struct dentry_tree *dir, *rb; + struct vfsmount *mnt; + struct dentry *dentry; + struct nameidata nd; + int error = -ENOENT; + + dir = rb_find_dentry(parent); + if(dir == NULL) + goto err; + + nd.mnt = mntget(dir->mnt); + nd.dentry = dget(dir->dentry); + nd.last_type = 0; + nd.flags = 0; + nd.depth = 0; + + error = path_walk(file, &nd); + if(error) + goto err; + + dentry = nd.dentry; + mnt = nd.mnt; + + rb = save_dentry(dentry, mnt); + if(IS_ERR(rb)){ + error = PTR_ERR(rb); + goto err_nd; + } + atomic_inc(&rb->count); + + path_release(&nd); + + *mnt_out = mnt; + return dentry; + +err_nd: + path_release(&nd); +err: + return ERR_PTR(error); +} + +int fuse_vfs_lookup(unsigned long long parent, const char *file, + unsigned long long *inode, unsigned long *mode, + unsigned long *nlink, unsigned long *uid, + unsigned long *gid, unsigned long *rdev, + unsigned long long *size, unsigned long *blksize, + unsigned long long *blocks, unsigned long long *atime, + unsigned long long *mtime, unsigned long long *ctime) +{ + struct dentry *dentry; + struct vfsmount *mnt; + int error; + + dentry = do_vfs_lookup(parent, file, &mnt); + + if(IS_ERR(dentry)){ + error = PTR_ERR(dentry); + goto err; + } + + error = do_stat(dentry, mnt, inode, mode, nlink, uid, gid, rdev, size, + blksize, blocks, atime, mtime, ctime); +err: + return error; +} + +int fuse_vfs_stat(unsigned long long file, unsigned long long *inode, + unsigned long *mode, unsigned long *nlink, unsigned long *uid, + unsigned long *gid, unsigned long *rdev, + unsigned long long *size, unsigned long *blksize, + unsigned long long *blocks, unsigned long long *atime, + unsigned long long *mtime, unsigned long long *ctime) +{ + struct dentry_tree *dir = rb_find_dentry(file); + int error = -ENOENT; + + if(dir == NULL) + goto err; + + error = do_stat(dir->dentry, dir->mnt, inode, mode, nlink, uid, gid, + rdev, size, blksize, blocks, atime, mtime, ctime); +err: + return error; +} + +int fuse_vfs_setattr(unsigned long long file, int mask, + unsigned long long *inode, unsigned long *mode, + unsigned long *nlink, unsigned long *uid, + unsigned long *gid, unsigned long *rdev, + unsigned long long *size, unsigned long *blksize, + unsigned long long *blocks, unsigned long long *atime, + unsigned long long *mtime, unsigned long long *ctime) +{ + struct iattr attrs; + struct dentry_tree *rb = rb_find_dentry(file); + int err; + + log_info("fuse_vfs_setattr - rb = 0x%p\n", rb); + if(rb == NULL) + return -ENOENT; + atomic_inc(&rb->count); + + attrs.ia_valid = 0; + if(mask & UML_FUSE_MODE){ + attrs.ia_valid |= ATTR_MODE; + attrs.ia_mode = *mode; + } + if(mask & UML_FUSE_UID){ + attrs.ia_valid |= ATTR_UID; + attrs.ia_uid = *uid; + } + if(mask & UML_FUSE_GID){ + attrs.ia_valid |= ATTR_GID; + attrs.ia_gid = *gid; + } + if(mask & UML_FUSE_SIZE){ + attrs.ia_valid |= ATTR_SIZE; + attrs.ia_size = *size; + } + if(mask & UML_FUSE_ATIME){ + attrs.ia_valid |= ATTR_ATIME; + attrs.ia_atime.tv_sec = *atime / BILLION; + attrs.ia_atime.tv_nsec = *atime % BILLION; + } + if(mask & UML_FUSE_MTIME){ + attrs.ia_valid |= ATTR_MTIME; + attrs.ia_mtime.tv_sec = *mtime / BILLION; + attrs.ia_mtime.tv_nsec = *mtime % BILLION; + } + + err = notify_change(rb->dentry, &attrs); + if(err) + goto err; + + err = do_stat(rb->dentry, rb->mnt, inode, mode, nlink, uid, gid, rdev, + size, blksize, blocks, atime, mtime, ctime); + +err: + atomic_dec(&rb->count); + return err; +} + +void fuse_vfs_forget(unsigned long long inode, int nforgets) +{ + struct dentry_tree *node = rb_find_dentry(inode); + + if(node == NULL){ + printk("dentry for inode %Ld not found\n", inode); + return; + } + + if(atomic_sub_and_test(nforgets, &node->count)){ + spin_lock(&dentry_lock); + rb_erase(&node->rb, &dentry_root); + spin_unlock(&dentry_lock); + dput(node->dentry); + kfree(node); + } +} + +int fuse_vfs_opendir(unsigned long long inode, void **file_out) +{ + struct file *file; + struct dentry_tree *rb; + + rb = rb_find_dentry(inode); + if(rb == NULL) + return -ENOENT; + + file = dentry_open(dget(rb->dentry), mntget(rb->mnt), + O_RDONLY | O_LARGEFILE | O_DIRECTORY); + if(IS_ERR(file)) + return PTR_ERR(file); + + *file_out = file; + return 0; +} + +static int filldir(void *buf, const char *name, int namelen, loff_t pos, + ino_t ino, unsigned int type) +{ + return dirbuf_add(buf, (char *) name, namelen, ino, type << 12); +} + +int fuse_vfs_readdir(void *f, void *data) +{ + struct file *file = f; + int err; + + err = vfs_readdir(file, filldir, data); + if(err) + return err; + return 0; +} + +void fuse_vfs_release(void *f) +{ + fput((struct file *) f); +} + +int fuse_vfs_readlink(unsigned long long ino, char *buf, int bufsize) +{ + struct inode *inode; + struct dentry *dentry; + struct dentry_tree *rb; + int error; + + rb = rb_find_dentry(ino); + if(rb == NULL) + return -ENOENT; + + /* This is a copy of the guts of sys_readlinkat - there is no vfs + * interface which does a readlink given a dentry. + */ + dentry = rb->dentry; + inode = dentry->d_inode; + error = -EINVAL; + if (inode->i_op && inode->i_op->readlink) { + error = security_inode_readlink(dentry); + if (!error) { + touch_atime(rb->mnt, dentry); + error = inode->i_op->readlink(dentry, buf, bufsize); + + if((error >= 0) && (error < bufsize)) + buf[error] = '\0'; + } + } + + return error; +} + +static const int flag_bits[] = { [OPEN_APPEND] = O_APPEND, + [OPEN_NONBLOCK] = O_NONBLOCK, + [OPEN_SYNC] = O_SYNC, + [OPEN_FASYNC] = FASYNC, + [OPEN_DIRECT] = O_DIRECT, + [OPEN_LARGEFILE] = O_LARGEFILE, + [OPEN_NOFOLLOW] = O_NOFOLLOW, + [OPEN_NOATIME] = O_NOATIME }; + +static int convert_mode(int flags) +{ + int i, mode = 0; + + for(i = 0; i < sizeof(mode) * 8; i++){ + if(flags & (1 << i)) + mode |= flag_bits[i]; + } + mode = flags & ((1 << OPEN_READ) | (1 << OPEN_WRITE)); + switch(mode){ + case 1 << OPEN_READ: + mode |= O_RDONLY; break; + case 1 << OPEN_WRITE: + mode |= O_WRONLY; break; + case (1 << OPEN_READ) | (1 << OPEN_WRITE): + mode |= O_RDWR; break; + default: + printk("fuse_vfs_open - bad mode %d\n", mode); break; + } + + return mode; +} + +unsigned long fuse_vfs_open(unsigned long long ino, unsigned long flags, + void **file_out) +{ + struct dentry_tree *rb; + struct vfsmount *mnt; + struct dentry *dentry; + struct file *file; + int error; + + rb = rb_find_dentry(ino); + if(rb == NULL) + return -ENOENT; + + dentry = dget(rb->dentry); + mnt = mntget(rb->mnt); + file = dentry_open(dentry, mnt, convert_mode(flags)); + if(IS_ERR(file)) + return PTR_ERR(file); + + log_info("use_vfs_open saving inode %ld\n", dentry->d_inode->i_ino); + rb = save_dentry(dentry, mnt); + if(IS_ERR(rb)){ + error = PTR_ERR(rb); + goto err; + } + atomic_inc(&rb->count); + + *file_out = file; + return 0; + +err: + fput(file); + return error; +} + +int fuse_vfs_create(unsigned long long parent, const char *name, int flags, + int perms, void **file_out, unsigned long long *inode, + unsigned long *mode, unsigned long *nlink, + unsigned long *uid, unsigned long *gid, unsigned long *rdev, + unsigned long long *size, unsigned long *blksize, + unsigned long long *blocks, unsigned long long *atime, + unsigned long long *mtime, unsigned long long *ctime) +{ + struct dentry_tree *dir, *rb; + struct dentry *dentry; + struct vfsmount *mnt; + struct file *file; + int error = -ENOENT; + + dir = rb_find_dentry(parent); + if(dir == NULL) + goto err; + + dentry = lookup_one_len(name, dir->dentry, strlen(name)); + if(IS_ERR(dentry)){ + error = PTR_ERR(dentry); + goto err; + } + + error = vfs_create(dir->dentry->d_inode, dentry, perms, NULL); + if(error) + goto err; + + dentry = dget(dentry); + mnt = mntget(dir->mnt); + file = dentry_open(dentry, mnt, convert_mode(flags)); + if(IS_ERR(file)){ + error = PTR_ERR(file); + goto err_unlink; + } + + log_info("use_vfs_create saving inode %ld\n", dentry->d_inode->i_ino); + rb = save_dentry(dentry, mnt); + if(IS_ERR(rb)){ + error = PTR_ERR(rb); + goto err_fput; + } + atomic_inc(&rb->count); + + *file_out = file; + error = do_stat(dentry, dir->mnt, inode, mode, nlink, uid, gid, rdev, + size, blksize, blocks, atime, mtime, ctime); +err: + return error; + +err_fput: + fput(file); +err_unlink: + vfs_unlink(dentry->d_inode, dentry); + + return error; +} + +int fuse_vfs_read(void *f, unsigned long long off, void *buf, + unsigned long size) +{ + struct file *file = f; + loff_t pos = off; + int err; + + err = vfs_read(file, buf, size, &pos); + file->f_pos = pos; + + return err; +} + +int fuse_vfs_write(void *f, unsigned long long off, const void *buf, + unsigned long size) +{ + struct file *file = f; + loff_t pos = off; + int err; + + err = vfs_write(file, buf, size, &pos); + file->f_pos = pos; + + return err; +} +