Initial revision
authorMark Huang <mlhuang@cs.princeton.edu>
Fri, 20 Aug 2004 18:50:31 +0000 (18:50 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Fri, 20 Aug 2004 18:50:31 +0000 (18:50 +0000)
arch/um/include/aio.h [new file with mode: 0644]
arch/um/include/filehandle.h [new file with mode: 0644]
arch/um/kernel/filehandle.c [new file with mode: 0644]
arch/um/os-Linux/aio.c [new file with mode: 0644]
arch/um/sys-i386/semaphore.c [new file with mode: 0644]
fs/hostfs/externfs.c [new file with mode: 0644]
fs/hostfs/host_file.c [new file with mode: 0644]
fs/hostfs/host_fs.c [new file with mode: 0644]
fs/hostfs/humfs.c [new file with mode: 0644]
fs/hostfs/meta_fs.c [new file with mode: 0644]
fs/hostfs/metadata.h [new file with mode: 0644]

diff --git a/arch/um/include/aio.h b/arch/um/include/aio.h
new file mode 100644 (file)
index 0000000..6096f4f
--- /dev/null
@@ -0,0 +1,36 @@
+/* 
+ * 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:
+ */
diff --git a/arch/um/include/filehandle.h b/arch/um/include/filehandle.h
new file mode 100644 (file)
index 0000000..adc5108
--- /dev/null
@@ -0,0 +1,51 @@
+/* 
+ * 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:
+ */
diff --git a/arch/um/kernel/filehandle.c b/arch/um/kernel/filehandle.c
new file mode 100644 (file)
index 0000000..a44dccf
--- /dev/null
@@ -0,0 +1,250 @@
+/* 
+ * 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:
+ */
diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c
new file mode 100644 (file)
index 0000000..56b3782
--- /dev/null
@@ -0,0 +1,404 @@
+/* 
+ * 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:
+ */
diff --git a/arch/um/sys-i386/semaphore.c b/arch/um/sys-i386/semaphore.c
new file mode 100644 (file)
index 0000000..073912c
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * 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
diff --git a/fs/hostfs/externfs.c b/fs/hostfs/externfs.c
new file mode 100644 (file)
index 0000000..884c33c
--- /dev/null
@@ -0,0 +1,1317 @@
+/* 
+ * 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:
+ */
diff --git a/fs/hostfs/host_file.c b/fs/hostfs/host_file.c
new file mode 100644 (file)
index 0000000..e8eb901
--- /dev/null
@@ -0,0 +1,442 @@
+/* 
+ * 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:
+ */
diff --git a/fs/hostfs/host_fs.c b/fs/hostfs/host_fs.c
new file mode 100644 (file)
index 0000000..c059539
--- /dev/null
@@ -0,0 +1,467 @@
+/* 
+ * 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:
+ */
diff --git a/fs/hostfs/humfs.c b/fs/hostfs/humfs.c
new file mode 100644 (file)
index 0000000..7878be5
--- /dev/null
@@ -0,0 +1,1026 @@
+/* 
+ * 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:
+ */
diff --git a/fs/hostfs/meta_fs.c b/fs/hostfs/meta_fs.c
new file mode 100644 (file)
index 0000000..7464149
--- /dev/null
@@ -0,0 +1,520 @@
+/* 
+ * 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:
+ */
diff --git a/fs/hostfs/metadata.h b/fs/hostfs/metadata.h
new file mode 100644 (file)
index 0000000..924fb5b
--- /dev/null
@@ -0,0 +1,79 @@
+/* 
+ * Copyright (C) 2004 Piotr Neuman (sikkh@wp.pl) and 
+ * Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_FS_METADATA
+#define __UM_FS_METADATA
+
+#include "linux/fs.h"
+#include "linux/list.h"
+#include "os.h"
+#include "hostfs.h"
+
+struct humfs {
+       struct externfs_data ext;
+       __u64 used;
+       __u64 total;
+       char *data;
+       int mmap;
+       int direct;
+       struct humfs_meta_ops *meta;
+};
+
+struct humfs_file {
+       struct humfs *mount;
+       struct file_handle data;
+       struct externfs_inode ext;
+};
+
+struct humfs_meta_ops {
+       struct list_head list;
+       char *name;
+       struct humfs_file *(*init_file)(void);
+       int (*open_file)(struct humfs_file *hf, const char *path, 
+                        struct inode *inode, struct humfs *humfs);
+       int (*create_file)(struct humfs_file *hf, const char *path, int mode, 
+                          int uid, int gid, struct inode *inode, 
+                          struct humfs *humfs);
+       void (*close_file)(struct humfs_file *humfs);
+       int (*ownerships)(const char *path, int *mode_out, int *uid_out, 
+                         int *gid_out, char *type_out, int *maj_out, 
+                         int *min_out, struct humfs *humfs);
+       int (*make_node)(const char *path, int mode, int uid, int gid,
+                        int type, int major, int minor, struct humfs *humfs);
+       int (*create_link)(const char *to, const char *from, 
+                          struct humfs *humfs);
+       int (*remove_file)(const char *path, struct humfs *humfs);
+       int (*create_dir)(const char *path, int mode, int uid, int gid, 
+                         struct humfs *humfs);
+       int (*remove_dir)(const char *path, struct humfs *humfs);
+       int (*change_ownerships)(const char *path, int mode, int uid, int gid,
+                                struct humfs *humfs);
+       int (*rename_file)(const char *from, const char *to, 
+                          struct humfs *humfs);
+       void (*invisible)(struct humfs_file *hf);
+       struct humfs *(*init_mount)(char *root);
+       void (*free_mount)(struct humfs *humfs);
+};
+
+extern void register_meta(struct humfs_meta_ops *ops);
+extern void unregister_meta(struct humfs_meta_ops *ops);
+
+extern char *humfs_path(char *dir, char *file);
+extern char *humfs_name(struct inode *inode, char *prefix);
+extern struct humfs *inode_humfs_info(struct inode *inode);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */