--- /dev/null
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef AIO_H__
+#define AIO_H__
+
+enum aio_type { AIO_READ, AIO_WRITE, AIO_MMAP };
+
+struct aio_thread_reply {
+ void *data;
+ int err;
+};
+
+struct aio_context {
+ int reply_fd;
+};
+
+#define INIT_AIO_CONTEXT { .reply_fd = -1 }
+
+extern int submit_aio(enum aio_type type, int fd, char *buf, int len,
+ unsigned long long offset, int reply_fd, void *data);
+
+#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:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __FILEHANDLE_H__
+#define __FILEHANDLE_H__
+
+#include "linux/list.h"
+#include "linux/fs.h"
+#include "os.h"
+
+struct file_handle {
+ struct list_head list;
+ int fd;
+ char *(*get_name)(struct inode *);
+ struct inode *inode;
+ struct openflags flags;
+};
+
+extern struct file_handle bad_filehandle;
+
+extern int open_file(char *name, struct openflags flags, int mode);
+extern void *open_dir(char *file);
+extern int open_filehandle(char *name, struct openflags flags, int mode,
+ struct file_handle *fh);
+extern int read_file(struct file_handle *fh, unsigned long long offset,
+ char *buf, int len);
+extern int write_file(struct file_handle *fh, unsigned long long offset,
+ const char *buf, int len);
+extern int truncate_file(struct file_handle *fh, unsigned long long size);
+extern int close_file(struct file_handle *fh);
+extern void not_reclaimable(struct file_handle *fh);
+extern void is_reclaimable(struct file_handle *fh,
+ char *(name_proc)(struct inode *),
+ struct inode *inode);
+extern int filehandle_fd(struct file_handle *fh);
+extern int make_pipe(struct file_handle *fhs);
+
+#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:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/slab.h"
+#include "linux/list.h"
+#include "linux/spinlock.h"
+#include "linux/fs.h"
+#include "linux/errno.h"
+#include "filehandle.h"
+#include "os.h"
+#include "kern_util.h"
+
+static spinlock_t open_files_lock = SPIN_LOCK_UNLOCKED;
+static struct list_head open_files = LIST_HEAD_INIT(open_files);
+
+#define NUM_RECLAIM 128
+
+static void reclaim_fds(void)
+{
+ struct file_handle *victim;
+ int closed = NUM_RECLAIM;
+
+ spin_lock(&open_files_lock);
+ while(!list_empty(&open_files) && closed--){
+ victim = list_entry(open_files.prev, struct file_handle, list);
+ os_close_file(victim->fd);
+ victim->fd = -1;
+ list_del_init(&victim->list);
+ }
+ spin_unlock(&open_files_lock);
+}
+
+int open_file(char *name, struct openflags flags, int mode)
+{
+ int fd;
+
+ fd = os_open_file(name, flags, mode);
+ if(fd != -EMFILE)
+ return(fd);
+
+ reclaim_fds();
+ fd = os_open_file(name, flags, mode);
+
+ return(fd);
+}
+
+void *open_dir(char *file)
+{
+ void *dir;
+ int err;
+
+ dir = os_open_dir(file, &err);
+ if(dir != NULL)
+ return(dir);
+ if(err != -EMFILE)
+ return(ERR_PTR(err));
+
+ reclaim_fds();
+
+ dir = os_open_dir(file, &err);
+ if(dir == NULL)
+ dir = ERR_PTR(err);
+
+ return(dir);
+}
+
+void not_reclaimable(struct file_handle *fh)
+{
+ char *name;
+
+ if(fh->get_name == NULL)
+ return;
+
+ if(list_empty(&fh->list)){
+ name = (*fh->get_name)(fh->inode);
+ if(name != NULL){
+ fh->fd = open_file(name, fh->flags, 0);
+ kfree(name);
+ }
+ else printk("File descriptor %d has no name\n", fh->fd);
+ }
+ else {
+ spin_lock(&open_files_lock);
+ list_del_init(&fh->list);
+ spin_unlock(&open_files_lock);
+ }
+}
+
+void is_reclaimable(struct file_handle *fh, char *(name_proc)(struct inode *),
+ struct inode *inode)
+{
+ fh->get_name = name_proc;
+ fh->inode = inode;
+
+ spin_lock(&open_files_lock);
+ list_add(&fh->list, &open_files);
+ spin_unlock(&open_files_lock);
+}
+
+static int active_handle(struct file_handle *fh)
+{
+ int fd;
+ char *name;
+
+ if(!list_empty(&fh->list))
+ list_move(&fh->list, &open_files);
+
+ if(fh->fd != -1)
+ return(0);
+
+ if(fh->inode == NULL)
+ return(-ENOENT);
+
+ name = (*fh->get_name)(fh->inode);
+ if(name == NULL)
+ return(-ENOMEM);
+
+ fd = open_file(name, fh->flags, 0);
+ kfree(name);
+ if(fd < 0)
+ return(fd);
+
+ fh->fd = fd;
+ is_reclaimable(fh, fh->get_name, fh->inode);
+
+ return(0);
+}
+
+int filehandle_fd(struct file_handle *fh)
+{
+ int err;
+
+ err = active_handle(fh);
+ if(err)
+ return(err);
+
+ return(fh->fd);
+}
+
+static void init_fh(struct file_handle *fh, int fd, struct openflags flags)
+{
+ flags.c = 0;
+ *fh = ((struct file_handle) { .list = LIST_HEAD_INIT(fh->list),
+ .fd = fd,
+ .get_name = NULL,
+ .inode = NULL,
+ .flags = flags });
+}
+
+int open_filehandle(char *name, struct openflags flags, int mode,
+ struct file_handle *fh)
+{
+ int fd;
+
+ fd = open_file(name, flags, mode);
+ if(fd < 0)
+ return(fd);
+
+ init_fh(fh, fd, flags);
+ return(0);
+}
+
+int close_file(struct file_handle *fh)
+{
+ spin_lock(&open_files_lock);
+ list_del(&fh->list);
+ spin_unlock(&open_files_lock);
+
+ os_close_file(fh->fd);
+
+ fh->fd = -1;
+ return(0);
+}
+
+int read_file(struct file_handle *fh, unsigned long long offset, char *buf,
+ int len)
+{
+ int err;
+
+ err = active_handle(fh);
+ if(err)
+ return(err);
+
+ err = os_seek_file(fh->fd, offset);
+ if(err)
+ return(err);
+
+ return(os_read_file(fh->fd, buf, len));
+}
+
+int write_file(struct file_handle *fh, unsigned long long offset,
+ const char *buf, int len)
+{
+ int err;
+
+ err = active_handle(fh);
+ if(err)
+ return(err);
+
+ if(offset != -1)
+ err = os_seek_file(fh->fd, offset);
+ if(err)
+ return(err);
+
+ return(os_write_file(fh->fd, buf, len));
+}
+
+int truncate_file(struct file_handle *fh, unsigned long long size)
+{
+ int err;
+
+ err = active_handle(fh);
+ if(err)
+ return(err);
+
+ return(os_truncate_fd(fh->fd, size));
+}
+
+int make_pipe(struct file_handle *fhs)
+{
+ int fds[2], err;
+
+ err = os_pipe(fds, 1, 1);
+ if(err && (err != -EMFILE))
+ return(err);
+
+ if(err){
+ reclaim_fds();
+ err = os_pipe(fds, 1, 1);
+ }
+ if(err)
+ return(err);
+
+ init_fh(&fhs[0], fds[0], OPENFLAGS());
+ init_fh(&fhs[1], fds[1], OPENFLAGS());
+ return(0);
+}
+
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include "os.h"
+#include "helper.h"
+#include "aio.h"
+#include "init.h"
+#include "user.h"
+#include "mode.h"
+
+struct aio_thread_req {
+ enum aio_type type;
+ int io_fd;
+ unsigned long long offset;
+ char *buf;
+ int len;
+ int reply_fd;
+ void *data;
+};
+
+static int aio_req_fd_r = -1;
+static int aio_req_fd_w = -1;
+
+#if defined(HAVE_AIO_ABI)
+#include <linux/aio_abi.h>
+
+/* If we have the headers, we are going to build with AIO enabled.
+ * If we don't have aio in libc, we define the necessary stubs here.
+ */
+
+#if !defined(HAVE_AIO_LIBC)
+
+#define __NR_io_setup 245
+#define __NR_io_getevents 247
+#define __NR_io_submit 248
+
+static long io_setup(int n, aio_context_t *ctxp)
+{
+ return(syscall(__NR_io_setup, n, ctxp));
+}
+
+static long io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp)
+{
+ return(syscall(__NR_io_submit, ctx, nr, iocbpp));
+}
+
+static long io_getevents(aio_context_t ctx_id, long min_nr, long nr,
+ struct io_event *events, struct timespec *timeout)
+{
+ return(syscall(__NR_io_getevents, ctx_id, min_nr, nr, events, timeout));
+}
+
+#endif
+
+/* The AIO_MMAP cases force the mmapped page into memory here
+ * rather than in whatever place first touches the data. I used
+ * to do this by touching the page, but that's delicate because
+ * gcc is prone to optimizing that away. So, what's done here
+ * is we read from the descriptor from which the page was
+ * mapped. The caller is required to pass an offset which is
+ * inside the page that was mapped. Thus, when the read
+ * returns, we know that the page is in the page cache, and
+ * that it now backs the mmapped area.
+ */
+
+static int do_aio(aio_context_t ctx, enum aio_type type, int fd, char *buf,
+ int len, unsigned long long offset, void *data)
+{
+ struct iocb iocb, *iocbp = &iocb;
+ char c;
+ int err;
+
+ iocb = ((struct iocb) { .aio_data = (unsigned long) data,
+ .aio_reqprio = 0,
+ .aio_fildes = fd,
+ .aio_buf = (unsigned long) buf,
+ .aio_nbytes = len,
+ .aio_offset = offset,
+ .aio_reserved1 = 0,
+ .aio_reserved2 = 0,
+ .aio_reserved3 = 0 });
+
+ switch(type){
+ case AIO_READ:
+ iocb.aio_lio_opcode = IOCB_CMD_PREAD;
+ err = io_submit(ctx, 1, &iocbp);
+ break;
+ case AIO_WRITE:
+ iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
+ err = io_submit(ctx, 1, &iocbp);
+ break;
+ case AIO_MMAP:
+ iocb.aio_lio_opcode = IOCB_CMD_PREAD;
+ iocb.aio_buf = (unsigned long) &c;
+ iocb.aio_nbytes = sizeof(c);
+ err = io_submit(ctx, 1, &iocbp);
+ break;
+ default:
+ printk("Bogus op in do_aio - %d\n", type);
+ err = -EINVAL;
+ break;
+ }
+ if(err > 0)
+ err = 0;
+
+ return(err);
+}
+
+static aio_context_t ctx = 0;
+
+static int aio_thread(void *arg)
+{
+ struct aio_thread_reply reply;
+ struct io_event event;
+ int err, n, reply_fd;
+
+ signal(SIGWINCH, SIG_IGN);
+
+ while(1){
+ n = io_getevents(ctx, 1, 1, &event, NULL);
+ if(n < 0){
+ if(errno == EINTR)
+ continue;
+ printk("aio_thread - io_getevents failed, "
+ "errno = %d\n", errno);
+ }
+ else {
+ reply = ((struct aio_thread_reply)
+ { .data = (void *) event.data,
+ .err = event.res });
+ reply_fd =
+ ((struct aio_context *) event.data)->reply_fd;
+ err = os_write_file(reply_fd, &reply, sizeof(reply));
+ if(err != sizeof(reply))
+ printk("not_aio_thread - write failed, "
+ "fd = %d, err = %d\n",
+ aio_req_fd_r, -err);
+ }
+ }
+ return(0);
+}
+
+#endif
+
+static int do_not_aio(struct aio_thread_req *req)
+{
+ char c;
+ int err;
+
+ switch(req->type){
+ case AIO_READ:
+ err = os_seek_file(req->io_fd, req->offset);
+ if(err)
+ goto out;
+
+ err = os_read_file(req->io_fd, req->buf, req->len);
+ break;
+ case AIO_WRITE:
+ err = os_seek_file(req->io_fd, req->offset);
+ if(err)
+ goto out;
+
+ err = os_write_file(req->io_fd, req->buf, req->len);
+ break;
+ case AIO_MMAP:
+ err = os_seek_file(req->io_fd, req->offset);
+ if(err)
+ goto out;
+
+ err = os_read_file(req->io_fd, &c, sizeof(c));
+ break;
+ default:
+ printk("do_not_aio - bad request type : %d\n", req->type);
+ err = -EINVAL;
+ break;
+ }
+
+ out:
+ return(err);
+}
+
+static int not_aio_thread(void *arg)
+{
+ struct aio_thread_req req;
+ struct aio_thread_reply reply;
+ int err;
+
+ signal(SIGWINCH, SIG_IGN);
+ while(1){
+ err = os_read_file(aio_req_fd_r, &req, sizeof(req));
+ if(err != sizeof(req)){
+ if(err < 0)
+ printk("not_aio_thread - read failed, fd = %d, "
+ "err = %d\n", aio_req_fd_r, -err);
+ else {
+ printk("not_aio_thread - short read, fd = %d, "
+ "length = %d\n", aio_req_fd_r, err);
+ }
+ continue;
+ }
+ err = do_not_aio(&req);
+ reply = ((struct aio_thread_reply) { .data = req.data,
+ .err = err });
+ err = os_write_file(req.reply_fd, &reply, sizeof(reply));
+ if(err != sizeof(reply))
+ printk("not_aio_thread - write failed, fd = %d, "
+ "err = %d\n", aio_req_fd_r, -err);
+ }
+}
+
+static int aio_pid = -1;
+
+static int init_aio_24(void)
+{
+ unsigned long stack;
+ int fds[2], err;
+
+ err = os_pipe(fds, 1, 1);
+ if(err)
+ goto out;
+
+ aio_req_fd_w = fds[0];
+ aio_req_fd_r = fds[1];
+ err = run_helper_thread(not_aio_thread, NULL,
+ CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0);
+ if(err < 0)
+ goto out_close_pipe;
+
+ aio_pid = err;
+ goto out;
+
+ out_close_pipe:
+ os_close_file(fds[0]);
+ os_close_file(fds[1]);
+ aio_req_fd_w = -1;
+ aio_req_fd_r = -1;
+ out:
+ return(0);
+}
+
+#ifdef HAVE_AIO_ABI
+#define DEFAULT_24_AIO 0
+static int init_aio_26(void)
+{
+ unsigned long stack;
+ int err;
+
+ if(io_setup(256, &ctx)){
+ printk("aio_thread failed to initialize context, err = %d\n",
+ errno);
+ return(-errno);
+ }
+
+ err = run_helper_thread(aio_thread, NULL,
+ CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0);
+ if(err < 0)
+ return(-errno);
+
+ aio_pid = err;
+ err = 0;
+ out:
+ return(err);
+}
+
+int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len,
+ unsigned long long offset, int reply_fd, void *data)
+{
+ struct aio_thread_reply reply;
+ int err;
+
+ ((struct aio_context *) data)->reply_fd = reply_fd;
+
+ err = do_aio(ctx, type, io_fd, buf, len, offset, data);
+ if(err){
+ reply = ((struct aio_thread_reply) { .data = data,
+ .err = err });
+ err = os_write_file(reply_fd, &reply, sizeof(reply));
+ if(err != sizeof(reply))
+ printk("submit_aio_26 - write failed, "
+ "fd = %d, err = %d\n", reply_fd, -err);
+ else err = 0;
+ }
+
+ return(err);
+}
+
+#else
+#define DEFAULT_24_AIO 1
+static int init_aio_26(void)
+{
+ return(-ENOSYS);
+}
+
+int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len,
+ unsigned long long offset, int reply_fd, void *data)
+{
+ return(-ENOSYS);
+}
+#endif
+
+static int aio_24 = DEFAULT_24_AIO;
+
+static int __init set_aio_24(char *name, int *add)
+{
+ aio_24 = 1;
+ return(0);
+}
+
+__uml_setup("aio=2.4", set_aio_24,
+"aio=2.4\n"
+" This is used to force UML to use 2.4-style AIO even when 2.6 AIO is\n"
+" available. 2.4 AIO is a single thread that handles one request at a\n"
+" time, synchronously. 2.6 AIO is a thread which uses 2.5 AIO interface\n"
+" to handle an arbitrary number of pending requests. 2.6 AIO is not\n"
+" available in tt mode, on 2.4 hosts, or when UML is built with\n"
+" /usr/include/linux/aio_abi no available.\n\n"
+);
+
+static int init_aio(void)
+{
+ int err;
+
+ CHOOSE_MODE(({
+ if(!aio_24){
+ printk("Disabling 2.6 AIO in tt mode\n");
+ aio_24 = 1;
+ } }), (void) 0);
+
+ if(!aio_24){
+ err = init_aio_26();
+ if(err && (errno == ENOSYS)){
+ printk("2.6 AIO not supported on the host - "
+ "reverting to 2.4 AIO\n");
+ aio_24 = 1;
+ }
+ else return(err);
+ }
+
+ if(aio_24)
+ return(init_aio_24());
+
+ return(0);
+}
+
+__initcall(init_aio);
+
+static void exit_aio(void)
+{
+ if(aio_pid != -1)
+ os_kill_process(aio_pid, 1);
+}
+
+__uml_exitcall(exit_aio);
+
+int submit_aio_24(enum aio_type type, int io_fd, char *buf, int len,
+ unsigned long long offset, int reply_fd, void *data)
+{
+ struct aio_thread_req req = { .type = type,
+ .io_fd = io_fd,
+ .offset = offset,
+ .buf = buf,
+ .len = len,
+ .reply_fd = reply_fd,
+ .data = data,
+ };
+ int err;
+
+ err = os_write_file(aio_req_fd_w, &req, sizeof(req));
+ if(err == sizeof(req))
+ err = 0;
+
+ return(err);
+}
+
+int submit_aio(enum aio_type type, int io_fd, char *buf, int len,
+ unsigned long long offset, int reply_fd, void *data)
+{
+ if(aio_24)
+ return(submit_aio_24(type, io_fd, buf, len, offset, reply_fd,
+ data));
+ else {
+ return(submit_aio_26(type, io_fd, buf, len, offset, reply_fd,
+ data));
+ }
+}
+
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * i386 semaphore implementation.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ *
+ * Portions Copyright 1999 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * rw semaphores implemented November 1999 by Benjamin LaHaise <bcrl@redhat.com>
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <asm/semaphore.h>
+
+/*
+ * Semaphores are implemented using a two-way counter:
+ * The "count" variable is decremented for each process
+ * that tries to acquire the semaphore, while the "sleeping"
+ * variable is a count of such acquires.
+ *
+ * Notably, the inline "up()" and "down()" functions can
+ * efficiently test if they need to do any extra work (up
+ * needs to do something only if count was negative before
+ * the increment operation.
+ *
+ * "sleeping" and the contention routine ordering is protected
+ * by the spinlock in the semaphore's waitqueue head.
+ *
+ * Note that these functions are only called when there is
+ * contention on the lock, and as such all this is the
+ * "non-critical" part of the whole semaphore business. The
+ * critical part is the inline stuff in <asm/semaphore.h>
+ * where we want to avoid any extra jumps and calls.
+ */
+
+/*
+ * Logic:
+ * - only on a boundary condition do we need to care. When we go
+ * from a negative count to a non-negative, we wake people up.
+ * - when we go from a non-negative count to a negative do we
+ * (a) synchronize with the "sleeper" count and (b) make sure
+ * that we're on the wakeup list before we synchronize so that
+ * we cannot lose wakeup events.
+ */
+
+asmlinkage void __up(struct semaphore *sem)
+{
+ wake_up(&sem->wait);
+}
+
+asmlinkage void __sched __down(struct semaphore * sem)
+{
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+ unsigned long flags;
+
+ tsk->state = TASK_UNINTERRUPTIBLE;
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ add_wait_queue_exclusive_locked(&sem->wait, &wait);
+
+ sem->sleepers++;
+ for (;;) {
+ int sleepers = sem->sleepers;
+
+ /*
+ * Add "everybody else" into it. They aren't
+ * playing, because we own the spinlock in
+ * the wait_queue_head.
+ */
+ if (!atomic_add_negative(sleepers - 1, &sem->count)) {
+ sem->sleepers = 0;
+ break;
+ }
+ sem->sleepers = 1; /* us - see -1 above */
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+
+ schedule();
+
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ tsk->state = TASK_UNINTERRUPTIBLE;
+ }
+ remove_wait_queue_locked(&sem->wait, &wait);
+ wake_up_locked(&sem->wait);
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+ tsk->state = TASK_RUNNING;
+}
+
+asmlinkage int __sched __down_interruptible(struct semaphore * sem)
+{
+ int retval = 0;
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+ unsigned long flags;
+
+ tsk->state = TASK_INTERRUPTIBLE;
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ add_wait_queue_exclusive_locked(&sem->wait, &wait);
+
+ sem->sleepers++;
+ for (;;) {
+ int sleepers = sem->sleepers;
+
+ /*
+ * With signals pending, this turns into
+ * the trylock failure case - we won't be
+ * sleeping, and we* can't get the lock as
+ * it has contention. Just correct the count
+ * and exit.
+ */
+ if (signal_pending(current)) {
+ retval = -EINTR;
+ sem->sleepers = 0;
+ atomic_add(sleepers, &sem->count);
+ break;
+ }
+
+ /*
+ * Add "everybody else" into it. They aren't
+ * playing, because we own the spinlock in
+ * wait_queue_head. The "-1" is because we're
+ * still hoping to get the semaphore.
+ */
+ if (!atomic_add_negative(sleepers - 1, &sem->count)) {
+ sem->sleepers = 0;
+ break;
+ }
+ sem->sleepers = 1; /* us - see -1 above */
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+
+ schedule();
+
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ tsk->state = TASK_INTERRUPTIBLE;
+ }
+ remove_wait_queue_locked(&sem->wait, &wait);
+ wake_up_locked(&sem->wait);
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+
+ tsk->state = TASK_RUNNING;
+ return retval;
+}
+
+/*
+ * Trylock failed - make sure we correct for
+ * having decremented the count.
+ *
+ * We could have done the trylock with a
+ * single "cmpxchg" without failure cases,
+ * but then it wouldn't work on a 386.
+ */
+asmlinkage int __down_trylock(struct semaphore * sem)
+{
+ int sleepers;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ sleepers = sem->sleepers + 1;
+ sem->sleepers = 0;
+
+ /*
+ * Add "everybody else" and us into it. They aren't
+ * playing, because we own the spinlock in the
+ * wait_queue_head.
+ */
+ if (!atomic_add_negative(sleepers, &sem->count)) {
+ wake_up_locked(&sem->wait);
+ }
+
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+ return 1;
+}
+
+
+/*
+ * The semaphore operations have a special calling sequence that
+ * allow us to do a simpler in-line version of them. These routines
+ * need to convert that sequence back into the C sequence when
+ * there is contention on the semaphore.
+ *
+ * %ecx contains the semaphore pointer on entry. Save the C-clobbered
+ * registers (%eax, %edx and %ecx) except %eax when used as a return
+ * value..
+ */
+asm(
+".section .sched.text\n"
+".align 4\n"
+".globl __down_failed\n"
+"__down_failed:\n\t"
+#if defined(CONFIG_FRAME_POINTER)
+ "pushl %ebp\n\t"
+ "movl %esp,%ebp\n\t"
+#endif
+ "pushl %eax\n\t"
+ "pushl %edx\n\t"
+ "pushl %ecx\n\t"
+ "call __down\n\t"
+ "popl %ecx\n\t"
+ "popl %edx\n\t"
+ "popl %eax\n\t"
+#if defined(CONFIG_FRAME_POINTER)
+ "movl %ebp,%esp\n\t"
+ "popl %ebp\n\t"
+#endif
+ "ret"
+);
+
+asm(
+".section .sched.text\n"
+".align 4\n"
+".globl __down_failed_interruptible\n"
+"__down_failed_interruptible:\n\t"
+#if defined(CONFIG_FRAME_POINTER)
+ "pushl %ebp\n\t"
+ "movl %esp,%ebp\n\t"
+#endif
+ "pushl %edx\n\t"
+ "pushl %ecx\n\t"
+ "call __down_interruptible\n\t"
+ "popl %ecx\n\t"
+ "popl %edx\n\t"
+#if defined(CONFIG_FRAME_POINTER)
+ "movl %ebp,%esp\n\t"
+ "popl %ebp\n\t"
+#endif
+ "ret"
+);
+
+asm(
+".section .sched.text\n"
+".align 4\n"
+".globl __down_failed_trylock\n"
+"__down_failed_trylock:\n\t"
+#if defined(CONFIG_FRAME_POINTER)
+ "pushl %ebp\n\t"
+ "movl %esp,%ebp\n\t"
+#endif
+ "pushl %edx\n\t"
+ "pushl %ecx\n\t"
+ "call __down_trylock\n\t"
+ "popl %ecx\n\t"
+ "popl %edx\n\t"
+#if defined(CONFIG_FRAME_POINTER)
+ "movl %ebp,%esp\n\t"
+ "popl %ebp\n\t"
+#endif
+ "ret"
+);
+
+asm(
+".section .sched.text\n"
+".align 4\n"
+".globl __up_wakeup\n"
+"__up_wakeup:\n\t"
+ "pushl %eax\n\t"
+ "pushl %edx\n\t"
+ "pushl %ecx\n\t"
+ "call __up\n\t"
+ "popl %ecx\n\t"
+ "popl %edx\n\t"
+ "popl %eax\n\t"
+ "ret"
+);
+
+/*
+ * rw spinlock fallbacks
+ */
+#if defined(CONFIG_SMP)
+asm(
+".section .sched.text\n"
+".align 4\n"
+".globl __write_lock_failed\n"
+"__write_lock_failed:\n\t"
+ LOCK "addl $" RW_LOCK_BIAS_STR ",(%eax)\n"
+"1: rep; nop\n\t"
+ "cmpl $" RW_LOCK_BIAS_STR ",(%eax)\n\t"
+ "jne 1b\n\t"
+ LOCK "subl $" RW_LOCK_BIAS_STR ",(%eax)\n\t"
+ "jnz __write_lock_failed\n\t"
+ "ret"
+);
+
+asm(
+".section .sched.text\n"
+".align 4\n"
+".globl __read_lock_failed\n"
+"__read_lock_failed:\n\t"
+ LOCK "incl (%eax)\n"
+"1: rep; nop\n\t"
+ "cmpl $1,(%eax)\n\t"
+ "js 1b\n\t"
+ LOCK "decl (%eax)\n\t"
+ "js __read_lock_failed\n\t"
+ "ret"
+);
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/stddef.h>
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/blkdev.h>
+#include <linux/statfs.h>
+#include <asm/uaccess.h>
+#include "hostfs.h"
+#include "kern_util.h"
+#include "kern.h"
+#include "user_util.h"
+#include "2_5compat.h"
+#include "mem.h"
+#include "filehandle.h"
+
+struct externfs {
+ struct list_head list;
+ struct externfs_mount_ops *mount_ops;
+ struct file_system_type type;
+};
+
+static inline struct externfs_inode *EXTERNFS_I(struct inode *inode)
+{
+ return(container_of(inode, struct externfs_inode, vfs_inode));
+}
+
+#define file_externfs_i(file) EXTERNFS_I((file)->f_dentry->d_inode)
+
+int externfs_d_delete(struct dentry *dentry)
+{
+ return(1);
+}
+
+struct dentry_operations externfs_dentry_ops = {
+};
+
+#define EXTERNFS_SUPER_MAGIC 0x00c0ffee
+
+static struct inode_operations externfs_iops;
+static struct inode_operations externfs_dir_iops;
+static struct address_space_operations externfs_link_aops;
+
+static char *dentry_name(struct dentry *dentry, int extra)
+{
+ struct dentry *parent;
+ char *name;
+ int len;
+
+ len = 0;
+ parent = dentry;
+ while(parent->d_parent != parent){
+ len += parent->d_name.len + 1;
+ parent = parent->d_parent;
+ }
+
+ name = kmalloc(len + extra + 1, GFP_KERNEL);
+ if(name == NULL) return(NULL);
+
+ name[len] = '\0';
+ parent = dentry;
+ while(parent->d_parent != parent){
+ len -= parent->d_name.len + 1;
+ name[len] = '/';
+ strncpy(&name[len + 1], parent->d_name.name,
+ parent->d_name.len);
+ parent = parent->d_parent;
+ }
+
+ return(name);
+}
+
+char *inode_name(struct inode *ino, int extra)
+{
+ struct dentry *dentry;
+
+ dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias);
+ return(dentry_name(dentry, extra));
+}
+
+char *inode_name_prefix(struct inode *inode, char *prefix)
+{
+ int len;
+ char *name;
+
+ len = strlen(prefix);
+ name = inode_name(inode, len);
+ if(name == NULL)
+ return(name);
+
+ memmove(&name[len], name, strlen(name) + 1);
+ memcpy(name, prefix, strlen(prefix));
+ return(name);
+}
+
+static int read_name(struct inode *ino, char *name)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->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
+ */
+ dev_t i_rdev;
+ int err;
+ int i_mode, i_nlink, i_blksize;
+ unsigned long atime, mtime, ctime;
+ unsigned long long i_size;
+ unsigned long long i_ino;
+ unsigned long long i_blocks;
+
+ err = (*ops->stat_file)(name, ino->i_sb->s_fs_info, &i_rdev, &i_ino,
+ &i_mode, &i_nlink, &ino->i_uid, &ino->i_gid,
+ &i_size, &atime, &mtime, &ctime, &i_blksize,
+ &i_blocks);
+ if(err) return(err);
+
+ ino->i_atime.tv_sec = atime;
+ ino->i_atime.tv_nsec = 0;
+
+ ino->i_ctime.tv_sec = ctime;
+ ino->i_ctime.tv_nsec = 0;
+
+ ino->i_mtime.tv_sec = mtime;
+ ino->i_mtime.tv_nsec = 0;
+
+ ino->i_ino = i_ino;
+ ino->i_rdev = i_rdev;
+ ino->i_mode = i_mode;
+ ino->i_nlink = i_nlink;
+ ino->i_size = i_size;
+ ino->i_blksize = i_blksize;
+ ino->i_blocks = i_blocks;
+ return(0);
+}
+
+static char *follow_link(char *link,
+ int (*do_read_link)(char *path, int uid, int gid,
+ char *buf, int size,
+ struct externfs_data *ed),
+ int uid, int gid, struct externfs_data *ed)
+{
+ int len, n;
+ char *name, *resolved, *end;
+
+ len = 64;
+ while(1){
+ n = -ENOMEM;
+ name = kmalloc(len, GFP_KERNEL);
+ if(name == NULL)
+ goto out;
+
+ n = (*do_read_link)(link, uid, gid, name, len, ed);
+ if(n < len)
+ break;
+ len *= 2;
+ kfree(name);
+ }
+ if(n < 0)
+ goto out_free;
+
+ if(*name == '/')
+ return(name);
+
+ end = strrchr(link, '/');
+ if(end == NULL)
+ return(name);
+
+ *(end + 1) = '\0';
+ len = strlen(link) + strlen(name) + 1;
+
+ resolved = kmalloc(len, GFP_KERNEL);
+ if(resolved == NULL){
+ n = -ENOMEM;
+ goto out_free;
+ }
+
+ sprintf(resolved, "%s%s", link, name);
+ kfree(name);
+ return(resolved);
+
+ out_free:
+ kfree(name);
+ out:
+ return(ERR_PTR(n));
+}
+
+static int read_inode(struct inode *ino)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *name, *new;
+ int err = 0, type;
+
+ /* Unfortunately, we are called from iget() when we don't have a dentry
+ * allocated yet.
+ */
+ if(list_empty(&ino->i_dentry))
+ goto out;
+
+ err = -ENOMEM;
+ name = inode_name(ino, 0);
+ if(name == NULL)
+ goto out;
+
+ type = (*ops->file_type)(name, NULL, ed);
+ if(type < 0){
+ err = type;
+ goto out_free;
+ }
+
+ if(type == OS_TYPE_SYMLINK){
+ new = follow_link(name, ops->read_link, current->fsuid,
+ current->fsgid, ed);
+ if(IS_ERR(new)){
+ err = PTR_ERR(new);
+ goto out_free;
+ }
+ kfree(name);
+ name = new;
+ }
+
+ err = read_name(ino, name);
+ out_free:
+ kfree(name);
+ out:
+ return(err);
+}
+
+int externfs_statfs(struct super_block *sb, struct kstatfs *sf)
+{
+ /* do_statfs uses struct statfs64 internally, but the linux kernel
+ * struct statfs still has 32-bit versions for most of these fields,
+ * so we convert them here
+ */
+ int err;
+ long long f_blocks;
+ long long f_bfree;
+ long long f_bavail;
+ long long f_files;
+ long long f_ffree;
+ struct externfs_data *ed = sb->s_fs_info;
+
+ err = (*ed->file_ops->statfs)(&sf->f_bsize, &f_blocks, &f_bfree,
+ &f_bavail, &f_files, &f_ffree,
+ &sf->f_fsid, sizeof(sf->f_fsid),
+ &sf->f_namelen, sf->f_spare, ed);
+ if(err)
+ return(err);
+
+ sf->f_blocks = f_blocks;
+ sf->f_bfree = f_bfree;
+ sf->f_bavail = f_bavail;
+ sf->f_files = f_files;
+ sf->f_ffree = f_ffree;
+ sf->f_type = EXTERNFS_SUPER_MAGIC;
+ return(0);
+}
+
+static struct inode *externfs_alloc_inode(struct super_block *sb)
+{
+ struct externfs_data *ed = sb->s_fs_info;
+ struct externfs_inode *ext;
+
+ ext = (*ed->mount_ops->init_file)(ed);
+ if(ext == NULL)
+ return(NULL);
+
+ *ext = ((struct externfs_inode) { .ops = ed->file_ops });
+
+ inode_init_once(&ext->vfs_inode);
+ return(&ext->vfs_inode);
+}
+
+static void externfs_destroy_inode(struct inode *inode)
+{
+ struct externfs_inode *ext = EXTERNFS_I(inode);
+
+ (*ext->ops->close_file)(ext, inode->i_size);
+}
+
+static void externfs_read_inode(struct inode *inode)
+{
+ read_inode(inode);
+}
+
+static struct super_operations externfs_sbops = {
+ .alloc_inode = externfs_alloc_inode,
+ .destroy_inode = externfs_destroy_inode,
+ .read_inode = externfs_read_inode,
+ .statfs = externfs_statfs,
+};
+
+int externfs_readdir(struct file *file, void *ent, filldir_t filldir)
+{
+ void *dir;
+ char *name;
+ unsigned long long next, ino;
+ int error, len;
+ struct externfs_file_ops *ops = file_externfs_i(file)->ops;
+ struct externfs_data *ed = file->f_dentry->d_inode->i_sb->s_fs_info;
+
+ name = dentry_name(file->f_dentry, 0);
+ if(name == NULL)
+ return(-ENOMEM);
+
+ dir = (*ops->open_dir)(name, current->fsuid, current->fsgid, ed);
+ kfree(name);
+ if(IS_ERR(dir))
+ return(PTR_ERR(dir));
+
+ next = file->f_pos;
+ while((name = (*ops->read_dir)(dir, &next, &ino, &len, ed)) != NULL){
+ error = (*filldir)(ent, name, len, file->f_pos, ino,
+ DT_UNKNOWN);
+ if(error)
+ break;
+ file->f_pos = next;
+ }
+ (*ops->close_dir)(dir, ed);
+ return(0);
+}
+
+int externfs_file_open(struct inode *ino, struct file *file)
+{
+ ino->i_nlink++;
+ return(0);
+}
+
+int externfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ struct externfs_file_ops *ops = file_externfs_i(file)->ops;
+ struct inode *inode = dentry->d_inode;
+ struct externfs_data *ed = inode->i_sb->s_fs_info;
+
+ return((*ops->truncate_file)(EXTERNFS_I(inode), inode->i_size, ed));
+}
+
+static struct file_operations externfs_file_fops = {
+ .llseek = generic_file_llseek,
+ .read = generic_file_read,
+ .write = generic_file_write,
+ .mmap = generic_file_mmap,
+ .open = externfs_file_open,
+ .release = NULL,
+ .fsync = externfs_fsync,
+};
+
+static struct file_operations externfs_dir_fops = {
+ .readdir = externfs_readdir,
+ .read = generic_read_dir,
+};
+
+struct wp_info {
+ struct page *page;
+ int count;
+ unsigned long long start;
+ unsigned long long size;
+ int (*truncate)(struct externfs_inode *ext, __u64 size,
+ struct externfs_data *ed);
+ struct externfs_inode *ei;
+ struct externfs_data *ed;
+};
+
+static void externfs_finish_writepage(char *buffer, int res, void *arg)
+{
+ struct wp_info *wp = arg;
+
+ if(res == wp->count){
+ ClearPageError(wp->page);
+ if(wp->start + res > wp->size)
+ (*wp->truncate)(wp->ei, wp->size, wp->ed);
+ }
+ else {
+ SetPageError(wp->page);
+ ClearPageUptodate(wp->page);
+ }
+
+ kunmap(wp->page);
+ unlock_page(wp->page);
+ kfree(wp);
+}
+
+int externfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = mapping->host;
+ struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
+ struct wp_info *wp;
+ struct externfs_data *ed = inode->i_sb->s_fs_info;
+ char *buffer;
+ unsigned long long base;
+ int count = PAGE_CACHE_SIZE;
+ int end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+ int err, offset;
+
+ base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;
+
+ /* If we are entirely outside the file, then return an error */
+ err = -EIO;
+ offset = inode->i_size & (PAGE_CACHE_SIZE-1);
+ if (page->index > end_index ||
+ ((page->index == end_index) && !offset))
+ goto out_unlock;
+
+ err = -ENOMEM;
+ wp = kmalloc(sizeof(*wp), GFP_KERNEL);
+ if(wp == NULL)
+ goto out_unlock;
+
+ *wp = ((struct wp_info) { .page = page,
+ .count = count,
+ .start = base,
+ .size = inode->i_size,
+ .truncate = ops->truncate_file,
+ .ei = EXTERNFS_I(inode),
+ .ed = ed });
+
+ buffer = kmap(page);
+ err = (*ops->write_file)(EXTERNFS_I(inode), base, buffer, 0,
+ count, externfs_finish_writepage, wp, ed);
+
+ return err;
+
+ out_unlock:
+ unlock_page(page);
+ return(err);
+}
+
+static void externfs_finish_readpage(char *buffer, int res, void *arg)
+{
+ struct page *page = arg;
+ struct inode *inode;
+
+ if(res < 0){
+ SetPageError(page);
+ goto out;
+ }
+
+ inode = page->mapping->host;
+ if(inode->i_size >> PAGE_CACHE_SHIFT == page->index)
+ res = inode->i_size % PAGE_CACHE_SIZE;
+
+ memset(&buffer[res], 0, PAGE_CACHE_SIZE - res);
+
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ if (PageError(page))
+ ClearPageError(page);
+ out:
+ kunmap(page);
+ unlock_page(page);
+}
+
+static int externfs_readpage(struct file *file, struct page *page)
+{
+ struct inode *ino = page->mapping->host;
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *buffer;
+ long long start;
+ int err = 0;
+
+ start = (long long) page->index << PAGE_CACHE_SHIFT;
+ buffer = kmap(page);
+
+ if(ops->map_file_page != NULL){
+ /* XXX What happens when PAGE_SIZE != PAGE_CACHE_SIZE? */
+ err = (*ops->map_file_page)(file_externfs_i(file), start,
+ buffer, file->f_mode & FMODE_WRITE,
+ ed);
+ if(!err)
+ err = PAGE_CACHE_SIZE;
+ }
+ else err = (*ops->read_file)(file_externfs_i(file), start, buffer,
+ PAGE_CACHE_SIZE, 0, 0,
+ externfs_finish_readpage, page, ed);
+
+ if(err > 0)
+ err = 0;
+ return(err);
+}
+
+struct writepage_info {
+ struct semaphore sem;
+ int res;
+};
+
+static void externfs_finish_prepare(char *buffer, int res, void *arg)
+{
+ struct writepage_info *wp = arg;
+
+ wp->res = res;
+ up(&wp->sem);
+}
+
+int externfs_prepare_write(struct file *file, struct page *page,
+ unsigned int from, unsigned int to)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = mapping->host;
+ struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
+ struct externfs_data *ed = inode->i_sb->s_fs_info;
+ char *buffer;
+ long long start;
+ int err;
+ struct writepage_info wp;
+
+ if(PageUptodate(page))
+ return(0);
+
+ start = (long long) page->index << PAGE_CACHE_SHIFT;
+ buffer = kmap(page);
+
+ if(ops->map_file_page != NULL){
+ err = (*ops->map_file_page)(file_externfs_i(file), start,
+ buffer, file->f_mode & FMODE_WRITE,
+ ed);
+ goto out;
+
+ }
+
+ init_MUTEX_LOCKED(&wp.sem);
+ err = (*ops->read_file)(file_externfs_i(file), start, buffer,
+ PAGE_CACHE_SIZE, from, to,
+ externfs_finish_prepare, &wp, ed);
+ down(&wp.sem);
+ if(err < 0)
+ goto out;
+
+ err = wp.res;
+ if(err < 0)
+ goto out;
+
+ if(from > 0)
+ memset(buffer, 0, from);
+ if(to < PAGE_CACHE_SIZE)
+ memset(buffer + to, 0, PAGE_CACHE_SIZE - to);
+
+ SetPageUptodate(page);
+ err = 0;
+ out:
+ kunmap(page);
+ return(err);
+}
+
+static int externfs_commit_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = mapping->host;
+ struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops;
+ unsigned long long size;
+ long long start;
+ int err;
+
+ start = (long long) (page->index << PAGE_CACHE_SHIFT);
+
+ if(ops->map_file_page != NULL)
+ err = to - from;
+ else {
+ size = start + to;
+ if(size > inode->i_size){
+ inode->i_size = size;
+ mark_inode_dirty(inode);
+ }
+ }
+
+ set_page_dirty(page);
+ return(to - from);
+}
+
+static int externfs_removepage(struct page *page, int gfpmask)
+{
+ physmem_remove_mapping(page_address(page));
+ return(0);
+}
+
+static struct address_space_operations externfs_aops = {
+ .writepage = externfs_writepage,
+ .readpage = externfs_readpage,
+ .releasepage = externfs_removepage,
+/* .set_page_dirty = __set_page_dirty_nobuffers, */
+ .prepare_write = externfs_prepare_write,
+ .commit_write = externfs_commit_write
+};
+
+static int init_inode(struct inode *inode, struct dentry *dentry)
+{
+ char *name = NULL;
+ int type, err = -ENOMEM, rdev;
+ struct externfs_inode *ext = EXTERNFS_I(inode);
+ struct externfs_file_ops *ops = ext->ops;
+ struct externfs_data *ed = inode->i_sb->s_fs_info;
+
+ if(dentry){
+ name = dentry_name(dentry, 0);
+ if(name == NULL)
+ goto out;
+ type = (*ops->file_type)(name, &rdev, ed);
+ }
+ else type = OS_TYPE_DIR;
+
+ err = 0;
+ if(type == OS_TYPE_SYMLINK)
+ inode->i_op = &page_symlink_inode_operations;
+ else if(type == OS_TYPE_DIR)
+ inode->i_op = &externfs_dir_iops;
+ else inode->i_op = &externfs_iops;
+
+ if(type == OS_TYPE_DIR) inode->i_fop = &externfs_dir_fops;
+ else inode->i_fop = &externfs_file_fops;
+
+ if(type == OS_TYPE_SYMLINK)
+ inode->i_mapping->a_ops = &externfs_link_aops;
+ else inode->i_mapping->a_ops = &externfs_aops;
+
+ switch (type) {
+ case OS_TYPE_CHARDEV:
+ init_special_inode(inode, S_IFCHR, rdev);
+ break;
+ case OS_TYPE_BLOCKDEV:
+ init_special_inode(inode, S_IFBLK, rdev);
+ break;
+ case OS_TYPE_FIFO:
+ init_special_inode(inode, S_IFIFO, 0);
+ break;
+ case OS_TYPE_SOCK:
+ init_special_inode(inode, S_IFSOCK, 0);
+ break;
+ case OS_TYPE_SYMLINK:
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
+ }
+
+ err = (*ops->open_file)(ext, name, current->fsuid, current->fsgid,
+ inode, ed);
+ if((err != -EISDIR) && (err != -ENOENT) && (err != -ENXIO))
+ goto out_put;
+
+ err = 0;
+
+ out_free:
+ kfree(name);
+ out:
+ return(err);
+
+ out_put:
+ iput(inode);
+ goto out_free;
+}
+
+int externfs_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd)
+{
+ struct externfs_inode *ext = EXTERNFS_I(dir);
+ struct externfs_file_ops *ops = ext->ops;
+ struct inode *inode;
+ struct externfs_data *ed = dir->i_sb->s_fs_info;
+ char *name;
+ int err = -ENOMEM;
+
+ inode = iget(dir->i_sb, 0);
+ if(inode == NULL)
+ goto out;
+
+ err = init_inode(inode, dentry);
+ if(err)
+ goto out_put;
+
+ err = -ENOMEM;
+ name = dentry_name(dentry, 0);
+ if(name == NULL)
+ goto out_put;
+
+ err = (*ops->create_file)(ext, name, mode, current->fsuid,
+ current->fsuid, inode, ed);
+ if(err)
+ goto out_free;
+
+ err = read_name(inode, name);
+ if(err)
+ goto out_rm;
+
+ inode->i_nlink++;
+ d_instantiate(dentry, inode);
+ kfree(name);
+ out:
+ return(err);
+
+ out_rm:
+ (*ops->unlink_file)(name, ed);
+ out_free:
+ kfree(name);
+ out_put:
+ inode->i_nlink = 0;
+ iput(inode);
+ goto out;
+}
+
+struct dentry *externfs_lookup(struct inode *ino, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct inode *inode;
+ char *name;
+ int err = -ENOMEM;
+
+ inode = iget(ino->i_sb, 0);
+ if(inode == NULL)
+ goto out;
+
+ err = init_inode(inode, dentry);
+ if(err)
+ goto out_put;
+
+ err = -ENOMEM;
+ name = dentry_name(dentry, 0);
+ if(name == NULL)
+ goto out_put;
+
+ err = read_name(inode, name);
+ kfree(name);
+ if(err){
+ if(err != -ENOENT)
+ goto out_put;
+
+ inode->i_nlink = 0;
+ iput(inode);
+ inode = NULL;
+ }
+ d_add(dentry, inode);
+ dentry->d_op = &externfs_dentry_ops;
+ return(NULL);
+
+ out_put:
+ inode->i_nlink = 0;
+ iput(inode);
+ out:
+ return(ERR_PTR(err));
+}
+
+static char *inode_dentry_name(struct inode *ino, struct dentry *dentry)
+{
+ char *file;
+ int len;
+
+ file = inode_name(ino, dentry->d_name.len + 1);
+ if(file == NULL) return(NULL);
+ strcat(file, "/");
+ len = strlen(file);
+ strncat(file, dentry->d_name.name, dentry->d_name.len);
+ file[len + dentry->d_name.len] = '\0';
+ return(file);
+}
+
+int externfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *from_name, *to_name;
+ int err = -ENOMEM;
+
+ from_name = inode_dentry_name(ino, from);
+ if(from_name == NULL)
+ goto out;
+
+ to_name = dentry_name(to, 0);
+ if(to_name == NULL)
+ goto out_free_from;
+
+ err = (*ops->link_file)(to_name, from_name, current->fsuid,
+ current->fsgid, ed);
+ if(err)
+ goto out_free_to;
+
+ d_instantiate(from, to->d_inode);
+ to->d_inode->i_nlink++;
+ atomic_inc(&to->d_inode->i_count);
+
+ out_free_to:
+ kfree(to_name);
+ out_free_from:
+ kfree(from_name);
+ out:
+ return(err);
+}
+
+int externfs_unlink(struct inode *ino, struct dentry *dentry)
+{
+ struct inode *inode;
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *file;
+ int err;
+
+ file = inode_dentry_name(ino, dentry);
+ if(file == NULL)
+ return(-ENOMEM);
+
+ inode = dentry->d_inode;
+ if((inode->i_nlink == 1) && (ops->invisible != NULL))
+ (*ops->invisible)(EXTERNFS_I(inode));
+
+ err = (*ops->unlink_file)(file, ed);
+ kfree(file);
+
+ inode->i_nlink--;
+
+ return(err);
+}
+
+int externfs_symlink(struct inode *ino, struct dentry *dentry, const char *to)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct inode *inode;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *file;
+ int err;
+
+ file = inode_dentry_name(ino, dentry);
+ if(file == NULL)
+ return(-ENOMEM);
+ err = (*ops->make_symlink)(file, to, current->fsuid, current->fsgid,
+ ed);
+ kfree(file);
+ if(err)
+ goto out;
+
+ err = -ENOMEM;
+ inode = iget(ino->i_sb, 0);
+ if(inode == NULL)
+ goto out;
+
+ err = init_inode(inode, dentry);
+ if(err)
+ goto out_put;
+
+ d_instantiate(dentry, inode);
+ inode->i_nlink++;
+ out:
+ return(err);
+
+ out_put:
+ iput(inode);
+ goto out;
+}
+
+int externfs_make_dir(struct inode *ino, struct dentry *dentry, int mode)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct inode *inode;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *file;
+ int err = -ENOMEM;
+
+ file = inode_dentry_name(ino, dentry);
+ if(file == NULL)
+ goto out;
+ err = (*ops->make_dir)(file, mode, current->fsuid, current->fsgid, ed);
+
+ err = -ENOMEM;
+ inode = iget(ino->i_sb, 0);
+ if(inode == NULL)
+ goto out_free;
+
+ err = init_inode(inode, dentry);
+ if(err)
+ goto out_put;
+
+ err = read_name(inode, file);
+ if(err)
+ goto out_put;
+
+ kfree(file);
+ d_instantiate(dentry, inode);
+ inode->i_nlink = 2;
+ inode->i_mode = S_IFDIR | mode;
+
+ ino->i_nlink++;
+ out:
+ return(err);
+ out_put:
+ inode->i_nlink = 0;
+ iput(inode);
+ out_free:
+ kfree(file);
+ goto out;
+}
+
+int externfs_remove_dir(struct inode *ino, struct dentry *dentry)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *file;
+ int err;
+
+ file = inode_dentry_name(ino, dentry);
+ if(file == NULL)
+ return(-ENOMEM);
+ err = (*ops->remove_dir)(file, current->fsuid, current->fsgid, ed);
+ kfree(file);
+
+ dentry->d_inode->i_nlink = 0;
+ ino->i_nlink--;
+ return(err);
+}
+
+int externfs_make_node(struct inode *dir, struct dentry *dentry, int mode,
+ dev_t dev)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(dir)->ops;
+ struct externfs_data *ed = dir->i_sb->s_fs_info;
+ struct inode *inode;
+ char *name;
+ int err = -ENOMEM;
+
+ inode = iget(dir->i_sb, 0);
+ if(inode == NULL)
+ goto out;
+
+ err = init_inode(inode, dentry);
+ if(err)
+ goto out_put;
+
+ err = -ENOMEM;
+ name = dentry_name(dentry, 0);
+ if(name == NULL)
+ goto out_put;
+
+ init_special_inode(inode, mode, dev);
+ err = (*ops->make_node)(name, mode & S_IRWXUGO, current->fsuid,
+ current->fsgid, mode & S_IFMT, MAJOR(dev),
+ MINOR(dev), ed);
+ if(err)
+ goto out_free;
+
+ err = read_name(inode, name);
+ if(err)
+ goto out_rm;
+
+ inode->i_nlink++;
+ d_instantiate(dentry, inode);
+ kfree(name);
+ out:
+ return(err);
+
+ out_rm:
+ (*ops->unlink_file)(name, ed);
+ out_free:
+ kfree(name);
+ out_put:
+ inode->i_nlink = 0;
+ iput(inode);
+ goto out;
+}
+
+int externfs_rename(struct inode *from_ino, struct dentry *from,
+ struct inode *to_ino, struct dentry *to)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(from_ino)->ops;
+ struct externfs_data *ed = from_ino->i_sb->s_fs_info;
+ char *from_name, *to_name;
+ int err;
+
+ from_name = inode_dentry_name(from_ino, from);
+ if(from_name == NULL)
+ return(-ENOMEM);
+ to_name = inode_dentry_name(to_ino, to);
+ if(to_name == NULL){
+ kfree(from_name);
+ return(-ENOMEM);
+ }
+ err = (*ops->rename_file)(from_name, to_name, ed);
+ kfree(from_name);
+ kfree(to_name);
+
+ from_ino->i_nlink--;
+ to_ino->i_nlink++;
+ return(err);
+}
+
+void externfs_truncate(struct inode *ino)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+
+ (*ops->truncate_file)(EXTERNFS_I(ino), ino->i_size, ed);
+}
+
+int externfs_permission(struct inode *ino, int desired, struct nameidata *nd)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *name;
+ int r = 0, w = 0, x = 0, err;
+
+ 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 = (*ops->access_file)(name, r, w, x, current->fsuid,
+ current->fsgid, ed);
+ kfree(name);
+
+ if(!err)
+ err = vfs_permission(ino, desired);
+ return(err);
+}
+
+int externfs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ struct externfs_file_ops *ops = EXTERNFS_I(dentry->d_inode)->ops;
+ struct externfs_data *ed = dentry->d_inode->i_sb->s_fs_info;
+ struct externfs_iattr attrs;
+ char *name;
+ int err;
+
+ attrs.ia_valid = 0;
+ if(attr->ia_valid & ATTR_MODE){
+ attrs.ia_valid |= EXTERNFS_ATTR_MODE;
+ attrs.ia_mode = attr->ia_mode;
+ }
+ if(attr->ia_valid & ATTR_UID){
+ attrs.ia_valid |= EXTERNFS_ATTR_UID;
+ attrs.ia_uid = attr->ia_uid;
+ }
+ if(attr->ia_valid & ATTR_GID){
+ attrs.ia_valid |= EXTERNFS_ATTR_GID;
+ attrs.ia_gid = attr->ia_gid;
+ }
+ if(attr->ia_valid & ATTR_SIZE){
+ attrs.ia_valid |= EXTERNFS_ATTR_SIZE;
+ attrs.ia_size = attr->ia_size;
+ }
+ if(attr->ia_valid & ATTR_ATIME){
+ attrs.ia_valid |= EXTERNFS_ATTR_ATIME;
+ attrs.ia_atime = attr->ia_atime.tv_sec;
+ }
+ if(attr->ia_valid & ATTR_MTIME){
+ attrs.ia_valid |= EXTERNFS_ATTR_MTIME;
+ attrs.ia_mtime = attr->ia_mtime.tv_sec;
+ }
+ if(attr->ia_valid & ATTR_CTIME){
+ attrs.ia_valid |= EXTERNFS_ATTR_CTIME;
+ attrs.ia_ctime = attr->ia_ctime.tv_sec;
+ }
+ if(attr->ia_valid & ATTR_ATIME_SET){
+ attrs.ia_valid |= EXTERNFS_ATTR_ATIME_SET;
+ attrs.ia_atime = attr->ia_atime.tv_sec;
+ }
+ if(attr->ia_valid & ATTR_MTIME_SET){
+ attrs.ia_valid |= EXTERNFS_ATTR_MTIME_SET;
+ }
+ name = dentry_name(dentry, 0);
+ if(name == NULL)
+ return(-ENOMEM);
+ err = (*ops->set_attr)(name, &attrs, ed);
+ kfree(name);
+ if(err)
+ return(err);
+
+ return(inode_setattr(dentry->d_inode, attr));
+}
+
+int externfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat)
+{
+ generic_fillattr(dentry->d_inode, stat);
+ return(0);
+}
+
+static struct inode_operations externfs_iops = {
+ .create = externfs_create,
+ .link = externfs_link,
+ .unlink = externfs_unlink,
+ .symlink = externfs_symlink,
+ .mkdir = externfs_make_dir,
+ .rmdir = externfs_remove_dir,
+ .mknod = externfs_make_node,
+ .rename = externfs_rename,
+ .truncate = externfs_truncate,
+ .permission = externfs_permission,
+ .setattr = externfs_setattr,
+ .getattr = externfs_getattr,
+};
+
+static struct inode_operations externfs_dir_iops = {
+ .create = externfs_create,
+ .lookup = externfs_lookup,
+ .link = externfs_link,
+ .unlink = externfs_unlink,
+ .symlink = externfs_symlink,
+ .mkdir = externfs_make_dir,
+ .rmdir = externfs_remove_dir,
+ .mknod = externfs_make_node,
+ .rename = externfs_rename,
+ .truncate = externfs_truncate,
+ .permission = externfs_permission,
+ .setattr = externfs_setattr,
+ .getattr = externfs_getattr,
+};
+
+int externfs_link_readpage(struct file *file, struct page *page)
+{
+ struct inode *ino = page->mapping->host;
+ struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops;
+ struct externfs_data *ed = ino->i_sb->s_fs_info;
+ char *buffer, *name;
+ long long start;
+ int err;
+
+ start = page->index << PAGE_CACHE_SHIFT;
+ buffer = kmap(page);
+ name = inode_name(ino, 0);
+ if(name == NULL)
+ return(-ENOMEM);
+
+ err = (*ops->read_link)(name, current->fsuid, current->fsgid, buffer,
+ PAGE_CACHE_SIZE, ed);
+
+ kfree(name);
+ if(err == PAGE_CACHE_SIZE)
+ err = -E2BIG;
+ else if(err > 0){
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ if (PageError(page)) ClearPageError(page);
+ err = 0;
+ }
+ kunmap(page);
+ unlock_page(page);
+ return(err);
+}
+
+static int externfs_flushpage(struct page *page, unsigned long offset)
+{
+ return(externfs_writepage(page, NULL));
+}
+
+struct externfs_data *inode_externfs_info(struct inode *inode)
+{
+ return(inode->i_sb->s_fs_info);
+}
+
+static struct address_space_operations externfs_link_aops = {
+ .readpage = externfs_link_readpage,
+ .releasepage = externfs_removepage,
+ .invalidatepage = externfs_flushpage,
+};
+
+DECLARE_MUTEX(externfs_sem);
+struct list_head externfses = LIST_HEAD_INIT(externfses);
+
+static struct externfs *find_externfs(struct file_system_type *type)
+{
+ struct list_head *ele;
+ struct externfs *fs;
+
+ down(&externfs_sem);
+ list_for_each(ele, &externfses){
+ fs = list_entry(ele, struct externfs, list);
+ if(&fs->type == type)
+ goto out;
+ }
+ fs = NULL;
+ out:
+ up(&externfs_sem);
+ return(fs);
+}
+
+#define DEFAULT_ROOT "/"
+
+char *host_root_filename(char *mount_arg)
+{
+ char *root = DEFAULT_ROOT;
+
+ if((mount_arg != NULL) && (*mount_arg != '\0'))
+ root = mount_arg;
+
+ return(uml_strdup(root));
+}
+
+static int externfs_fill_sb(struct super_block *sb, void *data, int silent)
+{
+ struct externfs *fs;
+ struct inode *root_inode;
+ struct externfs_data *sb_data;
+ int err = -EINVAL;
+
+ sb->s_blocksize = 1024;
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = EXTERNFS_SUPER_MAGIC;
+ sb->s_op = &externfs_sbops;
+
+ fs = find_externfs(sb->s_type);
+ if(fs == NULL){
+ printk("Couldn't find externfs for filesystem '%s'\n",
+ sb->s_type->name);
+ goto out;
+ }
+
+ sb_data = (*fs->mount_ops->mount)(data);
+ if(IS_ERR(sb_data)){
+ err = PTR_ERR(sb_data);
+ goto out;
+ }
+
+ sb->s_fs_info = sb_data;
+ sb_data->mount_ops = fs->mount_ops;
+
+ root_inode = iget(sb, 0);
+ if(root_inode == NULL)
+ goto out;
+
+ err = init_inode(root_inode, NULL);
+ if(err)
+ goto out_put;
+
+ err = -ENOMEM;
+ sb->s_root = d_alloc_root(root_inode);
+ if(sb->s_root == NULL)
+ goto out_put;
+
+ err = read_inode(root_inode);
+ if(err)
+ goto out_put;
+
+ out:
+ return(err);
+
+ out_put:
+ iput(root_inode);
+ goto out;
+}
+
+struct super_block *externfs_read_super(struct file_system_type *type,
+ int flags, const char *dev_name,
+ void *data)
+{
+ return(get_sb_nodev(type, flags, data, externfs_fill_sb));
+}
+
+void init_externfs(struct externfs_data *ed, struct externfs_file_ops *ops)
+{
+ ed->file_ops = ops;
+}
+
+int register_externfs(char *name, struct externfs_mount_ops *mount_ops)
+{
+ struct externfs *new;
+ int err = -ENOMEM;
+
+ new = kmalloc(sizeof(*new), GFP_KERNEL);
+ if(new == NULL)
+ goto out;
+
+ memset(new, 0, sizeof(*new));
+ *new = ((struct externfs) { .list = LIST_HEAD_INIT(new->list),
+ .mount_ops = mount_ops,
+ .type = { .name = name,
+ .get_sb = externfs_read_super,
+ .kill_sb = kill_anon_super,
+ .fs_flags = 0,
+ .owner = THIS_MODULE } });
+ list_add(&new->list, &externfses);
+
+ err = register_filesystem(&new->type);
+ if(err)
+ goto out_del;
+ return(0);
+
+ out_del:
+ list_del(&new->list);
+ kfree(new);
+ out:
+ return(err);
+}
+
+void unregister_externfs(char *name)
+{
+ struct list_head *ele;
+ struct externfs *fs;
+
+ down(&externfs_sem);
+ list_for_each(ele, &externfses){
+ fs = list_entry(ele, struct externfs, list);
+ if(!strcmp(fs->type.name, name)){
+ list_del(ele);
+ up(&externfs_sem);
+ return;
+ }
+ }
+ up(&externfs_sem);
+ printk("Unregister_externfs - filesystem '%s' not found\n", name);
+}
+
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/string.h"
+#include "linux/errno.h"
+#include "linux/types.h"
+#include "linux/slab.h"
+#include "linux/fs.h"
+#include "asm/fcntl.h"
+#include "hostfs.h"
+#include "filehandle.h"
+
+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 = kmalloc(new, GFP_KERNEL);
+ if(buf == NULL)
+ return(NULL);
+ }
+
+ p = buf;
+ for(s = path; *s != NULL; s++){
+ strcpy(p, *s);
+ p += strlen(*s);
+ if((*(s + 1) != NULL) && (strlen(*s) > 0) &&
+ ((*s)[strlen(*s) - 1] != '/'))
+ strcpy(p++, "/");
+ }
+
+ return(buf);
+}
+
+void free_path(const char *buf, char *tmp)
+{
+ if((buf != tmp) && (buf != NULL))
+ kfree((char *) buf);
+}
+
+int host_open_file(const char *path[], int r, int w, struct file_handle *fh)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int mode = 0, err;
+ struct openflags flags = OPENFLAGS();
+
+ if (r)
+ flags = of_read(flags);
+ if (w)
+ flags = of_write(flags);
+ if(append)
+ flags = of_append(flags);
+
+ err = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = open_filehandle(file, flags, mode, fh);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+void *host_open_dir(const char *path[])
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ void *dir = ERR_PTR(-ENOMEM);
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ dir = open_dir(file);
+ out:
+ free_path(file, tmp);
+ return(dir);
+}
+
+char *host_read_dir(void *stream, unsigned long long *pos,
+ unsigned long long *ino_out, int *len_out)
+{
+ int err;
+ char *name;
+
+ err = os_seek_dir(stream, *pos);
+ if(err)
+ return(ERR_PTR(err));
+
+ err = os_read_dir(stream, ino_out, &name);
+ if(err)
+ return(ERR_PTR(err));
+
+ if(name == NULL)
+ return(NULL);
+
+ *len_out = strlen(name);
+ *pos = os_tell_dir(stream);
+ return(name);
+}
+
+int host_file_type(const char *path[], int *rdev)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ struct uml_stat buf;
+ int ret;
+
+ ret = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ if(rdev != NULL){
+ ret = os_lstat_file(file, &buf);
+ if(ret)
+ goto out;
+ *rdev = MKDEV(buf.ust_rmajor, buf.ust_rminor);
+ }
+
+ ret = os_file_type(file);
+ out:
+ free_path(file, tmp);
+ return(ret);
+}
+
+int host_create_file(const char *path[], int mode, struct file_handle *fh)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int err = -ENOMEM;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = open_filehandle(file, of_create(of_rdwr(OPENFLAGS())), mode, fh);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+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 uml_stat buf;
+ int err;
+
+ err = os_lstat_file(path, &buf);
+ if(err < 0)
+ return(err);
+
+ if(dev_out != NULL) *dev_out = MKDEV(buf.ust_major, buf.ust_minor);
+ if(inode_out != NULL) *inode_out = buf.ust_ino;
+ if(mode_out != NULL) *mode_out = buf.ust_mode;
+ if(nlink_out != NULL) *nlink_out = buf.ust_nlink;
+ if(uid_out != NULL) *uid_out = buf.ust_uid;
+ if(gid_out != NULL) *gid_out = buf.ust_gid;
+ if(size_out != NULL) *size_out = buf.ust_size;
+ if(atime_out != NULL) *atime_out = buf.ust_atime;
+ if(mtime_out != NULL) *mtime_out = buf.ust_mtime;
+ if(ctime_out != NULL) *ctime_out = buf.ust_ctime;
+ if(blksize_out != NULL) *blksize_out = buf.ust_blksize;
+ if(blocks_out != NULL) *blocks_out = buf.ust_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 externfs_iattr *attrs)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ unsigned long time;
+ int err = 0, ma;
+
+ if(append && (attrs->ia_valid & EXTERNFS_ATTR_SIZE))
+ return(-EPERM);
+
+ err = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ if(attrs->ia_valid & EXTERNFS_ATTR_MODE){
+ err = os_set_file_perms(file, attrs->ia_mode);
+ if(err < 0)
+ goto out;
+ }
+ if(attrs->ia_valid & EXTERNFS_ATTR_UID){
+ err = os_set_file_owner(file, attrs->ia_uid, -1);
+ if(err < 0)
+ goto out;
+ }
+ if(attrs->ia_valid & EXTERNFS_ATTR_GID){
+ err = os_set_file_owner(file, -1, attrs->ia_gid);
+ if(err < 0)
+ goto out;
+ }
+ if(attrs->ia_valid & EXTERNFS_ATTR_SIZE){
+ err = os_truncate_file(file, attrs->ia_size);
+ if(err < 0)
+ goto out;
+ }
+ ma = EXTERNFS_ATTR_ATIME_SET | EXTERNFS_ATTR_MTIME_SET;
+ if((attrs->ia_valid & ma) == ma){
+ err = os_set_file_time(file, attrs->ia_atime, attrs->ia_mtime);
+ if(err)
+ goto out;
+ }
+ else {
+ if(attrs->ia_valid & EXTERNFS_ATTR_ATIME_SET){
+ err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, &time,
+ NULL, NULL, NULL);
+ if(err != 0)
+ goto out;
+
+ err = os_set_file_time(file, attrs->ia_atime, time);
+ if(err)
+ goto out;
+ }
+ if(attrs->ia_valid & EXTERNFS_ATTR_MTIME_SET){
+ err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, &time, NULL,
+ NULL, NULL, NULL);
+ if(err != 0)
+ goto out;
+
+ err = os_set_file_time(file, time, attrs->ia_mtime);
+ if(err)
+ goto out;
+ }
+ }
+ if(attrs->ia_valid & EXTERNFS_ATTR_CTIME) ;
+ if(attrs->ia_valid & (EXTERNFS_ATTR_ATIME | EXTERNFS_ATTR_MTIME)){
+ err = do_stat_file(file, 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 = os_make_symlink(to, file);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+int host_unlink_file(const char *path[])
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int err = -ENOMEM;
+
+ if(append)
+ return(-EPERM);
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = os_remove_file(file);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+int host_make_dir(const char *path[], int mode)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int err = -ENOMEM;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = os_make_dir(file, mode);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+int host_remove_dir(const char *path[])
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int err = -ENOMEM;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = os_remove_dir(file);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+int host_link_file(const char *to[], const char *from[])
+{
+ char from_tmp[HOSTFS_BUFSIZE], *f, to_tmp[HOSTFS_BUFSIZE], *t;
+ int err = -ENOMEM;
+
+ f = get_path(from, from_tmp, sizeof(from_tmp));
+ t = get_path(to, to_tmp, sizeof(to_tmp));
+ if((f == NULL) || (t == NULL))
+ goto out;
+
+ err = os_link_file(t, f);
+ out:
+ free_path(f, from_tmp);
+ free_path(t, to_tmp);
+ return(err);
+}
+
+int host_read_link(const char *path[], char *buf, int size)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int n = -ENOMEM;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ n = os_read_symlink(file, buf, size);
+ if(n < size)
+ buf[n] = '\0';
+ out:
+ free_path(file, tmp);
+ return(n);
+}
+
+int host_rename_file(const char *from[], const char *to[])
+{
+ char from_tmp[HOSTFS_BUFSIZE], *f, to_tmp[HOSTFS_BUFSIZE], *t;
+ int err = -ENOMEM;
+
+ f = get_path(from, from_tmp, sizeof(from_tmp));
+ t = get_path(to, to_tmp, sizeof(to_tmp));
+ if((f == NULL) || (t == NULL))
+ goto out;
+
+ err = os_move_file(f, t);
+ out:
+ free_path(f, from_tmp);
+ free_path(t, to_tmp);
+ return(err);
+}
+
+int host_stat_fs(const char *path[], long *bsize_out, long long *blocks_out,
+ long long *bfree_out, long long *bavail_out,
+ long long *files_out, long long *ffree_out, void *fsid_out,
+ int fsid_size, long *namelen_out, long *spare_out)
+{
+ char tmp[HOSTFS_BUFSIZE], *file;
+ int err = -ENOMEM;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = os_stat_filesystem(file, bsize_out, blocks_out, bfree_out,
+ bavail_out, files_out, ffree_out, fsid_out,
+ fsid_size, namelen_out, spare_out);
+ out:
+ free_path(file, tmp);
+ return(err);
+}
+
+char *generic_host_read_dir(void *stream, unsigned long long *pos,
+ unsigned long long *ino_out, int *len_out,
+ void *mount)
+{
+ return(host_read_dir(stream, pos, ino_out, len_out));
+}
+
+int generic_host_truncate_file(struct file_handle *fh, __u64 size, void *m)
+{
+ return(truncate_file(fh, size));
+}
+
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/string.h"
+#include "linux/types.h"
+#include "linux/errno.h"
+#include "linux/slab.h"
+#include "linux/init.h"
+#include "linux/fs.h"
+#include "linux/stat.h"
+#include "hostfs.h"
+#include "kern.h"
+#include "init.h"
+#include "kern_util.h"
+#include "filehandle.h"
+#include "os.h"
+
+/* Changed in hostfs_args before the kernel starts running */
+static char *jail_dir = "/";
+int append = 0;
+
+static int __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"
+);
+
+struct hostfs_data {
+ struct externfs_data ext;
+ char *mount;
+};
+
+struct hostfs_file {
+ struct externfs_inode ext;
+ struct file_handle fh;
+};
+
+static int hostfs_access_file(char *file, int uid, int w, int x, int gid,
+ int r, struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+ char tmp[HOSTFS_BUFSIZE];
+ int err, mode = 0;
+
+ if(r) mode = OS_ACC_R_OK;
+ if(w) mode |= OS_ACC_W_OK;
+ if(x) mode |= OS_ACC_X_OK;
+
+ err = -ENOMEM;
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ err = os_access(file, mode);
+ free_path(file, tmp);
+ out:
+ return(err);
+}
+
+static int hostfs_make_node(const char *file, int mode, int uid, int gid,
+ int type, int major, int minor,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+ char tmp[HOSTFS_BUFSIZE];
+ int err = -ENOMEM;
+
+ file = get_path(path, tmp, sizeof(tmp));
+ if(file == NULL)
+ goto out;
+
+ /* XXX Pass type in an OS-independent way */
+ mode |= type;
+
+ err = os_make_dev(file, mode, major, minor);
+ free_path(file, tmp);
+ out:
+ return(err);
+}
+
+static int hostfs_stat_file(const char *file, struct externfs_data *ed,
+ 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)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+
+ /* XXX Why pretend everything is owned by root? */
+ *uid_out = 0;
+ *gid_out = 0;
+ return(host_stat_file(path, dev_out, inode_out, mode_out, nlink_out,
+ NULL, NULL, size_out, atime_out, mtime_out,
+ ctime_out, blksize_out, blocks_out));
+}
+
+static int hostfs_file_type(const char *file, int *rdev,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+
+ return(host_file_type(path, rdev));
+}
+
+static char *hostfs_name(struct inode *inode)
+{
+ struct externfs_data *ed = inode_externfs_info(inode);
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+
+ return(inode_name_prefix(inode, mount));
+}
+
+static struct externfs_inode *hostfs_init_file(struct externfs_data *ed)
+{
+ struct hostfs_file *hf;
+
+ hf = kmalloc(sizeof(*hf), GFP_KERNEL);
+ if(hf == NULL)
+ return(NULL);
+
+ hf->fh.fd = -1;
+ return(&hf->ext);
+}
+
+static int hostfs_open_file(struct externfs_inode *ext, char *file,
+ int uid, int gid, struct inode *inode,
+ struct externfs_data *ed)
+{
+ struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+ int err;
+
+ err = host_open_file(path, 1, 1, &hf->fh);
+ if(err == -EISDIR)
+ goto out;
+
+ if(err == -EACCES)
+ err = host_open_file(path, 1, 0, &hf->fh);
+
+ if(err)
+ goto out;
+
+ is_reclaimable(&hf->fh, hostfs_name, inode);
+ out:
+ return(err);
+}
+
+static void *hostfs_open_dir(char *file, int uid, int gid,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+
+ return(host_open_dir(path));
+}
+
+static void hostfs_close_dir(void *stream, struct externfs_data *ed)
+{
+ os_close_dir(stream);
+}
+
+static char *hostfs_read_dir(void *stream, unsigned long long *pos,
+ unsigned long long *ino_out, int *len_out,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+
+ return(generic_host_read_dir(stream, pos, ino_out, len_out, mount));
+}
+
+static int hostfs_read_file(struct externfs_inode *ext,
+ unsigned long long offset, char *buf, int len,
+ int ignore_start, int ignore_end,
+ void (*completion)(char *, int, void *), void *arg,
+ struct externfs_data *ed)
+{
+ struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+ int err = 0;
+
+ if(ignore_start != 0){
+ err = read_file(&hf->fh, offset, buf, ignore_start);
+ if(err < 0)
+ goto out;
+ }
+
+ if(ignore_end != len)
+ err = read_file(&hf->fh, offset + ignore_end, buf + ignore_end,
+ len - ignore_end);
+
+ out:
+
+ (*completion)(buf, err, arg);
+ if (err > 0)
+ err = 0;
+ return(err);
+}
+
+static int hostfs_write_file(struct externfs_inode *ext,
+ unsigned long long offset, const char *buf,
+ int start, int len,
+ void (*completion)(char *, int, void *),
+ void *arg, struct externfs_data *ed)
+{
+ struct file_handle *fh;
+ int err;
+
+ fh = &container_of(ext, struct hostfs_file, ext)->fh;
+ err = write_file(fh, offset + start, buf + start, len);
+
+ (*completion)((char *) buf, err, arg);
+ if (err > 0)
+ err = 0;
+
+ return(err);
+}
+
+static int hostfs_create_file(struct externfs_inode *ext, char *file, int mode,
+ int uid, int gid, struct inode *inode,
+ struct externfs_data *ed)
+{
+ struct hostfs_file *hf = container_of(ext, struct hostfs_file,
+ ext);
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+ int err = -ENOMEM;
+
+ err = host_create_file(path, mode, &hf->fh);
+ if(err)
+ goto out;
+
+ is_reclaimable(&hf->fh, hostfs_name, inode);
+ out:
+ return(err);
+}
+
+static int hostfs_set_attr(const char *file, struct externfs_iattr *attrs,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+
+ return(host_set_attr(path, attrs));
+}
+
+static int hostfs_make_symlink(const char *from, const char *to, int uid,
+ int gid, struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, from, NULL };
+
+ return(host_make_symlink(path, to));
+}
+
+static int hostfs_link_file(const char *to, const char *from, int uid, int gid,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *to_path[] = { jail_dir, mount, to, NULL };
+ const char *from_path[] = { jail_dir, mount, from, NULL };
+
+ return(host_link_file(to_path, from_path));
+}
+
+static int hostfs_unlink_file(const char *file, struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+
+ return(host_unlink_file(path));
+}
+
+static int hostfs_make_dir(const char *file, int mode, int uid, int gid,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+
+ return(host_make_dir(path, mode));
+}
+
+static int hostfs_remove_dir(const char *file, int uid, int gid,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+
+ return(host_remove_dir(path));
+}
+
+static int hostfs_read_link(char *file, int uid, int gid, char *buf, int size,
+ struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, file, NULL };
+
+ return(host_read_link(path, buf, size));
+}
+
+static int hostfs_rename_file(char *from, char *to, struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *to_path[] = { jail_dir, mount, to, NULL };
+ const char *from_path[] = { jail_dir, mount, from, NULL };
+
+ return(host_rename_file(from_path, to_path));
+}
+
+static int hostfs_stat_fs(long *bsize_out, long long *blocks_out,
+ long long *bfree_out, long long *bavail_out,
+ long long *files_out, long long *ffree_out,
+ void *fsid_out, int fsid_size, long *namelen_out,
+ long *spare_out, struct externfs_data *ed)
+{
+ char *mount = container_of(ed, struct hostfs_data, ext)->mount;
+ const char *path[] = { jail_dir, mount, NULL };
+
+ return(host_stat_fs(path, bsize_out, blocks_out, bfree_out, bavail_out,
+ files_out, ffree_out, fsid_out, fsid_size,
+ namelen_out, spare_out));
+}
+
+void hostfs_close_file(struct externfs_inode *ext,
+ unsigned long long size)
+{
+ struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+
+ if(hf->fh.fd != -1){
+ truncate_file(&hf->fh, size);
+ close_file(&hf->fh);
+ }
+
+ kfree(hf);
+}
+
+int hostfs_truncate_file(struct externfs_inode *ext, __u64 size,
+ struct externfs_data *ed)
+{
+ struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext);
+
+ return(truncate_file(&hf->fh, size));
+}
+
+static struct externfs_file_ops hostfs_file_ops = {
+ .stat_file = hostfs_stat_file,
+ .file_type = hostfs_file_type,
+ .access_file = hostfs_access_file,
+ .open_file = hostfs_open_file,
+ .open_dir = hostfs_open_dir,
+ .read_dir = hostfs_read_dir,
+ .read_file = hostfs_read_file,
+ .write_file = hostfs_write_file,
+ .map_file_page = NULL,
+ .close_file = hostfs_close_file,
+ .close_dir = hostfs_close_dir,
+ .invisible = NULL,
+ .create_file = hostfs_create_file,
+ .set_attr = hostfs_set_attr,
+ .make_symlink = hostfs_make_symlink,
+ .unlink_file = hostfs_unlink_file,
+ .make_dir = hostfs_make_dir,
+ .remove_dir = hostfs_remove_dir,
+ .make_node = hostfs_make_node,
+ .link_file = hostfs_link_file,
+ .read_link = hostfs_read_link,
+ .rename_file = hostfs_rename_file,
+ .statfs = hostfs_stat_fs,
+ .truncate_file = hostfs_truncate_file
+};
+
+static struct externfs_data *mount_fs(char *mount_arg)
+{
+ struct hostfs_data *hd;
+ int err = -ENOMEM;
+
+ hd = kmalloc(sizeof(*hd), GFP_KERNEL);
+ if(hd == NULL)
+ goto out;
+
+ hd->mount = host_root_filename(mount_arg);
+ if(hd->mount == NULL)
+ goto out_free;
+
+ init_externfs(&hd->ext, &hostfs_file_ops);
+
+ return(&hd->ext);
+
+ out_free:
+ kfree(hd);
+ out:
+ return(ERR_PTR(err));
+}
+
+static struct externfs_mount_ops hostfs_mount_ops = {
+ .init_file = hostfs_init_file,
+ .mount = mount_fs,
+};
+
+static int __init init_hostfs(void)
+{
+ return(register_externfs("hostfs", &hostfs_mount_ops));
+}
+
+static void __exit exit_hostfs(void)
+{
+ unregister_externfs("hostfs");
+}
+
+__initcall(init_hostfs);
+__exitcall(exit_hostfs);
+
+#if 0
+module_init(init_hostfs)
+module_exit(exit_hostfs)
+MODULE_LICENSE("GPL");
+#endif
+
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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/slab.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/kdev_t.h>
+#include "linux/init.h"
+#include "linux/workqueue.h"
+#include <asm/irq.h>
+#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:
+ */
--- /dev/null
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#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:
+ */
--- /dev/null
+/*
+ * 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:
+ */