Initial revision
authorMark Huang <mlhuang@cs.princeton.edu>
Mon, 7 Jun 2004 19:53:02 +0000 (19:53 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Mon, 7 Jun 2004 19:53:02 +0000 (19:53 +0000)
26 files changed:
arch/um/drivers/cow.h [new file with mode: 0644]
arch/um/drivers/cow_kern.c [new file with mode: 0644]
arch/um/drivers/cow_user.c [new file with mode: 0644]
arch/um/drivers/net_kern.c.orig [new file with mode: 0644]
arch/um/include/irq_kern.h [new file with mode: 0644]
arch/um/include/mem_kern.h [new file with mode: 0644]
arch/um/kernel/physmem.c [new file with mode: 0644]
arch/um/kernel/skas/uaccess.c [new file with mode: 0644]
arch/um/kernel/smp.c.orig [new file with mode: 0644]
arch/um/kernel/tt/uaccess.c [new file with mode: 0644]
arch/um/os-Linux/user_syms.c [new file with mode: 0644]
arch/um/sys-i386/time.c [new file with mode: 0644]
fs/Makefile.orig [new file with mode: 0644]
fs/hostfs/Makefile [new file with mode: 0644]
fs/hostfs/hostfs.h [new file with mode: 0644]
fs/hostfs/hostfs_kern.c [new file with mode: 0644]
fs/hostfs/hostfs_user.c [new file with mode: 0644]
fs/hppfs/Makefile [new file with mode: 0644]
fs/hppfs/hppfs_kern.c [new file with mode: 0644]
include/asm-um/cpufeature.h [new file with mode: 0644]
include/asm-um/local.h [new file with mode: 0644]
include/asm-um/module-generic.h [new file with mode: 0644]
include/asm-um/sections.h [new file with mode: 0644]
include/linux/ghash.h [new file with mode: 0644]
include/linux/proc_mm.h [new file with mode: 0644]
mm/proc_mm.c [new file with mode: 0644]

diff --git a/arch/um/drivers/cow.h b/arch/um/drivers/cow.h
new file mode 100644 (file)
index 0000000..d875d04
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __COW_H__
+#define __COW_H__
+
+#include <asm/types.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+# define ntohll(x) (x)
+# define htonll(x) (x)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+# define ntohll(x)  bswap_64(x)
+# define htonll(x)  bswap_64(x)
+#else
+#error "__BYTE_ORDER not defined"
+#endif
+
+extern int init_cow_file(int fd, char *cow_file, char *backing_file, 
+                        int sectorsize, int alignment, int *bitmap_offset_out, 
+                        unsigned long *bitmap_len_out, int *data_offset_out);
+
+extern int file_reader(__u64 offset, char *buf, int len, void *arg);
+extern int read_cow_header(int (*reader)(__u64, char *, int, void *), 
+                          void *arg, __u32 *version_out, 
+                          char **backing_file_out, time_t *mtime_out, 
+                          __u64 *size_out, int *sectorsize_out, 
+                          __u32 *align_out, int *bitmap_offset_out);
+
+extern int write_cow_header(char *cow_file, int fd, char *backing_file, 
+                           int sectorsize, int alignment, long long *size);
+
+extern void cow_sizes(int version, __u64 size, int sectorsize, int align,
+                     int bitmap_offset, unsigned long *bitmap_len_out, 
+                     int *data_offset_out);
+
+#endif
+
+/*
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/drivers/cow_kern.c b/arch/um/drivers/cow_kern.c
new file mode 100644 (file)
index 0000000..ad843f3
--- /dev/null
@@ -0,0 +1,630 @@
+#define COW_MAJOR 60
+#define MAJOR_NR COW_MAJOR
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/stat.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+#include <linux/blk.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/devfs_fs.h>
+#include <asm/uaccess.h>
+#include "2_5compat.h"
+#include "cow.h"
+#include "ubd_user.h"
+
+#define COW_SHIFT 4
+
+struct cow {
+       int count;
+       char *cow_path;
+       dev_t cow_dev;
+       struct block_device *cow_bdev;
+       char *backing_path;
+       dev_t backing_dev;
+       struct block_device *backing_bdev;
+       int sectorsize;
+       unsigned long *bitmap;
+       unsigned long bitmap_len;
+       int bitmap_offset;
+       int data_offset;
+       devfs_handle_t devfs;
+       struct semaphore sem;
+       struct semaphore io_sem;
+       atomic_t working;
+       spinlock_t io_lock;
+       struct buffer_head *bh;
+       struct buffer_head *bhtail;
+       void *end_io;
+};
+
+#define DEFAULT_COW { \
+       .count                  = 0, \
+       .cow_path               = NULL, \
+       .cow_dev                = 0, \
+       .backing_path           = NULL, \
+       .backing_dev            = 0, \
+        .bitmap                        = NULL, \
+       .bitmap_len             = 0, \
+       .bitmap_offset          = 0, \
+        .data_offset           = 0, \
+       .devfs                  = NULL, \
+       .working                = ATOMIC_INIT(0), \
+       .io_lock                = SPIN_LOCK_UNLOCKED, \
+}
+
+#define MAX_DEV (8)
+#define MAX_MINOR (MAX_DEV << COW_SHIFT)
+
+struct cow cow_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_COW };
+
+/* Not modified by this driver */
+static int blk_sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = BLOCK_SIZE };
+static int hardsect_sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = 512 };
+
+/* Protected by cow_lock */
+static int sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = 0 };
+
+static struct hd_struct        cow_part[MAX_MINOR] =
+       { [ 0 ... MAX_MINOR - 1 ] = { 0, 0, 0 } };
+
+/* Protected by io_request_lock */
+static request_queue_t *cow_queue;
+
+static int cow_open(struct inode *inode, struct file *filp);
+static int cow_release(struct inode * inode, struct file * file);
+static int cow_ioctl(struct inode * inode, struct file * file,
+                    unsigned int cmd, unsigned long arg);
+static int cow_revalidate(kdev_t rdev);
+
+static struct block_device_operations cow_blops = {
+       .open           = cow_open,
+       .release        = cow_release,
+       .ioctl          = cow_ioctl,
+       .revalidate     = cow_revalidate,
+};
+
+/* Initialized in an initcall, and unchanged thereafter */
+devfs_handle_t cow_dir_handle;
+
+#define INIT_GENDISK(maj, name, parts, shift, bsizes, max, blops) \
+{ \
+       .major          = maj, \
+       .major_name     = name, \
+       .minor_shift    = shift, \
+       .max_p          = 1 << shift, \
+       .part           = parts, \
+       .sizes          = bsizes, \
+       .nr_real        = max, \
+       .real_devices   = NULL, \
+       .next           = NULL, \
+       .fops           = blops, \
+       .de_arr         = NULL, \
+       .flags          = 0 \
+}
+
+static spinlock_t cow_lock = SPIN_LOCK_UNLOCKED;
+
+static struct gendisk cow_gendisk = INIT_GENDISK(MAJOR_NR, "cow", cow_part,
+                                                COW_SHIFT, sizes, MAX_DEV, 
+                                                &cow_blops);
+
+static int cow_add(int n)
+{
+       struct cow *dev = &cow_dev[n];
+       char name[sizeof("nnnnnn\0")];
+       int err = -ENODEV;
+
+       if(dev->cow_path == NULL)
+               goto out;
+
+       sprintf(name, "%d", n);
+       dev->devfs = devfs_register(cow_dir_handle, name, DEVFS_FL_REMOVABLE,
+                                   MAJOR_NR, n << COW_SHIFT, S_IFBLK | 
+                                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+                                   &cow_blops, NULL);
+
+       init_MUTEX_LOCKED(&dev->sem);
+       init_MUTEX(&dev->io_sem);
+
+       return(0);
+
+ out:
+       return(err);
+}
+
+/*
+ * Add buffer_head to back of pending list
+ */
+static void cow_add_bh(struct cow *cow, struct buffer_head *bh)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&cow->io_lock, flags);
+       if(cow->bhtail != NULL){
+               cow->bhtail->b_reqnext = bh;
+               cow->bhtail = bh;
+       }
+       else {
+               cow->bh = bh;
+               cow->bhtail = bh;
+       }
+       spin_unlock_irqrestore(&cow->io_lock, flags);
+}
+
+/*
+* Grab first pending buffer
+*/
+static struct buffer_head *cow_get_bh(struct cow *cow)
+{
+       struct buffer_head *bh;
+
+       spin_lock_irq(&cow->io_lock);
+       bh = cow->bh;
+       if(bh != NULL){
+               if(bh == cow->bhtail)
+                       cow->bhtail = NULL;
+               cow->bh = bh->b_reqnext;
+               bh->b_reqnext = NULL;
+       }
+       spin_unlock_irq(&cow->io_lock);
+
+       return(bh);
+}
+
+static void cow_handle_bh(struct cow *cow, struct buffer_head *bh, 
+                         struct buffer_head **cow_bh, int ncow_bh)
+{
+       int i;
+
+       if(ncow_bh > 0)
+               ll_rw_block(WRITE, ncow_bh, cow_bh);
+
+       for(i = 0; i < ncow_bh ; i++){
+               wait_on_buffer(cow_bh[i]);
+               brelse(cow_bh[i]);
+       }
+
+       ll_rw_block(WRITE, 1, &bh);
+       brelse(bh);
+}
+
+static struct buffer_head *cow_new_bh(struct cow *dev, int sector)
+{
+       struct buffer_head *bh;
+
+       sector = (dev->bitmap_offset + sector / 8) / dev->sectorsize;
+       bh = getblk(dev->cow_dev, sector, dev->sectorsize);
+       memcpy(bh->b_data, dev->bitmap + sector / (8 * sizeof(dev->bitmap[0])),
+              dev->sectorsize);
+       return(bh);
+}
+
+/* Copied from loop.c, needed to avoid deadlocking in make_request. */
+
+static int cow_thread(void *data)
+{
+       struct cow *dev = data;
+       struct buffer_head *bh;
+
+       daemonize();
+       exit_files(current);
+
+       sprintf(current->comm, "cow%d", dev - cow_dev);
+
+       spin_lock_irq(&current->sigmask_lock);
+       sigfillset(&current->blocked);
+       flush_signals(current);
+       spin_unlock_irq(&current->sigmask_lock);
+
+       atomic_inc(&dev->working);
+
+       current->policy = SCHED_OTHER;
+       current->nice = -20;
+
+       current->flags |= PF_NOIO;
+
+       /*
+        * up sem, we are running
+        */
+       up(&dev->sem);
+
+       for(;;){
+               int start, len, nbh, i, update_bitmap = 0;
+               struct buffer_head *cow_bh[2];
+
+               down_interruptible(&dev->io_sem);
+               /*
+                * could be upped because of tear-down, not because of
+                * pending work
+                */
+               if(!atomic_read(&dev->working))
+                       break;
+
+               bh = cow_get_bh(dev);
+               if(bh == NULL){
+                       printk(KERN_ERR "cow: missing bh\n");
+                       continue;
+               }
+
+               start = bh->b_blocknr * bh->b_size / dev->sectorsize;
+               len = bh->b_size / dev->sectorsize;
+               for(i = 0; i < len ; i++){
+                       if(ubd_test_bit(start + i, 
+                                       (unsigned char *) dev->bitmap))
+                               continue;
+
+                       update_bitmap = 1;
+                       ubd_set_bit(start + i, (unsigned char *) dev->bitmap);
+               }
+
+               cow_bh[0] = NULL;
+               cow_bh[1] = NULL;
+               nbh = 0;
+               if(update_bitmap){
+                       cow_bh[0] = cow_new_bh(dev, start);
+                       nbh++;
+                       if(start / dev->sectorsize != 
+                          (start + len) / dev->sectorsize){
+                               cow_bh[1] = cow_new_bh(dev, start + len);
+                               nbh++;
+                       }
+               }
+               
+               bh->b_dev = dev->cow_dev;
+               bh->b_blocknr += dev->data_offset / dev->sectorsize;
+
+               cow_handle_bh(dev, bh, cow_bh, nbh);
+
+               /*
+                * upped both for pending work and tear-down, lo_pending
+                * will hit zero then
+                */
+               if(atomic_dec_and_test(&dev->working))
+                       break;
+       }
+
+       up(&dev->sem);
+       return(0);
+}
+
+static int cow_make_request(request_queue_t *q, int rw, struct buffer_head *bh)
+{
+       struct cow *dev;
+       int n, minor;
+
+       minor = MINOR(bh->b_rdev);
+       n = minor >> COW_SHIFT;
+       dev = &cow_dev[n];
+
+       dev->end_io = NULL;
+       if(ubd_test_bit(bh->b_rsector, (unsigned char *) dev->bitmap)){
+               bh->b_rdev = dev->cow_dev;
+               bh->b_rsector += dev->data_offset / dev->sectorsize;
+       }
+       else if(rw == WRITE){
+               bh->b_dev = dev->cow_dev;
+               bh->b_blocknr += dev->data_offset / dev->sectorsize;
+
+               cow_add_bh(dev, bh);
+               up(&dev->io_sem);
+               return(0);
+       }
+       else {
+               bh->b_rdev = dev->backing_dev;
+       }
+
+       return(1);
+}
+
+int cow_init(void)
+{
+       int i;
+
+       cow_dir_handle = devfs_mk_dir (NULL, "cow", NULL);
+       if (devfs_register_blkdev(MAJOR_NR, "cow", &cow_blops)) {
+               printk(KERN_ERR "cow: unable to get major %d\n", MAJOR_NR);
+               return -1;
+       }
+       read_ahead[MAJOR_NR] = 8;               /* 8 sector (4kB) read-ahead */
+       blksize_size[MAJOR_NR] = blk_sizes;
+       blk_size[MAJOR_NR] = sizes;
+       INIT_HARDSECT(hardsect_size, MAJOR_NR, hardsect_sizes);
+
+       cow_queue = BLK_DEFAULT_QUEUE(MAJOR_NR);
+       blk_init_queue(cow_queue, NULL);
+       INIT_ELV(cow_queue, &cow_queue->elevator);
+       blk_queue_make_request(cow_queue, cow_make_request);
+
+       add_gendisk(&cow_gendisk);
+
+       for(i=0;i<MAX_DEV;i++) 
+               cow_add(i);
+
+       return(0);
+}
+
+__initcall(cow_init);
+
+static int reader(__u64 start, char *buf, int count, void *arg)
+{
+       dev_t dev = *((dev_t *) arg);
+       struct buffer_head *bh;
+       __u64 block;
+       int cur, offset, left, n, blocksize = get_hardsect_size(dev);
+
+       if(blocksize == 0)
+               panic("Zero blocksize");
+
+       block = start / blocksize;
+       offset = start % blocksize;
+       left = count;
+       cur = 0;
+       while(left > 0){
+               n = (left > blocksize) ? blocksize : left;
+
+               bh = bread(dev, block, (n < 512) ? 512 : n);
+               if(bh == NULL)
+                       return(-EIO);
+
+               n -= offset;
+               memcpy(&buf[cur], bh->b_data + offset, n);
+               block++;
+               left -= n;
+               cur += n;
+               offset = 0;
+               brelse(bh);
+       }
+
+       return(count);
+}
+
+static int cow_open(struct inode *inode, struct file *filp)
+{
+       int (*dev_ioctl)(struct inode *, struct file *, unsigned int, 
+                        unsigned long);
+       mm_segment_t fs;
+       struct cow *dev;
+       __u64 size;
+       __u32 version, align;
+       time_t mtime;
+       char *backing_file;
+       int n, offset, err = 0;
+
+       n = DEVICE_NR(inode->i_rdev);
+       if(n >= MAX_DEV)
+               return(-ENODEV);
+       dev = &cow_dev[n];
+       offset = n << COW_SHIFT;
+
+       spin_lock(&cow_lock);
+
+       if(dev->count == 0){
+               dev->cow_dev = name_to_kdev_t(dev->cow_path);
+               if(dev->cow_dev == 0){
+                       printk(KERN_ERR "cow_open - name_to_kdev_t(\"%s\") "
+                              "failed\n", dev->cow_path);
+                       err = -ENODEV;
+               }
+
+               dev->backing_dev = name_to_kdev_t(dev->backing_path);
+               if(dev->backing_dev == 0){
+                       printk(KERN_ERR "cow_open - name_to_kdev_t(\"%s\") "
+                              "failed\n", dev->backing_path);
+                       err = -ENODEV;
+               }
+
+               if(err) 
+                       goto out;
+
+               dev->cow_bdev = bdget(dev->cow_dev);
+               if(dev->cow_bdev == NULL){
+                       printk(KERN_ERR "cow_open - bdget(\"%s\") failed\n", 
+                              dev->cow_path);
+                       err = -ENOMEM;
+               }
+               dev->backing_bdev = bdget(dev->backing_dev);
+               if(dev->backing_bdev == NULL){
+                       printk(KERN_ERR "cow_open - bdget(\"%s\") failed\n", 
+                              dev->backing_path);
+                       err = -ENOMEM;
+               }
+
+               if(err) 
+                       goto out;
+
+               err = blkdev_get(dev->cow_bdev, FMODE_READ|FMODE_WRITE, 0, 
+                                BDEV_RAW);
+               if(err){
+                       printk("cow_open - blkdev_get of COW device failed, "
+                              "error = %d\n", err);
+                       goto out;
+               }
+               
+               err = blkdev_get(dev->backing_bdev, FMODE_READ, 0, BDEV_RAW);
+               if(err){
+                       printk("cow_open - blkdev_get of backing device "
+                              "failed, error = %d\n", err);
+                       goto out;
+               }
+               
+               err = read_cow_header(reader, &dev->cow_dev, &version, 
+                                     &backing_file, &mtime, &size,
+                                     &dev->sectorsize, &align, 
+                                     &dev->bitmap_offset);
+               if(err){
+                       printk(KERN_ERR "cow_open - read_cow_header failed, "
+                              "err = %d\n", err);
+                       goto out;
+               }
+
+               cow_sizes(version, size, dev->sectorsize, align, 
+                         dev->bitmap_offset, &dev->bitmap_len, 
+                         &dev->data_offset);
+               dev->bitmap = (void *) vmalloc(dev->bitmap_len);
+               if(dev->bitmap == NULL){
+                       err = -ENOMEM;
+                       printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
+                       goto out;
+               }
+               flush_tlb_kernel_vm();
+               
+               err = reader(dev->bitmap_offset, (char *) dev->bitmap, 
+                            dev->bitmap_len, &dev->cow_dev);
+               if(err < 0){
+                       printk(KERN_ERR "Failed to read COW bitmap\n");
+                       vfree(dev->bitmap);
+                       goto out;
+               }
+
+               dev_ioctl = dev->backing_bdev->bd_op->ioctl;
+               fs = get_fs();
+               set_fs(KERNEL_DS);
+               err = (*dev_ioctl)(inode, filp, BLKGETSIZE, 
+                                  (unsigned long) &sizes[offset]);
+               set_fs(fs);
+               if(err){
+                       printk(KERN_ERR "cow_open - BLKGETSIZE failed, "
+                              "error = %d\n", err);
+                       goto out;
+               }
+
+               kernel_thread(cow_thread, dev, 
+                             CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+               down(&dev->sem);
+       }
+       dev->count++;
+ out:
+       spin_unlock(&cow_lock);
+       return(err);
+}
+
+static int cow_release(struct inode * inode, struct file * file)
+{
+       struct cow *dev;
+       int n, err;
+
+       n = DEVICE_NR(inode->i_rdev);
+       if(n >= MAX_DEV)
+               return(-ENODEV);
+       dev = &cow_dev[n];
+
+       spin_lock(&cow_lock);
+
+       if(--dev->count > 0)
+               goto out;
+
+       err = blkdev_put(dev->cow_bdev, BDEV_RAW);
+       if(err)
+               printk("cow_release - blkdev_put of cow device failed, "
+                      "error = %d\n", err);
+       bdput(dev->cow_bdev);
+       dev->cow_bdev = 0;
+
+       err = blkdev_put(dev->backing_bdev, BDEV_RAW);
+       if(err)
+               printk("cow_release - blkdev_put of backing device failed, "
+                      "error = %d\n", err);
+       bdput(dev->backing_bdev);
+       dev->backing_bdev = 0;
+
+ out:
+       spin_unlock(&cow_lock);
+       return(0);
+}
+
+static int cow_ioctl(struct inode * inode, struct file * file,
+                    unsigned int cmd, unsigned long arg)
+{
+       struct cow *dev;
+       int (*dev_ioctl)(struct inode *, struct file *, unsigned int, 
+                        unsigned long);
+       int n;
+
+       n = DEVICE_NR(inode->i_rdev);
+       if(n >= MAX_DEV)
+               return(-ENODEV);
+       dev = &cow_dev[n];
+
+       dev_ioctl = dev->backing_bdev->bd_op->ioctl;
+       return((*dev_ioctl)(inode, file, cmd, arg));
+}
+
+static int cow_revalidate(kdev_t rdev)
+{
+       printk(KERN_ERR "Need to implement cow_revalidate\n");
+       return(0);
+}
+
+static int parse_unit(char **ptr)
+{
+       char *str = *ptr, *end;
+       int n = -1;
+
+       if(isdigit(*str)) {
+               n = simple_strtoul(str, &end, 0);
+               if(end == str)
+                       return(-1);
+               *ptr = end;
+       }
+       else if (('a' <= *str) && (*str <= 'h')) {
+               n = *str - 'a';
+               str++;
+               *ptr = str;
+       }
+       return(n);
+}
+
+static int cow_setup(char *str)
+{
+       struct cow *dev;
+       char *cow_name, *backing_name;
+       int unit;
+
+       unit = parse_unit(&str);
+       if(unit < 0){
+               printk(KERN_ERR "cow_setup - Couldn't parse unit number\n");
+               return(1);
+       }
+
+       if(*str != '='){
+               printk(KERN_ERR "cow_setup - Missing '=' after unit "
+                      "number\n");
+               return(1);
+       }
+       str++;
+
+       cow_name = str;
+       backing_name = strchr(str, ',');
+       if(backing_name == NULL){
+               printk(KERN_ERR "cow_setup - missing backing device name\n");
+               return(0);
+       }
+       *backing_name = '\0';
+       backing_name++;
+
+       spin_lock(&cow_lock);
+
+       dev = &cow_dev[unit];
+       dev->cow_path = cow_name;
+       dev->backing_path = backing_name;
+       
+       spin_unlock(&cow_lock);
+       return(0);
+}
+
+__setup("cow", cow_setup);
+
+/*
+ * 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/drivers/cow_user.c b/arch/um/drivers/cow_user.c
new file mode 100644 (file)
index 0000000..014c2c8
--- /dev/null
@@ -0,0 +1,375 @@
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/user.h>
+#include <netinet/in.h>
+
+#include "os.h"
+
+#include "cow.h"
+#include "cow_sys.h"
+
+#define PATH_LEN_V1 256
+
+struct cow_header_v1 {
+       int magic;
+       int version;
+       char backing_file[PATH_LEN_V1];
+       time_t mtime;
+       __u64 size;
+       int sectorsize;
+};
+
+#define PATH_LEN_V2 MAXPATHLEN
+
+struct cow_header_v2 {
+       unsigned long magic;
+       unsigned long version;
+       char backing_file[PATH_LEN_V2];
+       time_t mtime;
+       __u64 size;
+       int sectorsize;
+};
+
+/* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in 
+ * case other systems have different values for MAXPATHLEN
+ */
+#define PATH_LEN_V3 4096
+
+/* Changes from V2 - 
+ *     PATH_LEN_V3 as described above
+ *     Explicitly specify field bit lengths for systems with different
+ *             lengths for the usual C types.  Not sure whether char or
+ *             time_t should be changed, this can be changed later without
+ *             breaking compatibility
+ *     Add alignment field so that different alignments can be used for the
+ *             bitmap and data
+ *     Add cow_format field to allow for the possibility of different ways
+ *             of specifying the COW blocks.  For now, the only value is 0,
+ *             for the traditional COW bitmap.
+ *     Move the backing_file field to the end of the header.  This allows
+ *             for the possibility of expanding it into the padding required
+ *             by the bitmap alignment.
+ *     The bitmap and data portions of the file will be aligned as specified
+ *             by the alignment field.  This is to allow COW files to be
+ *             put on devices with restrictions on access alignments, such as
+ *             /dev/raw, with a 512 byte alignment restriction.  This also
+ *             allows the data to be more aligned more strictly than on
+ *             sector boundaries.  This is needed for ubd-mmap, which needs
+ *             the data to be page aligned.
+ *     Fixed (finally!) the rounding bug
+ */
+
+struct cow_header_v3 {
+       __u32 magic;
+       __u32 version;
+       time_t mtime;
+       __u64 size;
+       __u32 sectorsize;
+       __u32 alignment;
+       __u32 cow_format;
+       char backing_file[PATH_LEN_V3];
+};
+
+/* COW format definitions - for now, we have only the usual COW bitmap */
+#define COW_BITMAP 0
+
+union cow_header {
+       struct cow_header_v1 v1;
+       struct cow_header_v2 v2;
+       struct cow_header_v3 v3;
+};
+
+#define COW_MAGIC 0x4f4f4f4d  /* MOOO */
+#define COW_VERSION 3
+
+#define DIV_ROUND(x, len) (((x) + (len) - 1) / (len))
+#define ROUND_UP(x, align) DIV_ROUND(x, align) * (align)
+
+void cow_sizes(int version, __u64 size, int sectorsize, int align, 
+              int bitmap_offset, unsigned long *bitmap_len_out, 
+              int *data_offset_out)
+{
+       if(version < 3){
+               *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize);
+
+               *data_offset_out = bitmap_offset + *bitmap_len_out;
+               *data_offset_out = (*data_offset_out + sectorsize - 1) / 
+                       sectorsize;
+               *data_offset_out *= sectorsize;
+       }
+       else {
+               *bitmap_len_out = DIV_ROUND(size, sectorsize);
+               *bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8);
+
+               *data_offset_out = bitmap_offset + *bitmap_len_out;
+               *data_offset_out = ROUND_UP(*data_offset_out, align);
+       }
+}
+
+static int absolutize(char *to, int size, char *from)
+{
+       char save_cwd[256], *slash;
+       int remaining;
+
+       if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) {
+               cow_printf("absolutize : unable to get cwd - errno = %d\n", 
+                          errno);
+               return(-1);
+       }
+       slash = strrchr(from, '/');
+       if(slash != NULL){
+               *slash = '\0';
+               if(chdir(from)){
+                       *slash = '/';
+                       cow_printf("absolutize : Can't cd to '%s' - " 
+                                  "errno = %d\n", from, errno);
+                       return(-1);
+               }
+               *slash = '/';
+               if(getcwd(to, size) == NULL){
+                       cow_printf("absolutize : unable to get cwd of '%s' - "
+                              "errno = %d\n", from, errno);
+                       return(-1);
+               }
+               remaining = size - strlen(to);
+               if(strlen(slash) + 1 > remaining){
+                       cow_printf("absolutize : unable to fit '%s' into %d "
+                              "chars\n", from, size);
+                       return(-1);
+               }
+               strcat(to, slash);
+       }
+       else {
+               if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){
+                       cow_printf("absolutize : unable to fit '%s' into %d "
+                              "chars\n", from, size);
+                       return(-1);
+               }
+               strcpy(to, save_cwd);
+               strcat(to, "/");
+               strcat(to, from);
+       }
+       chdir(save_cwd);
+       return(0);
+}
+
+int write_cow_header(char *cow_file, int fd, char *backing_file, 
+                    int sectorsize, int alignment, long long *size)
+{
+       struct cow_header_v3 *header;
+       unsigned long modtime;
+       int err;
+
+       err = cow_seek_file(fd, 0);
+       if(err < 0){
+               cow_printf("write_cow_header - lseek failed, err = %d\n", -err);
+               goto out;
+       }
+
+       err = -ENOMEM;
+       header = cow_malloc(sizeof(*header));
+       if(header == NULL){
+               cow_printf("Failed to allocate COW V3 header\n");
+               goto out;
+       }
+       header->magic = htonl(COW_MAGIC);
+       header->version = htonl(COW_VERSION);
+
+       err = -EINVAL;
+       if(strlen(backing_file) > sizeof(header->backing_file) - 1){
+               cow_printf("Backing file name \"%s\" is too long - names are "
+                          "limited to %d characters\n", backing_file, 
+                          sizeof(header->backing_file) - 1);
+               goto out_free;
+       }
+
+       if(absolutize(header->backing_file, sizeof(header->backing_file), 
+                     backing_file))
+               goto out_free;
+
+       err = os_file_modtime(header->backing_file, &modtime);
+       if(err < 0){
+               cow_printf("Backing file '%s' mtime request failed, "
+                          "err = %d\n", header->backing_file, -err);
+               goto out_free;
+       }
+
+       err = cow_file_size(header->backing_file, size);
+       if(err < 0){
+               cow_printf("Couldn't get size of backing file '%s', "
+                          "err = %d\n", header->backing_file, -err);
+               goto out_free;
+       }
+
+       header->mtime = htonl(modtime);
+       header->size = htonll(*size);
+       header->sectorsize = htonl(sectorsize);
+       header->alignment = htonl(alignment);
+       header->cow_format = COW_BITMAP;
+
+       err = os_write_file(fd, header, sizeof(*header));
+       if(err != sizeof(*header)){
+               cow_printf("Write of header to new COW file '%s' failed, "
+                          "err = %d\n", cow_file, -err);
+               goto out_free;
+       }
+       err = 0;
+ out_free:
+       cow_free(header);
+ out:
+       return(err);
+}
+
+int file_reader(__u64 offset, char *buf, int len, void *arg)
+{
+       int fd = *((int *) arg);
+
+       return(pread(fd, buf, len, offset));
+}
+
+/* XXX Need to sanity-check the values read from the header */
+
+int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, 
+                   __u32 *version_out, char **backing_file_out, 
+                   time_t *mtime_out, __u64 *size_out, 
+                   int *sectorsize_out, __u32 *align_out, 
+                   int *bitmap_offset_out)
+{
+       union cow_header *header;
+       char *file;
+       int err, n;
+       unsigned long version, magic;
+
+       header = cow_malloc(sizeof(*header));
+       if(header == NULL){
+               cow_printf("read_cow_header - Failed to allocate header\n");
+               return(-ENOMEM);
+       }
+       err = -EINVAL;
+       n = (*reader)(0, (char *) header, sizeof(*header), arg);
+       if(n < offsetof(typeof(header->v1), backing_file)){
+               cow_printf("read_cow_header - short header\n");
+               goto out;
+       }
+
+       magic = header->v1.magic;
+       if(magic == COW_MAGIC) {
+               version = header->v1.version;
+       }
+       else if(magic == ntohl(COW_MAGIC)){
+               version = ntohl(header->v1.version);
+       }
+       /* No error printed because the non-COW case comes through here */
+       else goto out;
+
+       *version_out = version;
+
+       if(version == 1){
+               if(n < sizeof(header->v1)){
+                       cow_printf("read_cow_header - failed to read V1 "
+                                  "header\n");
+                       goto out;
+               }
+               *mtime_out = header->v1.mtime;
+               *size_out = header->v1.size;
+               *sectorsize_out = header->v1.sectorsize;
+               *bitmap_offset_out = sizeof(header->v1);
+               *align_out = *sectorsize_out;
+               file = header->v1.backing_file;
+       }
+       else if(version == 2){
+               if(n < sizeof(header->v2)){
+                       cow_printf("read_cow_header - failed to read V2 "
+                                  "header\n");
+                       goto out;
+               }
+               *mtime_out = ntohl(header->v2.mtime);
+               *size_out = ntohll(header->v2.size);
+               *sectorsize_out = ntohl(header->v2.sectorsize);
+               *bitmap_offset_out = sizeof(header->v2);
+               *align_out = *sectorsize_out;
+               file = header->v2.backing_file;
+       }
+       else if(version == 3){
+               if(n < sizeof(header->v3)){
+                       cow_printf("read_cow_header - failed to read V2 "
+                                  "header\n");
+                       goto out;
+               }
+               *mtime_out = ntohl(header->v3.mtime);
+               *size_out = ntohll(header->v3.size);
+               *sectorsize_out = ntohl(header->v3.sectorsize);
+               *align_out = ntohl(header->v3.alignment);
+               *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out);
+               file = header->v3.backing_file;
+       }
+       else {
+               cow_printf("read_cow_header - invalid COW version\n");
+               goto out;               
+       }
+       err = -ENOMEM;
+       *backing_file_out = cow_strdup(file);
+       if(*backing_file_out == NULL){
+               cow_printf("read_cow_header - failed to allocate backing "
+                          "file\n");
+               goto out;
+       }
+       err = 0;
+ out:
+       cow_free(header);
+       return(err);
+}
+
+int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize,
+                 int alignment, int *bitmap_offset_out, 
+                 unsigned long *bitmap_len_out, int *data_offset_out)
+{
+       __u64 size, offset;
+       char zero = 0;
+       int err;
+
+       err = write_cow_header(cow_file, fd, backing_file, sectorsize, 
+                              alignment, &size);
+       if(err) 
+               goto out;
+       
+       *bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment);
+       cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out,
+                 bitmap_len_out, data_offset_out);
+
+       offset = *data_offset_out + size - sizeof(zero);
+       err = cow_seek_file(fd, offset);
+       if(err < 0){
+               cow_printf("cow bitmap lseek failed : err = %d\n", -err);
+               goto out;
+       }
+
+       /* does not really matter how much we write it is just to set EOF 
+        * this also sets the entire COW bitmap
+        * to zero without having to allocate it 
+        */
+       err = cow_write_file(fd, &zero, sizeof(zero));
+       if(err != sizeof(zero)){
+               cow_printf("Write of bitmap to new COW file '%s' failed, "
+                          "err = %d\n", cow_file, -err);
+               err = -EINVAL;
+               goto out;
+       }
+
+       return(0);
+
+ out:
+       return(err);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/drivers/net_kern.c.orig b/arch/um/drivers/net_kern.c.orig
new file mode 100644 (file)
index 0000000..4c2ee09
--- /dev/null
@@ -0,0 +1,870 @@
+/*
+ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
+ * James Leu (jleu@mindspring.net).
+ * Copyright (C) 2001 by various other people who didn't put their name here.
+ * Licensed under the GPL.
+ */
+
+#include "linux/config.h"
+#include "linux/kernel.h"
+#include "linux/netdevice.h"
+#include "linux/rtnetlink.h"
+#include "linux/skbuff.h"
+#include "linux/socket.h"
+#include "linux/spinlock.h"
+#include "linux/module.h"
+#include "linux/init.h"
+#include "linux/etherdevice.h"
+#include "linux/list.h"
+#include "linux/inetdevice.h"
+#include "linux/ctype.h"
+#include "linux/bootmem.h"
+#include "user_util.h"
+#include "kern_util.h"
+#include "net_kern.h"
+#include "net_user.h"
+#include "mconsole_kern.h"
+#include "init.h"
+#include "irq_user.h"
+
+static spinlock_t opened_lock = SPIN_LOCK_UNLOCKED;
+LIST_HEAD(opened);
+
+static int uml_net_rx(struct net_device *dev)
+{
+       struct uml_net_private *lp = dev->priv;
+       int pkt_len;
+       struct sk_buff *skb;
+
+       /* If we can't allocate memory, try again next round. */
+       if ((skb = dev_alloc_skb(dev->mtu)) == NULL) {
+               lp->stats.rx_dropped++;
+               return 0;
+       }
+
+       skb->dev = dev;
+       skb_put(skb, dev->mtu);
+       skb->mac.raw = skb->data;
+       pkt_len = (*lp->read)(lp->fd, &skb, lp);
+
+       if (pkt_len > 0) {
+               skb_trim(skb, pkt_len);
+               skb->protocol = (*lp->protocol)(skb);
+               netif_rx(skb);
+
+               lp->stats.rx_bytes += skb->len;
+               lp->stats.rx_packets++;
+               return pkt_len;
+       }
+
+       kfree_skb(skb);
+       return pkt_len;
+}
+
+void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = dev_id;
+       struct uml_net_private *lp = dev->priv;
+       int err;
+
+       if(!netif_running(dev))
+               return;
+
+       spin_lock(&lp->lock);
+       while((err = uml_net_rx(dev)) > 0) ;
+       if(err < 0) {
+               printk(KERN_ERR 
+                      "Device '%s' read returned %d, shutting it down\n", 
+                      dev->name, err);
+               dev_close(dev);
+               goto out;
+       }
+       reactivate_fd(lp->fd, UM_ETH_IRQ);
+
+ out:
+       spin_unlock(&lp->lock);
+}
+
+static int uml_net_open(struct net_device *dev)
+{
+       struct uml_net_private *lp = dev->priv;
+       char addr[sizeof("255.255.255.255\0")];
+       int err;
+
+       spin_lock(&lp->lock);
+
+       if(lp->fd >= 0){
+               err = -ENXIO;
+               goto out;
+       }
+
+       if(!lp->have_mac){
+               dev_ip_addr(dev, addr, &lp->mac[2]);
+               set_ether_mac(dev, lp->mac);
+       }
+
+       lp->fd = (*lp->open)(&lp->user);
+       if(lp->fd < 0){
+               err = lp->fd;
+               goto out;
+       }
+
+       err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
+                            SA_INTERRUPT | SA_SHIRQ, dev->name, dev);
+       if(err != 0){
+               printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
+               if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+               lp->fd = -1;
+               err = -ENETUNREACH;
+       }
+
+       lp->tl.data = (unsigned long) &lp->user;
+       netif_start_queue(dev);
+
+       spin_lock(&opened_lock);
+       list_add(&lp->list, &opened);
+       spin_unlock(&opened_lock);
+       MOD_INC_USE_COUNT;
+ out:
+       spin_unlock(&lp->lock);
+       return(err);
+}
+
+static int uml_net_close(struct net_device *dev)
+{
+       struct uml_net_private *lp = dev->priv;
+       
+       netif_stop_queue(dev);
+       spin_lock(&lp->lock);
+
+       free_irq(dev->irq, dev);
+       if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+       lp->fd = -1;
+       spin_lock(&opened_lock);
+       list_del(&lp->list);
+       spin_unlock(&opened_lock);
+
+       MOD_DEC_USE_COUNT;
+       spin_unlock(&lp->lock);
+       return 0;
+}
+
+static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct uml_net_private *lp = dev->priv;
+       unsigned long flags;
+       int len;
+
+       netif_stop_queue(dev);
+
+       spin_lock_irqsave(&lp->lock, flags);
+
+       len = (*lp->write)(lp->fd, &skb, lp);
+
+       if(len == skb->len) {
+               lp->stats.tx_packets++;
+               lp->stats.tx_bytes += skb->len;
+               dev->trans_start = jiffies;
+               netif_start_queue(dev);
+
+               /* this is normally done in the interrupt when tx finishes */
+               netif_wake_queue(dev);
+       } 
+       else if(len == 0){
+               netif_start_queue(dev);
+               lp->stats.tx_dropped++;
+       }
+       else {
+               netif_start_queue(dev);
+               printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len);
+       }
+
+       spin_unlock_irqrestore(&lp->lock, flags);
+
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+static struct net_device_stats *uml_net_get_stats(struct net_device *dev)
+{
+       struct uml_net_private *lp = dev->priv;
+       return &lp->stats;
+}
+
+static void uml_net_set_multicast_list(struct net_device *dev)
+{
+       if (dev->flags & IFF_PROMISC) return;
+       else if (dev->mc_count) dev->flags |= IFF_ALLMULTI;
+       else dev->flags &= ~IFF_ALLMULTI;
+}
+
+static void uml_net_tx_timeout(struct net_device *dev)
+{
+       dev->trans_start = jiffies;
+       netif_wake_queue(dev);
+}
+
+static int uml_net_set_mac(struct net_device *dev, void *addr)
+{
+       struct uml_net_private *lp = dev->priv;
+       struct sockaddr *hwaddr = addr;
+
+       spin_lock(&lp->lock);
+       memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
+       spin_unlock(&lp->lock);
+
+       return(0);
+}
+
+static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct uml_net_private *lp = dev->priv;
+       int err = 0;
+
+       spin_lock(&lp->lock);
+
+       new_mtu = (*lp->set_mtu)(new_mtu, &lp->user);
+       if(new_mtu < 0){
+               err = new_mtu;
+               goto out;
+       }
+
+       dev->mtu = new_mtu;
+
+ out:
+       spin_unlock(&lp->lock);
+       return err;
+}
+
+static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       return(-EINVAL);
+}
+
+void uml_net_user_timer_expire(unsigned long _conn)
+{
+#ifdef undef
+       struct connection *conn = (struct connection *)_conn;
+
+       dprintk(KERN_INFO "uml_net_user_timer_expire [%p]\n", conn);
+       do_connect(conn);
+#endif
+}
+
+/*
+ * default do nothing hard header packet routines for struct net_device init.
+ * real ethernet transports will overwrite with real routines.
+ */
+static int uml_net_hard_header(struct sk_buff *skb, struct net_device *dev,
+                 unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+       return(0); /* no change */
+}
+
+static int uml_net_rebuild_header(struct sk_buff *skb)
+{
+       return(0); /* ignore */ 
+}
+
+static int uml_net_header_cache(struct neighbour *neigh, struct hh_cache *hh)
+{
+       return(-1); /* fail */
+}
+
+static void uml_net_header_cache_update(struct hh_cache *hh,
+                 struct net_device *dev, unsigned char * haddr)
+{
+       /* ignore */
+}
+
+static int uml_net_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+       return(0); /* nothing */
+}
+
+static spinlock_t devices_lock = SPIN_LOCK_UNLOCKED;
+static struct list_head devices = LIST_HEAD_INIT(devices);
+
+static int eth_configure(int n, void *init, char *mac,
+                        struct transport *transport)
+{
+       struct uml_net *device;
+       struct net_device *dev;
+       struct uml_net_private *lp;
+       int err, size;
+
+       size = transport->private_size + sizeof(struct uml_net_private) + 
+               sizeof(((struct uml_net_private *) 0)->user);
+
+       device = kmalloc(sizeof(*device), GFP_KERNEL);
+       if (device == NULL) {
+               printk(KERN_ERR "eth_configure failed to allocate uml_net\n");
+               return(1);
+       }
+
+       memset(device, 0, sizeof(*device));
+       INIT_LIST_HEAD(&device->list);
+       device->index = n;
+
+       spin_lock(&devices_lock);
+       list_add(&device->list, &devices);
+       spin_unlock(&devices_lock);
+
+       if (setup_etheraddr(mac, device->mac))
+               device->have_mac = 1;
+
+       printk(KERN_INFO "Netdevice %d ", n);
+       if (device->have_mac)
+               printk("(%02x:%02x:%02x:%02x:%02x:%02x) ",
+                      device->mac[0], device->mac[1],
+                      device->mac[2], device->mac[3],
+                      device->mac[4], device->mac[5]);
+       printk(": ");
+       dev = alloc_etherdev(size);
+       if (dev == NULL) {
+               printk(KERN_ERR "eth_configure: failed to allocate device\n");
+               return 1;
+       }
+
+       /* If this name ends up conflicting with an existing registered
+        * netdevice, that is OK, register_netdev{,ice}() will notice this
+        * and fail.
+        */
+       snprintf(dev->name, sizeof(dev->name), "eth%d", n);
+       device->dev = dev;
+
+        dev->hard_header = uml_net_hard_header;
+        dev->rebuild_header = uml_net_rebuild_header;
+        dev->hard_header_cache = uml_net_header_cache;
+        dev->header_cache_update= uml_net_header_cache_update;
+        dev->hard_header_parse = uml_net_header_parse;
+
+       (*transport->kern->init)(dev, init);
+
+       dev->mtu = transport->user->max_packet;
+       dev->open = uml_net_open;
+       dev->hard_start_xmit = uml_net_start_xmit;
+       dev->stop = uml_net_close;
+       dev->get_stats = uml_net_get_stats;
+       dev->set_multicast_list = uml_net_set_multicast_list;
+       dev->tx_timeout = uml_net_tx_timeout;
+       dev->set_mac_address = uml_net_set_mac;
+       dev->change_mtu = uml_net_change_mtu;
+       dev->do_ioctl = uml_net_ioctl;
+       dev->watchdog_timeo = (HZ >> 1);
+       dev->irq = UM_ETH_IRQ;
+
+       rtnl_lock();
+       err = register_netdevice(dev);
+       rtnl_unlock();
+       if (err) {
+               device->dev = NULL;
+               /* XXX: should we call ->remove() here? */
+               free_netdev(dev);
+               return 1;
+       }
+       lp = dev->priv;
+
+       INIT_LIST_HEAD(&lp->list);
+       spin_lock_init(&lp->lock);
+       lp->dev = dev;
+       lp->fd = -1;
+       lp->mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 };
+       lp->have_mac = device->have_mac;
+       lp->protocol = transport->kern->protocol;
+       lp->open = transport->user->open;
+       lp->close = transport->user->close;
+       lp->remove = transport->user->remove;
+       lp->read = transport->kern->read;
+       lp->write = transport->kern->write;
+       lp->add_address = transport->user->add_address;
+       lp->delete_address = transport->user->delete_address;
+       lp->set_mtu = transport->user->set_mtu;
+
+       init_timer(&lp->tl);
+       lp->tl.function = uml_net_user_timer_expire;
+       if (lp->have_mac)
+               memcpy(lp->mac, device->mac, sizeof(lp->mac));
+
+       if (transport->user->init) 
+               (*transport->user->init)(&lp->user, dev);
+
+       if (device->have_mac)
+               set_ether_mac(dev, device->mac);
+       return(0);
+}
+
+static struct uml_net *find_device(int n)
+{
+       struct uml_net *device;
+       struct list_head *ele;
+
+       spin_lock(&devices_lock);
+       list_for_each(ele, &devices){
+               device = list_entry(ele, struct uml_net, list);
+               if(device->index == n)
+                       goto out;
+       }
+       device = NULL;
+ out:
+       spin_unlock(&devices_lock);
+       return(device);
+}
+
+static int eth_parse(char *str, int *index_out, char **str_out)
+{
+       char *end;
+       int n;
+
+       n = simple_strtoul(str, &end, 0);
+       if(end == str){
+               printk(KERN_ERR "eth_setup: Failed to parse '%s'\n", str);
+               return(1);
+       }
+       if(n < 0){
+               printk(KERN_ERR "eth_setup: device %d is negative\n", n);
+               return(1);
+       }
+       str = end;
+       if(*str != '='){
+               printk(KERN_ERR 
+                      "eth_setup: expected '=' after device number\n");
+               return(1);
+       }
+       str++;
+       if(find_device(n)){
+               printk(KERN_ERR "eth_setup: Device %d already configured\n",
+                      n);
+               return(1);
+       }
+       if(index_out) *index_out = n;
+       *str_out = str;
+       return(0);
+}
+
+struct eth_init {
+       struct list_head list;
+       char *init;
+       int index;
+};
+
+/* Filled in at boot time.  Will need locking if the transports become
+ * modular.
+ */
+struct list_head transports = LIST_HEAD_INIT(transports);
+
+/* Filled in during early boot */
+struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line);
+
+static int check_transport(struct transport *transport, char *eth, int n,
+                          void **init_out, char **mac_out)
+{
+       int len;
+
+       len = strlen(transport->name);
+       if(strncmp(eth, transport->name, len))
+               return(0);
+
+       eth += len;
+       if(*eth == ',')
+               eth++;
+       else if(*eth != '\0')
+               return(0);
+
+       *init_out = kmalloc(transport->setup_size, GFP_KERNEL);
+       if(*init_out == NULL)
+               return(1);
+
+       if(!transport->setup(eth, mac_out, *init_out)){
+               kfree(*init_out);
+               *init_out = NULL;
+       }
+       return(1);
+}
+
+void register_transport(struct transport *new)
+{
+       struct list_head *ele, *next;
+       struct eth_init *eth;
+       void *init;
+       char *mac = NULL;
+       int match;
+
+       list_add(&new->list, &transports);
+
+       list_for_each_safe(ele, next, &eth_cmd_line){
+               eth = list_entry(ele, struct eth_init, list);
+               match = check_transport(new, eth->init, eth->index, &init,
+                                       &mac);
+               if(!match)
+                       continue;
+               else if(init != NULL){
+                       eth_configure(eth->index, init, mac, new);
+                       kfree(init);
+               }
+               list_del(&eth->list);
+       }
+}
+
+static int eth_setup_common(char *str, int index)
+{
+       struct list_head *ele;
+       struct transport *transport;
+       void *init;
+       char *mac = NULL;
+
+       list_for_each(ele, &transports){
+               transport = list_entry(ele, struct transport, list);
+               if(!check_transport(transport, str, index, &init, &mac))
+                       continue;
+               if(init != NULL){
+                       eth_configure(index, init, mac, transport);
+                       kfree(init);
+               }
+               return(1);
+       }
+       return(0);
+}
+
+static int eth_setup(char *str)
+{
+       struct eth_init *new;
+       int n, err;
+
+       err = eth_parse(str, &n, &str);
+       if(err) return(1);
+
+       new = alloc_bootmem(sizeof(new));
+       if (new == NULL){
+               printk("eth_init : alloc_bootmem failed\n");
+               return(1);
+       }
+
+       INIT_LIST_HEAD(&new->list);
+       new->index = n;
+       new->init = str;
+
+       list_add_tail(&new->list, &eth_cmd_line);
+       return(1);
+}
+
+__setup("eth", eth_setup);
+__uml_help(eth_setup,
+"eth[0-9]+=<transport>,<options>\n"
+"    Configure a network device.\n\n"
+);
+
+static int eth_init(void)
+{
+       struct list_head *ele, *next;
+       struct eth_init *eth;
+
+       list_for_each_safe(ele, next, &eth_cmd_line){
+               eth = list_entry(ele, struct eth_init, list);
+
+               if(eth_setup_common(eth->init, eth->index))
+                       list_del(&eth->list);
+       }
+       
+       return(1);
+}
+
+__initcall(eth_init);
+
+static int net_config(char *str)
+{
+       int n, err;
+
+       err = eth_parse(str, &n, &str);
+       if(err) return(err);
+
+       str = uml_strdup(str);
+       if(str == NULL){
+               printk(KERN_ERR "net_config failed to strdup string\n");
+               return(-1);
+       }
+       err = !eth_setup_common(str, n);
+       if(err) 
+               kfree(str);
+       return(err);
+}
+
+static int net_remove(char *str)
+{
+       struct uml_net *device;
+       struct net_device *dev;
+       struct uml_net_private *lp;
+       char *end;
+       int n;
+
+       n = simple_strtoul(str, &end, 0);
+       if((*end != '\0') || (end == str))
+               return(-1);
+
+       device = find_device(n);
+       if(device == NULL)
+               return(0);
+
+       dev = device->dev;
+       lp = dev->priv;
+       if(lp->fd > 0) return(-1);
+       if(lp->remove != NULL) (*lp->remove)(&lp->user);
+       unregister_netdev(dev);
+
+       list_del(&device->list);
+       free_netdev(device);
+       return(0);
+}
+
+static struct mc_device net_mc = {
+       .name           = "eth",
+       .config         = net_config,
+       .get_config     = NULL,
+       .remove         = net_remove,
+};
+
+static int uml_inetaddr_event(struct notifier_block *this, unsigned long event,
+                             void *ptr)
+{
+       struct in_ifaddr *ifa = ptr;
+       u32 addr = ifa->ifa_address;
+       u32 netmask = ifa->ifa_mask;
+       struct net_device *dev = ifa->ifa_dev->dev;
+       struct uml_net_private *lp;
+       void (*proc)(unsigned char *, unsigned char *, void *);
+       unsigned char addr_buf[4], netmask_buf[4];
+
+       if(dev->open != uml_net_open) return(NOTIFY_DONE);
+
+       lp = dev->priv;
+
+       proc = NULL;
+       switch (event){
+       case NETDEV_UP:
+               proc = lp->add_address;
+               break;
+       case NETDEV_DOWN:
+               proc = lp->delete_address;
+               break;
+       }
+       if(proc != NULL){
+               addr_buf[0] = addr & 0xff;
+               addr_buf[1] = (addr >> 8) & 0xff;
+               addr_buf[2] = (addr >> 16) & 0xff;
+               addr_buf[3] = addr >> 24;
+               netmask_buf[0] = netmask & 0xff;
+               netmask_buf[1] = (netmask >> 8) & 0xff;
+               netmask_buf[2] = (netmask >> 16) & 0xff;
+               netmask_buf[3] = netmask >> 24;
+               (*proc)(addr_buf, netmask_buf, &lp->user);
+       }
+       return(NOTIFY_DONE);
+}
+
+struct notifier_block uml_inetaddr_notifier = {
+       .notifier_call          = uml_inetaddr_event,
+};
+
+static int uml_net_init(void)
+{
+       struct list_head *ele;
+       struct uml_net_private *lp;     
+       struct in_device *ip;
+       struct in_ifaddr *in;
+
+       mconsole_register_dev(&net_mc);
+       register_inetaddr_notifier(&uml_inetaddr_notifier);
+
+       /* Devices may have been opened already, so the uml_inetaddr_notifier
+        * didn't get a chance to run for them.  This fakes it so that
+        * addresses which have already been set up get handled properly.
+        */
+       list_for_each(ele, &opened){
+               lp = list_entry(ele, struct uml_net_private, list);
+               ip = lp->dev->ip_ptr;
+               if(ip == NULL) continue;
+               in = ip->ifa_list;
+               while(in != NULL){
+                       uml_inetaddr_event(NULL, NETDEV_UP, in);
+                       in = in->ifa_next;
+               }
+       }       
+
+       return(0);
+}
+
+__initcall(uml_net_init);
+
+static void close_devices(void)
+{
+       struct list_head *ele;
+       struct uml_net_private *lp;     
+
+       list_for_each(ele, &opened){
+               lp = list_entry(ele, struct uml_net_private, list);
+               if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+               if(lp->remove != NULL) (*lp->remove)(&lp->user);
+       }
+}
+
+__uml_exitcall(close_devices);
+
+int setup_etheraddr(char *str, unsigned char *addr)
+{
+       char *end;
+       int i;
+
+       if(str == NULL)
+               return(0);
+       for(i=0;i<6;i++){
+               addr[i] = simple_strtoul(str, &end, 16);
+               if((end == str) ||
+                  ((*end != ':') && (*end != ',') && (*end != '\0'))){
+                       printk(KERN_ERR 
+                              "setup_etheraddr: failed to parse '%s' "
+                              "as an ethernet address\n", str);
+                       return(0);
+               }
+               str = end + 1;
+       }
+       if(addr[0] & 1){
+               printk(KERN_ERR 
+                      "Attempt to assign a broadcast ethernet address to a "
+                      "device disallowed\n");
+               return(0);
+       }
+       return(1);
+}
+
+void dev_ip_addr(void *d, char *buf, char *bin_buf)
+{
+       struct net_device *dev = d;
+       struct in_device *ip = dev->ip_ptr;
+       struct in_ifaddr *in;
+       u32 addr;
+
+       if((ip == NULL) || ((in = ip->ifa_list) == NULL)){
+               printk(KERN_WARNING "dev_ip_addr - device not assigned an "
+                      "IP address\n");
+               return;
+       }
+       addr = in->ifa_address;
+       sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, 
+               (addr >> 16) & 0xff, addr >> 24);
+       if(bin_buf){
+               bin_buf[0] = addr & 0xff;
+               bin_buf[1] = (addr >> 8) & 0xff;
+               bin_buf[2] = (addr >> 16) & 0xff;
+               bin_buf[3] = addr >> 24;
+       }
+}
+
+void set_ether_mac(void *d, unsigned char *addr)
+{
+       struct net_device *dev = d;
+
+       memcpy(dev->dev_addr, addr, ETH_ALEN);  
+}
+
+struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)
+{
+       if((skb != NULL) && (skb_tailroom(skb) < extra)){
+               struct sk_buff *skb2;
+
+               skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC);
+               dev_kfree_skb(skb);
+               skb = skb2;
+       }
+       if(skb != NULL) skb_put(skb, extra);
+       return(skb);
+}
+
+void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *, 
+                                       void *), 
+                   void *arg)
+{
+       struct net_device *dev = d;
+       struct in_device *ip = dev->ip_ptr;
+       struct in_ifaddr *in;
+       unsigned char address[4], netmask[4];
+
+       if(ip == NULL) return;
+       in = ip->ifa_list;
+       while(in != NULL){
+               address[0] = in->ifa_address & 0xff;
+               address[1] = (in->ifa_address >> 8) & 0xff;
+               address[2] = (in->ifa_address >> 16) & 0xff;
+               address[3] = in->ifa_address >> 24;
+               netmask[0] = in->ifa_mask & 0xff;
+               netmask[1] = (in->ifa_mask >> 8) & 0xff;
+               netmask[2] = (in->ifa_mask >> 16) & 0xff;
+               netmask[3] = in->ifa_mask >> 24;
+               (*cb)(address, netmask, arg);
+               in = in->ifa_next;
+       }
+}
+
+int dev_netmask(void *d, void *m)
+{
+       struct net_device *dev = d;
+       struct in_device *ip = dev->ip_ptr;
+       struct in_ifaddr *in;
+       __u32 *mask_out = m;
+
+       if(ip == NULL) 
+               return(1);
+
+       in = ip->ifa_list;
+       if(in == NULL) 
+               return(1);
+
+       *mask_out = in->ifa_mask;
+       return(0);
+}
+
+void *get_output_buffer(int *len_out)
+{
+       void *ret;
+
+       ret = (void *) __get_free_pages(GFP_KERNEL, 0);
+       if(ret) *len_out = PAGE_SIZE;
+       else *len_out = 0;
+       return(ret);
+}
+
+void free_output_buffer(void *buffer)
+{
+       free_pages((unsigned long) buffer, 0);
+}
+
+int tap_setup_common(char *str, char *type, char **dev_name, char **mac_out, 
+                    char **gate_addr)
+{
+       char *remain;
+
+       remain = split_if_spec(str, dev_name, mac_out, gate_addr, NULL);
+       if(remain != NULL){
+               printk("tap_setup_common - Extra garbage on specification : "
+                      "'%s'\n", remain);
+               return(1);
+       }
+
+       return(0);
+}
+
+unsigned short eth_protocol(struct sk_buff *skb)
+{
+       return(eth_type_trans(skb, skb->dev));
+}
+
+/*
+ * 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/irq_kern.h b/arch/um/include/irq_kern.h
new file mode 100644 (file)
index 0000000..4bcb829
--- /dev/null
@@ -0,0 +1,28 @@
+/* 
+ * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __IRQ_KERN_H__
+#define __IRQ_KERN_H__
+
+#include "linux/interrupt.h"
+
+extern int um_request_irq(unsigned int irq, int fd, int type,
+                         irqreturn_t (*handler)(int, void *, 
+                                                struct pt_regs *),
+                         unsigned long irqflags,  const char * devname,
+                         void *dev_id);
+
+#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/mem_kern.h b/arch/um/include/mem_kern.h
new file mode 100644 (file)
index 0000000..b39f03d
--- /dev/null
@@ -0,0 +1,30 @@
+/* 
+ * Copyright (C) 2003 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __MEM_KERN_H__
+#define __MEM_KERN_H__
+
+#include "linux/list.h"
+#include "linux/types.h"
+
+struct remapper {
+       struct list_head list;
+       int (*proc)(int, unsigned long, int, __u64);
+};
+
+extern void register_remapper(struct remapper *info);
+
+#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/physmem.c b/arch/um/kernel/physmem.c
new file mode 100644 (file)
index 0000000..d0e0f50
--- /dev/null
@@ -0,0 +1,468 @@
+/* 
+ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/mm.h"
+#include "linux/ghash.h"
+#include "linux/slab.h"
+#include "linux/vmalloc.h"
+#include "linux/bootmem.h"
+#include "asm/types.h"
+#include "asm/pgtable.h"
+#include "kern_util.h"
+#include "user_util.h"
+#include "mode_kern.h"
+#include "mem.h"
+#include "mem_user.h"
+#include "os.h"
+#include "kern.h"
+#include "init.h"
+
+#if 0
+static pgd_t physmem_pgd[PTRS_PER_PGD];
+
+static struct phys_desc *lookup_mapping(void *addr)
+{
+       pgd = &physmem_pgd[pgd_index(addr)];
+       if(pgd_none(pgd))
+               return(NULL);
+
+       pmd = pmd_offset(pgd, addr);
+       if(pmd_none(pmd))
+               return(NULL);
+
+       pte = pte_offset_kernel(pmd, addr);
+       return((struct phys_desc *) pte_val(pte));
+}
+
+static struct add_mapping(void *addr, struct phys_desc *new)
+{
+}
+#endif
+
+#define PHYS_HASHSIZE (8192)
+
+struct phys_desc;
+
+DEF_HASH_STRUCTS(virtmem, PHYS_HASHSIZE, struct phys_desc);
+
+struct phys_desc {
+       struct virtmem_ptrs virt_ptrs;
+       int fd;
+       __u64 offset;
+       void *virt;
+       unsigned long phys;
+       struct list_head list;
+};
+
+struct virtmem_table virtmem_hash;
+
+static int virt_cmp(void *virt1, void *virt2)
+{
+       return(virt1 != virt2);
+}
+
+static int virt_hash(void *virt)
+{
+       unsigned long addr = ((unsigned long) virt) >> PAGE_SHIFT;
+       return(addr % PHYS_HASHSIZE);
+}
+
+DEF_HASH(static, virtmem, struct phys_desc, virt_ptrs, void *, virt, virt_cmp, 
+        virt_hash);
+
+LIST_HEAD(descriptor_mappings);
+
+struct desc_mapping {
+       int fd;
+       struct list_head list;
+       struct list_head pages;
+};
+
+static struct desc_mapping *find_mapping(int fd)
+{
+       struct desc_mapping *desc;
+       struct list_head *ele;
+
+       list_for_each(ele, &descriptor_mappings){
+               desc = list_entry(ele, struct desc_mapping, list);
+               if(desc->fd == fd)
+                       return(desc);
+       }
+
+       return(NULL);
+}
+
+static struct desc_mapping *descriptor_mapping(int fd)
+{
+       struct desc_mapping *desc;
+
+       desc = find_mapping(fd);
+       if(desc != NULL)
+               return(desc);
+
+       desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
+       if(desc == NULL)
+               return(NULL);
+
+       *desc = ((struct desc_mapping) 
+               { .fd =         fd,
+                 .list =       LIST_HEAD_INIT(desc->list),
+                 .pages =      LIST_HEAD_INIT(desc->pages) });
+       list_add(&desc->list, &descriptor_mappings);
+
+       return(desc);
+}
+
+int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
+{
+       struct desc_mapping *fd_maps;
+       struct phys_desc *desc;
+       unsigned long phys;
+       int err;
+
+       fd_maps = descriptor_mapping(fd);
+       if(fd_maps == NULL)
+               return(-ENOMEM);
+
+       phys = __pa(virt);
+       if(find_virtmem_hash(&virtmem_hash, virt) != NULL)
+               panic("Address 0x%p is already substituted\n", virt);
+
+       err = -ENOMEM;
+       desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
+       if(desc == NULL)
+               goto out;
+
+       *desc = ((struct phys_desc) 
+               { .virt_ptrs =  { NULL, NULL },
+                 .fd =         fd,
+                 .offset =             offset,
+                 .virt =               virt,
+                 .phys =               __pa(virt),
+                 .list =               LIST_HEAD_INIT(desc->list) });
+       insert_virtmem_hash(&virtmem_hash, desc);
+
+       list_add(&desc->list, &fd_maps->pages);
+
+       virt = (void *) ((unsigned long) virt & PAGE_MASK);
+       err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
+       if(!err)
+               goto out;
+
+       remove_virtmem_hash(&virtmem_hash, desc);
+       kfree(desc);
+ out:
+       return(err);
+}
+
+static int physmem_fd = -1;
+
+static void remove_mapping(struct phys_desc *desc)
+{
+       void *virt = desc->virt;
+       int err;
+
+       remove_virtmem_hash(&virtmem_hash, desc);
+       list_del(&desc->list);
+       kfree(desc);
+
+       err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
+       if(err)
+               panic("Failed to unmap block device page from physical memory, "
+                     "errno = %d", -err);
+}
+
+int physmem_remove_mapping(void *virt)
+{
+       struct phys_desc *desc;
+
+       virt = (void *) ((unsigned long) virt & PAGE_MASK);
+       desc = find_virtmem_hash(&virtmem_hash, virt);
+       if(desc == NULL)
+               return(0);
+
+       remove_mapping(desc);
+       return(1);
+}
+
+void physmem_forget_descriptor(int fd)
+{
+       struct desc_mapping *desc;
+       struct phys_desc *page;
+       struct list_head *ele, *next;
+       __u64 offset;
+       void *addr;
+       int err;
+
+       desc = find_mapping(fd);
+       if(desc == NULL)
+               return;
+
+       list_for_each_safe(ele, next, &desc->pages){
+               page = list_entry(ele, struct phys_desc, list);
+               offset = page->offset;
+               addr = page->virt;
+               remove_mapping(page);
+               err = os_seek_file(fd, offset);
+               if(err)
+                       panic("physmem_forget_descriptor - failed to seek "
+                             "to %lld in fd %d, error = %d\n",
+                             offset, fd, -err);
+               err = os_read_file(fd, addr, PAGE_SIZE);
+               if(err < 0)
+                       panic("physmem_forget_descriptor - failed to read "
+                             "from fd %d to 0x%p, error = %d\n",
+                             fd, addr, -err);
+       }
+
+       list_del(&desc->list);
+       kfree(desc);
+}
+
+void arch_free_page(struct page *page, int order)
+{
+       void *virt;
+       int i;
+
+       for(i = 0; i < (1 << order); i++){
+               virt = __va(page_to_phys(page + i));
+               physmem_remove_mapping(virt);
+       }
+}
+
+int is_remapped(void *virt)
+{
+       return(find_virtmem_hash(&virtmem_hash, virt) != NULL);
+}
+
+/* Changed during early boot */
+unsigned long high_physmem;
+
+extern unsigned long physmem_size;
+
+void *to_virt(unsigned long phys)
+{
+       return((void *) uml_physmem + phys);
+}
+
+unsigned long to_phys(void *virt)
+{
+       return(((unsigned long) virt) - uml_physmem);
+}
+
+int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
+{
+       struct page *p, *map;
+       unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
+       unsigned long iomem_len, iomem_pages, total_len, total_pages;
+       int i;
+
+       phys_pages = physmem >> PAGE_SHIFT;
+       phys_len = phys_pages * sizeof(struct page);
+
+       iomem_pages = iomem >> PAGE_SHIFT;
+       iomem_len = iomem_pages * sizeof(struct page);
+
+       highmem_pages = highmem >> PAGE_SHIFT;
+       highmem_len = highmem_pages * sizeof(struct page);
+
+       total_pages = phys_pages + iomem_pages + highmem_pages;
+       total_len = phys_len + iomem_pages + highmem_len;
+
+       if(kmalloc_ok){
+               map = kmalloc(total_len, GFP_KERNEL);
+               if(map == NULL) 
+                       map = vmalloc(total_len);
+       }
+       else map = alloc_bootmem_low_pages(total_len);
+
+       if(map == NULL)
+               return(-ENOMEM);
+
+       for(i = 0; i < total_pages; i++){
+               p = &map[i];
+               set_page_count(p, 0);
+               SetPageReserved(p);
+               INIT_LIST_HEAD(&p->lru);
+       }
+
+       mem_map = map;
+       max_mapnr = total_pages;
+       return(0);
+}
+
+struct page *phys_to_page(const unsigned long phys)
+{
+       return(&mem_map[phys >> PAGE_SHIFT]);
+}
+
+struct page *__virt_to_page(const unsigned long virt)
+{
+       return(&mem_map[__pa(virt) >> PAGE_SHIFT]);
+}
+
+unsigned long page_to_phys(struct page *page)
+{
+       return((page - mem_map) << PAGE_SHIFT);
+}
+
+pte_t mk_pte(struct page *page, pgprot_t pgprot)
+{
+       pte_t pte;
+
+       pte_val(pte) = page_to_phys(page) + pgprot_val(pgprot);
+       if(pte_present(pte)) pte_mknewprot(pte_mknewpage(pte));
+       return(pte);
+}
+
+/* Changed during early boot */
+static unsigned long kmem_top = 0;
+
+unsigned long get_kmem_end(void)
+{
+       if(kmem_top == 0) 
+               kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
+       return(kmem_top);
+}
+
+void map_memory(unsigned long virt, unsigned long phys, unsigned long len, 
+               int r, int w, int x)
+{
+       __u64 offset;
+       int fd, err;
+
+       fd = phys_mapping(phys, &offset);
+       err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
+       if(err)
+               panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
+                     "err = %d\n", virt, fd, offset, len, r, w, x, err);
+}
+
+#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
+
+void setup_physmem(unsigned long start, unsigned long reserve_end,
+                  unsigned long len, unsigned long highmem)
+{
+       unsigned long reserve = reserve_end - start;
+       int pfn = PFN_UP(__pa(reserve_end));
+       int delta = (len - reserve) >> PAGE_SHIFT;
+       int err, offset, bootmap_size;
+
+       physmem_fd = create_mem_file(len + highmem);
+
+       offset = uml_reserved - uml_physmem;
+       err = os_map_memory((void *) uml_reserved, physmem_fd, offset, 
+                           len - offset, 1, 1, 0);
+       if(err < 0){
+               os_print_error(err, "Mapping memory");
+               exit(1);
+       }
+
+       bootmap_size = init_bootmem(pfn, pfn + delta);
+       free_bootmem(__pa(reserve_end) + bootmap_size,
+                    len - bootmap_size - reserve);
+}
+
+int phys_mapping(unsigned long phys, __u64 *offset_out)
+{
+       struct phys_desc *desc = find_virtmem_hash(&virtmem_hash, 
+                                                  __va(phys & PAGE_MASK));
+       int fd = -1;
+
+       if(desc != NULL){
+               fd = desc->fd;
+               *offset_out = desc->offset;
+       }
+       else if(phys < physmem_size){
+               fd = physmem_fd;
+               *offset_out = phys;
+       }
+       else if(phys < __pa(end_iomem)){
+               struct iomem_region *region = iomem_regions;
+       
+               while(region != NULL){
+                       if((phys >= region->phys) && 
+                          (phys < region->phys + region->size)){
+                               fd = region->fd;
+                               *offset_out = phys - region->phys;
+                               break;
+                       }
+                       region = region->next;
+               }
+       }
+       else if(phys < __pa(end_iomem) + highmem){
+               fd = physmem_fd;
+               *offset_out = phys - iomem_size;
+       }
+
+       return(fd);
+}
+
+static int __init uml_mem_setup(char *line, int *add)
+{
+       char *retptr;
+       physmem_size = memparse(line,&retptr);
+       return 0;
+}
+__uml_setup("mem=", uml_mem_setup,
+"mem=<Amount of desired ram>\n"
+"    This controls how much \"physical\" memory the kernel allocates\n"
+"    for the system. The size is specified as a number followed by\n"
+"    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
+"    This is not related to the amount of memory in the host.  It can\n"
+"    be more, and the excess, if it's ever used, will just be swapped out.\n"
+"      Example: mem=64M\n\n"
+);
+
+unsigned long find_iomem(char *driver, unsigned long *len_out)
+{
+       struct iomem_region *region = iomem_regions;
+       
+       while(region != NULL){
+               if(!strcmp(region->driver, driver)){
+                       *len_out = region->size;
+                       return(region->virt);
+               }
+       }
+
+       return(0);
+}
+
+int setup_iomem(void)
+{
+       struct iomem_region *region = iomem_regions;
+       unsigned long iomem_start = high_physmem + PAGE_SIZE;
+       int err;
+
+       while(region != NULL){
+               err = os_map_memory((void *) iomem_start, region->fd, 0, 
+                                   region->size, 1, 1, 0);
+               if(err)
+                       printk("Mapping iomem region for driver '%s' failed, "
+                              "errno = %d\n", region->driver, -err);
+               else {
+                       region->virt = iomem_start;
+                       region->phys = __pa(region->virt);
+               }
+
+               iomem_start += region->size + PAGE_SIZE;
+               region = region->next;
+       }
+
+       return(0);
+}
+
+__initcall(setup_iomem);
+
+/*
+ * 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/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c
new file mode 100644 (file)
index 0000000..ea82f19
--- /dev/null
@@ -0,0 +1,219 @@
+/* 
+ * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/kernel.h"
+#include "linux/string.h"
+#include "linux/fs.h"
+#include "linux/highmem.h"
+#include "asm/page.h"
+#include "asm/pgtable.h"
+#include "asm/uaccess.h"
+#include "kern_util.h"
+
+extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, 
+                            pte_t *pte_out);
+
+static unsigned long maybe_map(unsigned long virt, int is_write)
+{
+       pte_t pte;
+       int err;
+
+       void *phys = um_virt_to_phys(current, virt, &pte);
+       int dummy_code;
+
+       if(IS_ERR(phys) || (is_write && !pte_write(pte))){
+               err = handle_page_fault(virt, 0, is_write, 0, &dummy_code);
+               if(err)
+                       return(0);
+               phys = um_virt_to_phys(current, virt, NULL);
+       }
+       return((unsigned long) phys);
+}
+
+static int do_op(unsigned long addr, int len, int is_write, 
+                int (*op)(unsigned long addr, int len, void *arg), void *arg)
+{
+       struct page *page;
+       int n;
+
+       addr = maybe_map(addr, is_write);
+       if(addr == -1)
+               return(-1);
+
+       page = phys_to_page(addr);
+       addr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK);
+       n = (*op)(addr, len, arg);
+       kunmap(page);
+
+       return(n);
+}
+
+static int buffer_op(unsigned long addr, int len, int is_write,
+                    int (*op)(unsigned long addr, int len, void *arg),
+                    void *arg)
+{
+       int size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
+       int remain = len, n;
+
+       n = do_op(addr, size, is_write, op, arg);
+       if(n != 0)
+               return(n < 0 ? remain : 0);
+
+       addr += size;
+       remain -= size;
+       if(remain == 0) 
+               return(0);
+
+       while(addr < ((addr + remain) & PAGE_MASK)){
+               n = do_op(addr, PAGE_SIZE, is_write, op, arg);
+               if(n != 0)
+                       return(n < 0 ? remain : 0);
+
+               addr += PAGE_SIZE;
+               remain -= PAGE_SIZE;
+       }
+       if(remain == 0)
+               return(0);
+
+       n = do_op(addr, remain, is_write, op, arg);
+       if(n != 0)
+               return(n < 0 ? remain : 0);
+       return(0);
+}
+
+static int copy_chunk_from_user(unsigned long from, int len, void *arg)
+{
+       unsigned long *to_ptr = arg, to = *to_ptr;
+
+       memcpy((void *) to, (void *) from, len);
+       *to_ptr += len;
+       return(0);
+}
+
+int copy_from_user_skas(void *to, const void *from, int n)
+{
+       if(segment_eq(get_fs(), KERNEL_DS)){
+               memcpy(to, from, n);
+               return(0);
+       }
+
+       return(access_ok_skas(VERIFY_READ, from, n) ?
+              buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
+              n);
+}
+
+static int copy_chunk_to_user(unsigned long to, int len, void *arg)
+{
+       unsigned long *from_ptr = arg, from = *from_ptr;
+
+       memcpy((void *) to, (void *) from, len);
+       *from_ptr += len;
+       return(0);
+}
+
+int copy_to_user_skas(void *to, const void *from, int n)
+{
+       if(segment_eq(get_fs(), KERNEL_DS)){
+               memcpy(to, from, n);
+               return(0);
+       }
+
+       return(access_ok_skas(VERIFY_WRITE, to, n) ?
+              buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
+              n);
+}
+
+static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
+{
+       char **to_ptr = arg, *to = *to_ptr;
+       int n;
+
+       strncpy(to, (void *) from, len);
+       n = strnlen(to, len);
+       *to_ptr += n;
+
+       if(n < len) 
+               return(1);
+       return(0);
+}
+
+int strncpy_from_user_skas(char *dst, const char *src, int count)
+{
+       int n;
+       char *ptr = dst;
+
+       if(segment_eq(get_fs(), KERNEL_DS)){
+               strncpy(dst, src, count);
+               return(strnlen(dst, count));
+       }
+
+       if(!access_ok_skas(VERIFY_READ, src, 1))
+               return(-EFAULT);
+
+       n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user, 
+                     &ptr);
+       if(n != 0)
+               return(-EFAULT);
+       return(strnlen(dst, count));
+}
+
+static int clear_chunk(unsigned long addr, int len, void *unused)
+{
+       memset((void *) addr, 0, len);
+       return(0);
+}
+
+int __clear_user_skas(void *mem, int len)
+{
+       return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL));
+}
+
+int clear_user_skas(void *mem, int len)
+{
+       if(segment_eq(get_fs(), KERNEL_DS)){
+               memset(mem, 0, len);
+               return(0);
+       }
+
+       return(access_ok_skas(VERIFY_WRITE, mem, len) ? 
+              buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len);
+}
+
+static int strnlen_chunk(unsigned long str, int len, void *arg)
+{
+       int *len_ptr = arg, n;
+
+       n = strnlen((void *) str, len);
+       *len_ptr += n;
+
+       if(n < len)
+               return(1);
+       return(0);
+}
+
+int strnlen_user_skas(const void *str, int len)
+{
+       int count = 0, n;
+
+       if(segment_eq(get_fs(), KERNEL_DS))
+               return(strnlen(str, len) + 1);
+
+       n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
+       if(n == 0)
+               return(count + 1);
+       return(-EFAULT);
+}
+
+/*
+ * 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/smp.c.orig b/arch/um/kernel/smp.c.orig
new file mode 100644 (file)
index 0000000..34f826c
--- /dev/null
@@ -0,0 +1,302 @@
+/* 
+ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/config.h"
+
+#ifdef CONFIG_SMP
+
+#include "linux/sched.h"
+#include "linux/module.h"
+#include "linux/threads.h"
+#include "linux/interrupt.h"
+#include "linux/err.h"
+#include "asm/smp.h"
+#include "asm/processor.h"
+#include "asm/spinlock.h"
+#include "asm/hardirq.h"
+#include "user_util.h"
+#include "kern_util.h"
+#include "kern.h"
+#include "irq_user.h"
+#include "os.h"
+
+/* CPU online map, set by smp_boot_cpus */
+unsigned long cpu_online_map = cpumask_of_cpu(0);
+
+EXPORT_SYMBOL(cpu_online_map);
+
+/* Per CPU bogomips and other parameters
+ * The only piece used here is the ipi pipe, which is set before SMP is
+ * started and never changed.
+ */
+struct cpuinfo_um cpu_data[NR_CPUS];
+
+spinlock_t um_bh_lock = SPIN_LOCK_UNLOCKED;
+
+atomic_t global_bh_count;
+
+/* Not used by UML */
+unsigned char global_irq_holder = NO_PROC_ID;
+unsigned volatile long global_irq_lock;
+
+/* Set when the idlers are all forked */
+int smp_threads_ready = 0;
+
+/* A statistic, can be a little off */
+int num_reschedules_sent = 0;
+
+/* Small, random number, never changed */
+unsigned long cache_decay_ticks = 5;
+
+/* Not changed after boot */
+struct task_struct *idle_threads[NR_CPUS];
+
+void smp_send_reschedule(int cpu)
+{
+       write(cpu_data[cpu].ipi_pipe[1], "R", 1);
+       num_reschedules_sent++;
+}
+
+static void show(char * str)
+{
+       int cpu = smp_processor_id();
+
+       printk(KERN_INFO "\n%s, CPU %d:\n", str, cpu);
+}
+       
+#define MAXCOUNT 100000000
+
+static inline void wait_on_bh(void)
+{
+       int count = MAXCOUNT;
+       do {
+               if (!--count) {
+                       show("wait_on_bh");
+                       count = ~0;
+               }
+               /* nothing .. wait for the other bh's to go away */
+       } while (atomic_read(&global_bh_count) != 0);
+}
+
+/*
+ * This is called when we want to synchronize with
+ * bottom half handlers. We need to wait until
+ * no other CPU is executing any bottom half handler.
+ *
+ * Don't wait if we're already running in an interrupt
+ * context or are inside a bh handler. 
+ */
+void synchronize_bh(void)
+{
+       if (atomic_read(&global_bh_count) && !in_interrupt())
+               wait_on_bh();
+}
+
+void smp_send_stop(void)
+{
+       int i;
+
+       printk(KERN_INFO "Stopping all CPUs...");
+       for(i = 0; i < num_online_cpus(); i++){
+               if(i == current->thread_info->cpu)
+                       continue;
+               write(cpu_data[i].ipi_pipe[1], "S", 1);
+       }
+       printk("done\n");
+}
+
+static cpumask_t smp_commenced_mask;
+static cpumask_t smp_callin_map = CPU_MASK_NONE;
+
+static int idle_proc(void *cpup)
+{
+       int cpu = (int) cpup, err;
+
+       err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1);
+       if(err)
+               panic("CPU#%d failed to create IPI pipe, errno = %d", cpu, 
+                     -err);
+
+       activate_ipi(cpu_data[cpu].ipi_pipe[0], 
+                    current->thread.mode.tt.extern_pid);
+       wmb();
+       if (cpu_test_and_set(cpu, &smp_callin_map)) {
+               printk("huh, CPU#%d already present??\n", cpu);
+               BUG();
+       }
+
+       while (!cpu_isset(cpu, &smp_commenced_mask))
+               cpu_relax();
+
+       cpu_set(cpu, cpu_online_map);
+       default_idle();
+       return(0);
+}
+
+static struct task_struct *idle_thread(int cpu)
+{
+       struct task_struct *new_task;
+       unsigned char c;
+
+        current->thread.request.u.thread.proc = idle_proc;
+        current->thread.request.u.thread.arg = (void *) cpu;
+       new_task = do_fork(CLONE_VM | CLONE_IDLETASK, 0, NULL, 0, NULL, NULL);
+       if(IS_ERR(new_task)) panic("do_fork failed in idle_thread");
+
+       cpu_tasks[cpu] = ((struct cpu_task) 
+                         { .pid =      new_task->thread.mode.tt.extern_pid,
+                           .task =     new_task } );
+       idle_threads[cpu] = new_task;
+       CHOOSE_MODE(write(new_task->thread.mode.tt.switch_pipe[1], &c, 
+                         sizeof(c)),
+                   ({ panic("skas mode doesn't support SMP"); }));
+       return(new_task);
+}
+
+void smp_prepare_cpus(unsigned int maxcpus)
+{
+       struct task_struct *idle;
+       unsigned long waittime;
+       int err, cpu;
+
+       cpu_set(0, cpu_online_map);
+       cpu_set(0, smp_callin_map);
+
+       err = os_pipe(cpu_data[0].ipi_pipe, 1, 1);
+       if(err) panic("CPU#0 failed to create IPI pipe, errno = %d", -err);
+
+       activate_ipi(cpu_data[0].ipi_pipe[0], 
+                    current->thread.mode.tt.extern_pid);
+
+       for(cpu = 1; cpu < ncpus; cpu++){
+               printk("Booting processor %d...\n", cpu);
+               
+               idle = idle_thread(cpu);
+
+               init_idle(idle, cpu);
+               unhash_process(idle);
+
+               waittime = 200000000;
+               while (waittime-- && !cpu_isset(cpu, smp_callin_map))
+                       cpu_relax();
+
+               if (cpu_isset(cpu, smp_callin_map))
+                       printk("done\n");
+               else printk("failed\n");
+       }
+}
+
+void smp_prepare_boot_cpu(void)
+{
+       cpu_set(smp_processor_id(), cpu_online_map);
+}
+
+int __cpu_up(unsigned int cpu)
+{
+       cpu_set(cpu, smp_commenced_mask);
+       while (!cpu_isset(cpu, cpu_online_map))
+               mb();
+       return(0);
+}
+
+int setup_profiling_timer(unsigned int multiplier)
+{
+       printk(KERN_INFO "setup_profiling_timer\n");
+       return(0);
+}
+
+void smp_call_function_slave(int cpu);
+
+void IPI_handler(int cpu)
+{
+       unsigned char c;
+       int fd;
+
+       fd = cpu_data[cpu].ipi_pipe[0];
+       while (read(fd, &c, 1) == 1) {
+               switch (c) {
+               case 'C':
+                       smp_call_function_slave(cpu);
+                       break;
+
+               case 'R':
+                       set_tsk_need_resched(current);
+                       break;
+
+               case 'S':
+                       printk("CPU#%d stopping\n", cpu);
+                       while(1)
+                               pause();
+                       break;
+
+               default:
+                       printk("CPU#%d received unknown IPI [%c]!\n", cpu, c);
+                       break;
+               }
+       }
+}
+
+int hard_smp_processor_id(void)
+{
+       return(pid_to_processor_id(os_getpid()));
+}
+
+static spinlock_t call_lock = SPIN_LOCK_UNLOCKED;
+static atomic_t scf_started;
+static atomic_t scf_finished;
+static void (*func)(void *info);
+static void *info;
+
+void smp_call_function_slave(int cpu)
+{
+       atomic_inc(&scf_started);
+       (*func)(info);
+       atomic_inc(&scf_finished);
+}
+
+int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, 
+                     int wait)
+{
+       int cpus = num_online_cpus() - 1;
+       int i;
+
+       if (!cpus)
+               return 0;
+
+       spin_lock_bh(&call_lock);
+       atomic_set(&scf_started, 0);
+       atomic_set(&scf_finished, 0);
+       func = _func;
+       info = _info;
+
+       for (i=0;i<NR_CPUS;i++)
+               if((i != current->thread_info->cpu) && 
+                  cpu_isset(i, cpu_online_map))
+                       write(cpu_data[i].ipi_pipe[1], "C", 1);
+
+       while (atomic_read(&scf_started) != cpus)
+               barrier();
+
+       if (wait)
+               while (atomic_read(&scf_finished) != cpus)
+                       barrier();
+
+       spin_unlock_bh(&call_lock);
+       return 0;
+}
+
+#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/tt/uaccess.c b/arch/um/kernel/tt/uaccess.c
new file mode 100644 (file)
index 0000000..9c84011
--- /dev/null
@@ -0,0 +1,73 @@
+/* 
+ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/sched.h"
+#include "asm/uaccess.h"
+
+int copy_from_user_tt(void *to, const void *from, int n)
+{
+       if(!access_ok_tt(VERIFY_READ, from, n)) 
+               return(n);
+
+       return(__do_copy_from_user(to, from, n, &current->thread.fault_addr,
+                                  &current->thread.fault_catcher));
+}
+
+int copy_to_user_tt(void *to, const void *from, int n)
+{
+       if(!access_ok_tt(VERIFY_WRITE, to, n))
+               return(n);
+               
+       return(__do_copy_to_user(to, from, n, &current->thread.fault_addr,
+                                &current->thread.fault_catcher));
+}
+
+int strncpy_from_user_tt(char *dst, const char *src, int count)
+{
+       int n;
+
+       if(!access_ok_tt(VERIFY_READ, src, 1)) 
+               return(-EFAULT);
+
+       n = __do_strncpy_from_user(dst, src, count, 
+                                  &current->thread.fault_addr,
+                                  &current->thread.fault_catcher);
+       if(n < 0) return(-EFAULT);
+       return(n);
+}
+
+int __clear_user_tt(void *mem, int len)
+{
+       return(__do_clear_user(mem, len,
+                              &current->thread.fault_addr,
+                              &current->thread.fault_catcher));
+}
+
+int clear_user_tt(void *mem, int len)
+{
+       if(!access_ok_tt(VERIFY_WRITE, mem, len))
+               return(len);
+
+       return(__do_clear_user(mem, len, &current->thread.fault_addr,
+                              &current->thread.fault_catcher));
+}
+
+int strnlen_user_tt(const void *str, int len)
+{
+       return(__do_strnlen_user(str, len,
+                                &current->thread.fault_addr,
+                                &current->thread.fault_catcher));
+}
+
+/*
+ * 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/user_syms.c b/arch/um/os-Linux/user_syms.c
new file mode 100644 (file)
index 0000000..ef0fb71
--- /dev/null
@@ -0,0 +1,88 @@
+#include "linux/types.h"
+#include "linux/module.h"
+
+/* Some of this are builtin function (some are not but could in the future),
+ * so I *must* declare good prototypes for them and then EXPORT them.
+ * The kernel code uses the macro defined by include/linux/string.h,
+ * so I undef macros; the userspace code does not include that and I
+ * add an EXPORT for the glibc one.*/
+
+#undef strlen
+#undef strstr
+#undef memcpy
+#undef memset
+
+extern size_t strlen(const char *);
+extern void *memcpy(void *, const void *, size_t);
+extern void *memset(void *, int, size_t);
+extern int printf(const char *, ...);
+
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(printf);
+
+EXPORT_SYMBOL(strstr);
+
+/* Here, instead, I can provide a fake prototype. Yes, someone cares: genksyms.
+ * However, the modules will use the CRC defined *here*, no matter if it is 
+ * good; so the versions of these symbols will always match
+ */
+#define EXPORT_SYMBOL_PROTO(sym)       \
+       int sym(void);                  \
+       EXPORT_SYMBOL(sym);
+
+EXPORT_SYMBOL_PROTO(__errno_location);
+
+EXPORT_SYMBOL_PROTO(access);
+EXPORT_SYMBOL_PROTO(open);
+EXPORT_SYMBOL_PROTO(open64);
+EXPORT_SYMBOL_PROTO(close);
+EXPORT_SYMBOL_PROTO(read);
+EXPORT_SYMBOL_PROTO(write);
+EXPORT_SYMBOL_PROTO(dup2);
+EXPORT_SYMBOL_PROTO(__xstat);
+EXPORT_SYMBOL_PROTO(__lxstat);
+EXPORT_SYMBOL_PROTO(__lxstat64);
+EXPORT_SYMBOL_PROTO(lseek);
+EXPORT_SYMBOL_PROTO(lseek64);
+EXPORT_SYMBOL_PROTO(chown);
+EXPORT_SYMBOL_PROTO(truncate);
+EXPORT_SYMBOL_PROTO(utime);
+EXPORT_SYMBOL_PROTO(chmod);
+EXPORT_SYMBOL_PROTO(rename);
+EXPORT_SYMBOL_PROTO(__xmknod);
+
+EXPORT_SYMBOL_PROTO(symlink);
+EXPORT_SYMBOL_PROTO(link);
+EXPORT_SYMBOL_PROTO(unlink);
+EXPORT_SYMBOL_PROTO(readlink);
+
+EXPORT_SYMBOL_PROTO(mkdir);
+EXPORT_SYMBOL_PROTO(rmdir);
+EXPORT_SYMBOL_PROTO(opendir);
+EXPORT_SYMBOL_PROTO(readdir);
+EXPORT_SYMBOL_PROTO(closedir);
+EXPORT_SYMBOL_PROTO(seekdir);
+EXPORT_SYMBOL_PROTO(telldir);
+
+EXPORT_SYMBOL_PROTO(ioctl);
+
+EXPORT_SYMBOL_PROTO(pread64);
+EXPORT_SYMBOL_PROTO(pwrite64);
+
+EXPORT_SYMBOL_PROTO(statfs);
+EXPORT_SYMBOL_PROTO(statfs64);
+
+EXPORT_SYMBOL_PROTO(getuid);
+
+/*
+ * 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/time.c b/arch/um/sys-i386/time.c
new file mode 100644 (file)
index 0000000..a6a5ba7
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * sys-i386/time.c 
+ * Created             25.9.2002       Sapan Bhatia
+ *
+ */
+
+unsigned long long time_stamp(void)
+{
+       unsigned long low, high;
+
+       asm("rdtsc" : "=a" (low), "=d" (high));
+       return((((unsigned long long) high) << 32) + low);
+}
+
+/*
+ * 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/Makefile.orig b/fs/Makefile.orig
new file mode 100644 (file)
index 0000000..a288c0c
--- /dev/null
@@ -0,0 +1,94 @@
+#
+# Makefile for the Linux filesystems.
+#
+# 14 Sep 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+# 
+
+obj-y :=       open.o read_write.o file_table.o buffer.o \
+               bio.o super.o block_dev.o char_dev.o stat.o exec.o pipe.o \
+               namei.o fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
+               dcache.o inode.o attr.o bad_inode.o file.o dnotify.o \
+               filesystems.o namespace.o seq_file.o xattr.o libfs.o \
+               fs-writeback.o mpage.o direct-io.o aio.o
+
+obj-$(CONFIG_EPOLL)            += eventpoll.o
+obj-$(CONFIG_COMPAT)           += compat.o
+
+nfsd-$(CONFIG_NFSD)            := nfsctl.o
+obj-y                          += $(nfsd-y) $(nfsd-m)
+
+obj-$(CONFIG_BINFMT_AOUT)      += binfmt_aout.o
+obj-$(CONFIG_BINFMT_EM86)      += binfmt_em86.o
+obj-$(CONFIG_BINFMT_MISC)      += binfmt_misc.o
+
+# binfmt_script is always there
+obj-y                          += binfmt_script.o
+
+obj-$(CONFIG_BINFMT_ELF)       += binfmt_elf.o
+obj-$(CONFIG_BINFMT_SOM)       += binfmt_som.o
+obj-$(CONFIG_BINFMT_FLAT)      += binfmt_flat.o
+
+obj-$(CONFIG_FS_MBCACHE)       += mbcache.o
+obj-$(CONFIG_FS_POSIX_ACL)     += posix_acl.o xattr_acl.o
+
+obj-$(CONFIG_QUOTA)            += dquot.o
+obj-$(CONFIG_QFMT_V1)          += quota_v1.o
+obj-$(CONFIG_QFMT_V2)          += quota_v2.o
+obj-$(CONFIG_QUOTACTL)         += quota.o
+
+obj-$(CONFIG_PROC_FS)          += proc/
+obj-y                          += partitions/
+obj-$(CONFIG_SYSFS)            += sysfs/
+obj-y                          += devpts/
+
+obj-$(CONFIG_PROFILING)                += dcookies.o
+# Do not add any filesystems before this line
+obj-$(CONFIG_REISERFS_FS)      += reiserfs/
+obj-$(CONFIG_EXT3_FS)          += ext3/ # Before ext2 so root fs can be ext3
+obj-$(CONFIG_JBD)              += jbd/
+obj-$(CONFIG_EXT2_FS)          += ext2/
+obj-$(CONFIG_CRAMFS)           += cramfs/
+obj-$(CONFIG_RAMFS)            += ramfs/
+obj-$(CONFIG_HUGETLBFS)                += hugetlbfs/
+obj-$(CONFIG_CODA_FS)          += coda/
+obj-$(CONFIG_INTERMEZZO_FS)    += intermezzo/
+obj-$(CONFIG_MINIX_FS)         += minix/
+obj-$(CONFIG_FAT_FS)           += fat/
+obj-$(CONFIG_UMSDOS_FS)                += umsdos/
+obj-$(CONFIG_MSDOS_FS)         += msdos/
+obj-$(CONFIG_VFAT_FS)          += vfat/
+obj-$(CONFIG_BFS_FS)           += bfs/
+obj-$(CONFIG_ISO9660_FS)       += isofs/
+obj-$(CONFIG_DEVFS_FS)         += devfs/
+obj-$(CONFIG_HFSPLUS_FS)       += hfsplus/ # Before hfs to find wrapped HFS+
+obj-$(CONFIG_HFS_FS)           += hfs/
+obj-$(CONFIG_VXFS_FS)          += freevxfs/
+obj-$(CONFIG_NFS_FS)           += nfs/
+obj-$(CONFIG_EXPORTFS)         += exportfs/
+obj-$(CONFIG_NFSD)             += nfsd/
+obj-$(CONFIG_LOCKD)            += lockd/
+obj-$(CONFIG_NLS)              += nls/
+obj-$(CONFIG_SYSV_FS)          += sysv/
+obj-$(CONFIG_SMB_FS)           += smbfs/
+obj-$(CONFIG_CIFS)             += cifs/
+obj-$(CONFIG_NCP_FS)           += ncpfs/
+obj-$(CONFIG_HPFS_FS)          += hpfs/
+obj-$(CONFIG_NTFS_FS)          += ntfs/
+obj-$(CONFIG_UFS_FS)           += ufs/
+obj-$(CONFIG_EFS_FS)           += efs/
+obj-$(CONFIG_JFFS_FS)          += jffs/
+obj-$(CONFIG_JFFS2_FS)         += jffs2/
+obj-$(CONFIG_AFFS_FS)          += affs/
+obj-$(CONFIG_ROMFS_FS)         += romfs/
+obj-$(CONFIG_QNX4FS_FS)                += qnx4/
+obj-$(CONFIG_AUTOFS_FS)                += autofs/
+obj-$(CONFIG_AUTOFS4_FS)       += autofs4/
+obj-$(CONFIG_ADFS_FS)          += adfs/
+obj-$(CONFIG_UDF_FS)           += udf/
+obj-$(CONFIG_SUN_OPENPROMFS)   += openpromfs/
+obj-$(CONFIG_JFS_FS)           += jfs/
+obj-$(CONFIG_XFS_FS)           += xfs/
+obj-$(CONFIG_AFS_FS)           += afs/
+obj-$(CONFIG_BEFS_FS)          += befs/
diff --git a/fs/hostfs/Makefile b/fs/hostfs/Makefile
new file mode 100644 (file)
index 0000000..794292e
--- /dev/null
@@ -0,0 +1,26 @@
+# 
+# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+# Licensed under the GPL
+#
+
+# struct stat64 changed the inode field name between 2.2 and 2.4 from st_ino
+# to __st_ino.  It stayed in the same place, so as long as the correct name
+# is used, hostfs compiled on 2.2 should work on 2.4 and vice versa.
+
+STAT64_INO_FIELD := $(shell grep -q __st_ino /usr/include/bits/stat.h && \
+                               echo __)st_ino
+
+hostfs-objs := hostfs_kern.o hostfs_user.o
+
+obj-y = 
+obj-$(CONFIG_HOSTFS) += hostfs.o
+
+SINGLE_OBJS = $(foreach f,$(patsubst %.o,%,$(obj-y) $(obj-m)),$($(f)-objs))
+
+USER_OBJS := $(filter %_user.o,$(obj-y) $(obj-m) $(SINGLE_OBJS))
+USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file))
+
+USER_CFLAGS += -DSTAT64_INO_FIELD=$(STAT64_INO_FIELD)
+
+$(USER_OBJS) : %.o: %.c
+       $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $<
diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h
new file mode 100644 (file)
index 0000000..d1f6c33
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef __UM_FS_HOSTFS
+#define __UM_FS_HOSTFS
+
+#include "os.h"
+
+/* These are exactly the same definitions as in fs.h, but the names are 
+ * changed so that this file can be included in both kernel and user files.
+ */
+
+#define HOSTFS_ATTR_MODE       1
+#define HOSTFS_ATTR_UID        2
+#define HOSTFS_ATTR_GID        4
+#define HOSTFS_ATTR_SIZE       8
+#define HOSTFS_ATTR_ATIME      16
+#define HOSTFS_ATTR_MTIME      32
+#define HOSTFS_ATTR_CTIME      64
+#define HOSTFS_ATTR_ATIME_SET  128
+#define HOSTFS_ATTR_MTIME_SET  256
+#define HOSTFS_ATTR_FORCE      512     /* Not a change, but a change it */
+#define HOSTFS_ATTR_ATTR_FLAG  1024
+
+struct hostfs_iattr {
+       unsigned int    ia_valid;
+       mode_t          ia_mode;
+       uid_t           ia_uid;
+       gid_t           ia_gid;
+       loff_t          ia_size;
+       struct timespec ia_atime;
+       struct timespec ia_mtime;
+       struct timespec ia_ctime;
+       unsigned int    ia_attr_flags;
+};
+
+extern int stat_file(const char *path, unsigned long long *inode_out, 
+                    int *mode_out, int *nlink_out, int *uid_out, int *gid_out,
+                    unsigned long long *size_out, struct timespec *atime_out, 
+                    struct timespec *mtime_out, struct timespec *ctime_out, 
+                    int *blksize_out, unsigned long long *blocks_out);
+extern int access_file(char *path, int r, int w, int x);
+extern int open_file(char *path, int r, int w, int append);
+extern int file_type(const char *path, int *rdev);
+extern void *open_dir(char *path, int *err_out);
+extern char *read_dir(void *stream, unsigned long long *pos, 
+                     unsigned long long *ino_out, int *len_out);
+extern void close_file(void *stream);
+extern void close_dir(void *stream);
+extern int read_file(int fd, unsigned long long *offset, char *buf, int len);
+extern int write_file(int fd, unsigned long long *offset, const char *buf,
+                     int len);
+extern int lseek_file(int fd, long long offset, int whence);
+extern int file_create(char *name, int ur, int uw, int ux, int gr, 
+                      int gw, int gx, int or, int ow, int ox);
+extern int set_attr(const char *file, struct hostfs_iattr *attrs);
+extern int make_symlink(const char *from, const char *to);
+extern int unlink_file(const char *file);
+extern int do_mkdir(const char *file, int mode);
+extern int do_rmdir(const char *file);
+extern int do_mknod(const char *file, int mode, int dev);
+extern int link_file(const char *from, const char *to);
+extern int do_readlink(char *file, char *buf, int size);
+extern int rename_file(char *from, char *to);
+extern int do_statfs(char *root, long *bsize_out, long long *blocks_out, 
+                    long long *bfree_out, long long *bavail_out, 
+                    long long *files_out, long long *ffree_out, 
+                    void *fsid_out, int fsid_size, long *namelen_out, 
+                    long *spare_out);
+
+#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/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
new file mode 100644 (file)
index 0000000..ef5d5d1
--- /dev/null
@@ -0,0 +1,1008 @@
+/* 
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ *
+ * Ported the filesystem routines to 2.5.
+ * 2003-02-10 Petr Baudis <pasky@ucw.cz>
+ */
+
+#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/list.h>
+#include <linux/buffer_head.h>
+#include <linux/root_dev.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 "init.h"
+
+struct hostfs_inode_info {
+       char *host_filename;
+       int fd;
+       int mode;
+       struct inode vfs_inode;
+};
+
+static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
+{
+       return(list_entry(inode, struct hostfs_inode_info, vfs_inode));
+}
+
+#define FILE_HOSTFS_I(file) HOSTFS_I((file)->f_dentry->d_inode)
+
+int hostfs_d_delete(struct dentry *dentry)
+{
+       return(1);
+}
+
+struct dentry_operations hostfs_dentry_ops = {
+       .d_delete               = hostfs_d_delete,
+};
+
+/* Changed in hostfs_args before the kernel starts running */
+static char *root_ino = "/";
+static int append = 0;
+
+#define HOSTFS_SUPER_MAGIC 0x00c0ffee
+
+static struct inode_operations hostfs_iops;
+static struct inode_operations hostfs_dir_iops;
+static struct address_space_operations hostfs_link_aops;
+
+static int __init hostfs_args(char *options, int *add)
+{
+       char *ptr;
+
+       ptr = strchr(options, ',');
+       if(ptr != NULL)
+               *ptr++ = '\0';
+       if(*options != '\0')
+               root_ino = options;
+
+       options = ptr;
+       while(options){
+               ptr = strchr(options, ',');
+               if(ptr != NULL)
+                       *ptr++ = '\0';
+               if(*options != '\0'){
+                       if(!strcmp(options, "append"))
+                               append = 1;
+                       else printf("hostfs_args - unsupported option - %s\n",
+                                   options);
+               }
+               options = ptr;
+       }
+       return(0);
+}
+
+__uml_setup("hostfs=", hostfs_args,
+"hostfs=<root dir>,<flags>,...\n"
+"    This is used to set hostfs parameters.  The root directory argument\n"
+"    is used to confine all hostfs mounts to within the specified directory\n"
+"    tree on the host.  If this isn't specified, then a user inside UML can\n"
+"    mount anything on the host that's accessible to the user that's running\n"
+"    it.\n"
+"    The only flag currently supported is 'append', which specifies that all\n"
+"    files opened by hostfs will be opened in append mode.\n\n"
+);
+
+static char *dentry_name(struct dentry *dentry, int extra)
+{
+       struct dentry *parent;
+       char *root, *name;
+       int len;
+
+       len = 0;
+       parent = dentry;
+       while(parent->d_parent != parent){
+               len += parent->d_name.len + 1;
+               parent = parent->d_parent;
+       }
+       
+       root = HOSTFS_I(parent->d_inode)->host_filename;
+       len += strlen(root);
+       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;
+       }
+       strncpy(name, root, strlen(root));
+       return(name);
+}
+
+static 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));
+}
+
+static int read_name(struct inode *ino, char *name)
+{
+       /* 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
+        */
+       int err;
+       int i_mode, i_nlink, i_blksize;
+       unsigned long long i_size;
+       unsigned long long i_ino;
+       unsigned long long i_blocks;
+
+       err = stat_file(name, &i_ino, &i_mode, &i_nlink, &ino->i_uid, 
+                       &ino->i_gid, &i_size, &ino->i_atime, &ino->i_mtime, 
+                       &ino->i_ctime, &i_blksize, &i_blocks);
+       if(err) 
+               return(err);
+
+       ino->i_ino = i_ino;
+       ino->i_mode = i_mode;
+       ino->i_nlink = i_nlink;
+       ino->i_size = i_size;
+       ino->i_blksize = i_blksize;
+       ino->i_blocks = i_blocks;
+       if((ino->i_sb->s_dev == ROOT_DEV) && (ino->i_uid == getuid()))
+               ino->i_uid = 0;
+       return(0);
+}
+
+static char *follow_link(char *link)
+{
+       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_readlink(link, name, len);
+               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);
+       kfree(link);
+       return(resolved);
+
+ out_free:
+       kfree(name);
+ out:
+       return(ERR_PTR(n));
+}
+
+static int read_inode(struct inode *ino)
+{
+       char *name;
+       int err = 0;
+
+       /* 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;
+
+       if(file_type(name, NULL) == OS_TYPE_SYMLINK){
+               name = follow_link(name);
+               if(IS_ERR(name)){
+                       err = PTR_ERR(name);
+                       goto out;
+               }
+       }
+       
+       err = read_name(ino, name);
+       kfree(name);
+ out:
+       return(err);
+}
+
+int hostfs_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;
+
+       err = do_statfs(HOSTFS_I(sb->s_root->d_inode)->host_filename,
+                       &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files,
+                       &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), 
+                       &sf->f_namelen, sf->f_spare);
+       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 = HOSTFS_SUPER_MAGIC;
+       return(0);
+}
+
+static struct inode *hostfs_alloc_inode(struct super_block *sb)
+{
+       struct hostfs_inode_info *hi;
+
+       hi = kmalloc(sizeof(*hi), GFP_KERNEL);
+       if(hi == NULL) 
+               return(NULL);
+
+       *hi = ((struct hostfs_inode_info) { .host_filename      = NULL,
+                                           .fd                 = -1,
+                                           .mode               = 0 });
+       inode_init_once(&hi->vfs_inode);
+       return(&hi->vfs_inode);
+}
+
+static void hostfs_destroy_inode(struct inode *inode)
+{
+       if(HOSTFS_I(inode)->host_filename) 
+               kfree(HOSTFS_I(inode)->host_filename);
+
+       if(HOSTFS_I(inode)->fd != -1) 
+               close_file(&HOSTFS_I(inode)->fd);
+
+       kfree(HOSTFS_I(inode));
+}
+
+static void hostfs_read_inode(struct inode *inode)
+{
+       read_inode(inode);
+}
+
+static struct super_operations hostfs_sbops = { 
+       .alloc_inode    = hostfs_alloc_inode,
+       .destroy_inode  = hostfs_destroy_inode,
+       .read_inode     = hostfs_read_inode,
+       .statfs         = hostfs_statfs,
+};
+
+int hostfs_readdir(struct file *file, void *ent, filldir_t filldir)
+{
+       void *dir;
+       char *name;
+       unsigned long long next, ino;
+       int error, len;
+
+       name = dentry_name(file->f_dentry, 0);
+       if(name == NULL) return(-ENOMEM);
+       dir = open_dir(name, &error);
+       kfree(name);
+       if(dir == NULL) return(-error);
+       next = file->f_pos;
+       while((name = read_dir(dir, &next, &ino, &len)) != NULL){
+               error = (*filldir)(ent, name, len, file->f_pos, 
+                                  ino, DT_UNKNOWN);
+               if(error) break;
+               file->f_pos = next;
+       }
+       close_dir(dir);
+       return(0);
+}
+
+int hostfs_file_open(struct inode *ino, struct file *file)
+{
+       char *name;
+       int mode = 0, r = 0, w = 0, fd;
+
+       mode = file->f_mode & (FMODE_READ | FMODE_WRITE);
+       if((mode & HOSTFS_I(ino)->mode) == mode)
+               return(0);
+
+       /* The file may already have been opened, but with the wrong access,
+        * so this resets things and reopens the file with the new access.
+        */
+       if(HOSTFS_I(ino)->fd != -1){
+               close_file(&HOSTFS_I(ino)->fd);
+               HOSTFS_I(ino)->fd = -1;
+       }
+
+       HOSTFS_I(ino)->mode |= mode;
+       if(HOSTFS_I(ino)->mode & FMODE_READ) 
+               r = 1;
+       if(HOSTFS_I(ino)->mode & FMODE_WRITE) 
+               w = 1;
+       if(w) 
+               r = 1;
+
+       name = dentry_name(file->f_dentry, 0);
+       if(name == NULL) 
+               return(-ENOMEM);
+
+       fd = open_file(name, r, w, append);
+       kfree(name);
+       if(fd < 0) return(fd);
+       FILE_HOSTFS_I(file)->fd = fd;
+
+       return(0);
+}
+
+int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+       return(0);
+}
+
+static struct file_operations hostfs_file_fops = {
+       .llseek         = generic_file_llseek,
+       .read           = generic_file_read,
+       .write          = generic_file_write,
+       .mmap           = generic_file_mmap,
+       .open           = hostfs_file_open,
+       .release        = NULL,
+       .fsync          = hostfs_fsync,
+};
+
+static struct file_operations hostfs_dir_fops = {
+       .readdir        = hostfs_readdir,
+       .read           = generic_read_dir,
+};
+
+int hostfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+       struct address_space *mapping = page->mapping;
+       struct inode *inode = mapping->host;
+       char *buffer;
+       unsigned long long base;
+       int count = PAGE_CACHE_SIZE;
+       int end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+       int err;
+
+       if (page->index >= end_index)
+               count = inode->i_size & (PAGE_CACHE_SIZE-1);
+
+       buffer = kmap(page);
+       base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;
+
+       err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count);
+       if(err != count){
+               ClearPageUptodate(page);
+               goto out;
+       }
+
+       if (base > inode->i_size)
+               inode->i_size = base;
+
+       if (PageError(page))
+               ClearPageError(page);   
+       err = 0;
+
+ out:  
+       kunmap(page);
+
+       unlock_page(page);
+       return err; 
+}
+
+int hostfs_readpage(struct file *file, struct page *page)
+{
+       char *buffer;
+       long long start;
+       int err = 0;
+
+       start = (long long) page->index << PAGE_CACHE_SHIFT;
+       buffer = kmap(page);
+       err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer,
+                       PAGE_CACHE_SIZE);
+       if(err < 0) goto out;
+
+       memset(&buffer[err], 0, PAGE_CACHE_SIZE - err);
+
+       flush_dcache_page(page);
+       SetPageUptodate(page);
+       if (PageError(page)) ClearPageError(page);
+       err = 0;
+ out:
+       kunmap(page);
+       unlock_page(page);
+       return(err);
+}
+
+int hostfs_prepare_write(struct file *file, struct page *page, 
+                        unsigned int from, unsigned int to)
+{
+       char *buffer;
+       long long start, tmp;
+       int err;
+
+       start = (long long) page->index << PAGE_CACHE_SHIFT;
+       buffer = kmap(page);
+       if(from != 0){
+               tmp = start;
+               err = read_file(FILE_HOSTFS_I(file)->fd, &tmp, buffer,
+                               from);
+               if(err < 0) goto out;
+       }
+       if(to != PAGE_CACHE_SIZE){
+               start += to;
+               err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer + to,
+                               PAGE_CACHE_SIZE - to);
+               if(err < 0) goto out;           
+       }
+       err = 0;
+ out:
+       kunmap(page);
+       return(err);
+}
+
+int hostfs_commit_write(struct file *file, struct page *page, unsigned from,
+                unsigned to)
+{
+       struct address_space *mapping = page->mapping;
+       struct inode *inode = mapping->host;
+       char *buffer;
+       long long start;
+       int err = 0;
+
+       start = (long long) (page->index << PAGE_CACHE_SHIFT) + from;
+       buffer = kmap(page);
+       err = write_file(FILE_HOSTFS_I(file)->fd, &start, buffer + from, 
+                        to - from);
+       if(err > 0) err = 0;
+       if(!err && (start > inode->i_size))
+               inode->i_size = start;
+
+       kunmap(page);
+       return(err);
+}
+
+static struct address_space_operations hostfs_aops = {
+       .writepage      = hostfs_writepage,
+       .readpage       = hostfs_readpage,
+/*     .set_page_dirty = __set_page_dirty_nobuffers, */
+       .prepare_write  = hostfs_prepare_write,
+       .commit_write   = hostfs_commit_write
+};
+
+static int init_inode(struct inode *inode, struct dentry *dentry)
+{
+       char *name;
+       int type, err = -ENOMEM, rdev;
+
+       if(dentry){
+               name = dentry_name(dentry, 0);
+               if(name == NULL)
+                       goto out;
+               type = file_type(name, &rdev);
+               kfree(name);
+       }
+       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 = &hostfs_dir_iops;
+       else inode->i_op = &hostfs_iops;
+
+       if(type == OS_TYPE_DIR) inode->i_fop = &hostfs_dir_fops;
+       else inode->i_fop = &hostfs_file_fops;
+
+       if(type == OS_TYPE_SYMLINK) 
+               inode->i_mapping->a_ops = &hostfs_link_aops;
+       else inode->i_mapping->a_ops = &hostfs_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;
+       }
+ out:
+       return(err);
+}
+
+int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, 
+                 struct nameidata *nd)
+{
+       struct inode *inode;
+       char *name;
+       int error, fd;
+
+       error = -ENOMEM;
+       inode = iget(dir->i_sb, 0);
+       if(inode == NULL) goto out;
+
+       error = init_inode(inode, dentry);
+       if(error) 
+               goto out_put;
+       
+       error = -ENOMEM;
+       name = dentry_name(dentry, 0);
+       if(name == NULL)
+               goto out_put;
+
+       fd = file_create(name, 
+                        mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR, 
+                        mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP, 
+                        mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH);
+       if(fd < 0) 
+               error = fd;
+       else error = read_name(inode, name);
+
+       kfree(name);
+       if(error)
+               goto out_put;
+
+       HOSTFS_I(inode)->fd = fd;
+       HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
+       d_instantiate(dentry, inode);
+       return(0);
+
+ out_put:
+       iput(inode);
+ out:
+       return(error);
+}
+
+struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, 
+                            struct nameidata *nd)
+{
+       struct inode *inode;
+       char *name;
+       int err;
+
+       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 == -ENOENT){
+               iput(inode);
+               inode = NULL;
+       }
+       else if(err)
+               goto out_put;
+
+       d_add(dentry, inode);
+       dentry->d_op = &hostfs_dentry_ops;
+       return(NULL);
+
+ out_put:
+       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 hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
+{
+        char *from_name, *to_name;
+        int err;
+
+        if((from_name = inode_dentry_name(ino, from)) == NULL) 
+                return(-ENOMEM);
+        to_name = dentry_name(to, 0);
+       if(to_name == NULL){
+               kfree(from_name);
+               return(-ENOMEM);
+       }
+        err = link_file(to_name, from_name);
+        kfree(from_name);
+        kfree(to_name);
+        return(err);
+}
+
+int hostfs_unlink(struct inode *ino, struct dentry *dentry)
+{
+       char *file;
+       int err;
+
+       if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM);
+       if(append)
+               return(-EPERM);
+
+       err = unlink_file(file);
+       kfree(file);
+       return(err);
+}
+
+int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to)
+{
+       char *file;
+       int err;
+
+       if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM);
+       err = make_symlink(file, to);
+       kfree(file);
+       return(err);
+}
+
+int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode)
+{
+       char *file;
+       int err;
+
+       if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM);
+       err = do_mkdir(file, mode);
+       kfree(file);
+       return(err);
+}
+
+int hostfs_rmdir(struct inode *ino, struct dentry *dentry)
+{
+       char *file;
+       int err;
+
+       if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM);
+       err = do_rmdir(file);
+       kfree(file);
+       return(err);
+}
+
+int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+       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 = do_mknod(name, mode, dev);
+       if(err)
+               goto out_free;
+
+       err = read_name(inode, name);
+       kfree(name);
+       if(err)
+               goto out_put;
+
+       d_instantiate(dentry, inode);
+       return(0);
+
+ out_free:
+       kfree(name);
+ out_put:
+       iput(inode);
+ out:
+       return(err);
+}
+
+int hostfs_rename(struct inode *from_ino, struct dentry *from,
+                 struct inode *to_ino, struct dentry *to)
+{
+       char *from_name, *to_name;
+       int err;
+
+       if((from_name = inode_dentry_name(from_ino, from)) == NULL)
+               return(-ENOMEM);
+       if((to_name = inode_dentry_name(to_ino, to)) == NULL){
+               kfree(from_name);
+               return(-ENOMEM);
+       }
+       err = rename_file(from_name, to_name);
+       kfree(from_name);
+       kfree(to_name);
+       return(err);
+}
+
+void hostfs_truncate(struct inode *ino)
+{
+       not_implemented();
+}
+
+int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd)
+{
+       char *name;
+       int r = 0, w = 0, x = 0, err;
+
+       if(desired & MAY_READ) r = 1;
+       if(desired & MAY_WRITE) w = 1;
+       if(desired & MAY_EXEC) x = 1;
+       name = inode_name(ino, 0);
+       if(name == NULL) return(-ENOMEM);
+       err = access_file(name, r, w, x);
+       kfree(name);
+       if(!err) err = vfs_permission(ino, desired);
+       return(err);
+}
+
+int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+       struct hostfs_iattr attrs;
+       char *name;
+       int err;
+       
+       if(append) 
+               attr->ia_valid &= ~ATTR_SIZE;
+
+       attrs.ia_valid = 0;
+       if(attr->ia_valid & ATTR_MODE){
+               attrs.ia_valid |= HOSTFS_ATTR_MODE;
+               attrs.ia_mode = attr->ia_mode;
+       }
+       if(attr->ia_valid & ATTR_UID){
+               if((dentry->d_inode->i_sb->s_dev == ROOT_DEV) && 
+                  (attr->ia_uid == 0))
+                       attr->ia_uid = getuid();
+               attrs.ia_valid |= HOSTFS_ATTR_UID;
+               attrs.ia_uid = attr->ia_uid;
+       }
+       if(attr->ia_valid & ATTR_GID){
+               if((dentry->d_inode->i_sb->s_dev == ROOT_DEV) && 
+                  (attr->ia_gid == 0))
+                       attr->ia_gid = getuid();
+               attrs.ia_valid |= HOSTFS_ATTR_GID;
+               attrs.ia_gid = attr->ia_gid;
+       }
+       if(attr->ia_valid & ATTR_SIZE){
+               attrs.ia_valid |= HOSTFS_ATTR_SIZE;
+               attrs.ia_size = attr->ia_size;
+       }
+       if(attr->ia_valid & ATTR_ATIME){
+               attrs.ia_valid |= HOSTFS_ATTR_ATIME;
+               attrs.ia_atime = attr->ia_atime;
+       }
+       if(attr->ia_valid & ATTR_MTIME){
+               attrs.ia_valid |= HOSTFS_ATTR_MTIME;
+               attrs.ia_mtime = attr->ia_mtime;
+       }
+       if(attr->ia_valid & ATTR_CTIME){
+               attrs.ia_valid |= HOSTFS_ATTR_CTIME;
+               attrs.ia_ctime = attr->ia_ctime;
+       }
+       if(attr->ia_valid & ATTR_ATIME_SET){
+               attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET;
+       }
+       if(attr->ia_valid & ATTR_MTIME_SET){
+               attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET;
+       }
+       name = dentry_name(dentry, 0);
+       if(name == NULL) return(-ENOMEM);
+       err = set_attr(name, &attrs);
+       kfree(name);
+       if(err)
+               return(err);
+
+       return(inode_setattr(dentry->d_inode, attr));
+}
+
+int hostfs_getattr(struct vfsmount *mnt, struct dentry *dentry, 
+          struct kstat *stat)
+{
+       generic_fillattr(dentry->d_inode, stat);
+       return(0);
+}
+
+static struct inode_operations hostfs_iops = {
+       .create         = hostfs_create,
+       .link           = hostfs_link,
+       .unlink         = hostfs_unlink,
+       .symlink        = hostfs_symlink,
+       .mkdir          = hostfs_mkdir,
+       .rmdir          = hostfs_rmdir,
+       .mknod          = hostfs_mknod,
+       .rename         = hostfs_rename,
+       .truncate       = hostfs_truncate,
+       .permission     = hostfs_permission,
+       .setattr        = hostfs_setattr,
+       .getattr        = hostfs_getattr,
+};
+
+static struct inode_operations hostfs_dir_iops = {
+       .create         = hostfs_create,
+       .lookup         = hostfs_lookup,
+       .link           = hostfs_link,
+       .unlink         = hostfs_unlink,
+       .symlink        = hostfs_symlink,
+       .mkdir          = hostfs_mkdir,
+       .rmdir          = hostfs_rmdir,
+       .mknod          = hostfs_mknod,
+       .rename         = hostfs_rename,
+       .truncate       = hostfs_truncate,
+       .permission     = hostfs_permission,
+       .setattr        = hostfs_setattr,
+       .getattr        = hostfs_getattr,
+};
+
+int hostfs_link_readpage(struct file *file, struct page *page)
+{
+       char *buffer, *name;
+       long long start;
+       int err;
+
+       start = page->index << PAGE_CACHE_SHIFT;
+       buffer = kmap(page);
+       name = inode_name(page->mapping->host, 0);
+       if(name == NULL) return(-ENOMEM);
+       err = do_readlink(name, buffer, PAGE_CACHE_SIZE);
+       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 struct address_space_operations hostfs_link_aops = {
+       .readpage       = hostfs_link_readpage,
+};
+
+static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
+{
+       struct inode *root_inode;
+       char *name, *data = d;
+       int err;
+
+       sb->s_blocksize = 1024;
+       sb->s_blocksize_bits = 10;
+       sb->s_magic = HOSTFS_SUPER_MAGIC;
+       sb->s_op = &hostfs_sbops;
+
+       if((data == NULL) || (*data == '\0')) 
+               data = root_ino;
+
+       err = -ENOMEM;
+       name = kmalloc(strlen(data) + 1, GFP_KERNEL);
+       if(name == NULL) 
+               goto out;
+
+       strcpy(name, data);
+
+       root_inode = iget(sb, 0);
+       if(root_inode == NULL)
+               goto out_free;
+
+       err = init_inode(root_inode, NULL);
+       if(err)
+               goto out_put;
+
+       HOSTFS_I(root_inode)->host_filename = name;
+
+       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;
+
+       return(0);
+
+ out_put:
+       iput(root_inode);
+ out_free:
+       kfree(name);
+ out:
+       return(err);
+}
+
+static struct super_block *hostfs_read_sb(struct file_system_type *type,
+                                            int flags, const char *dev_name,
+                                            void *data)
+{
+       return(get_sb_nodev(type, flags, data, hostfs_fill_sb_common));
+}
+
+static struct file_system_type hostfs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "hostfs",
+       .get_sb         = hostfs_read_sb,
+       .kill_sb        = kill_anon_super,
+       .fs_flags       = 0,
+};
+
+static int __init init_hostfs(void)
+{
+       return(register_filesystem(&hostfs_type));
+}
+
+static void __exit exit_hostfs(void)
+{
+       unregister_filesystem(&hostfs_type);
+}
+
+module_init(init_hostfs)
+module_exit(exit_hostfs)
+MODULE_LICENSE("GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * 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/hostfs_user.c b/fs/hostfs/hostfs_user.c
new file mode 100644 (file)
index 0000000..c406266
--- /dev/null
@@ -0,0 +1,361 @@
+/* 
+ * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <utime.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include "hostfs.h"
+#include "kern_util.h"
+#include "user.h"
+
+int stat_file(const char *path, unsigned long long *inode_out, int *mode_out,
+             int *nlink_out, int *uid_out, int *gid_out, 
+             unsigned long long *size_out, struct timespec *atime_out,
+             struct timespec *mtime_out, struct timespec *ctime_out,
+             int *blksize_out, unsigned long long *blocks_out)
+{
+       struct stat64 buf;
+
+       if(lstat64(path, &buf) < 0) 
+               return(-errno);
+
+       /* See the Makefile for why STAT64_INO_FIELD is passed in
+        * by the build
+        */
+       if(inode_out != NULL) *inode_out = buf.STAT64_INO_FIELD;
+       if(mode_out != NULL) *mode_out = buf.st_mode;
+       if(nlink_out != NULL) *nlink_out = buf.st_nlink;
+       if(uid_out != NULL) *uid_out = buf.st_uid;
+       if(gid_out != NULL) *gid_out = buf.st_gid;
+       if(size_out != NULL) *size_out = buf.st_size;
+       if(atime_out != NULL) {
+               atime_out->tv_sec = buf.st_atime;
+               atime_out->tv_nsec = 0;
+       }
+       if(mtime_out != NULL) {
+               mtime_out->tv_sec = buf.st_mtime;
+               mtime_out->tv_nsec = 0;
+       }
+       if(ctime_out != NULL) {
+               ctime_out->tv_sec = buf.st_ctime;
+               ctime_out->tv_nsec = 0;
+       }
+       if(blksize_out != NULL) *blksize_out = buf.st_blksize;
+       if(blocks_out != NULL) *blocks_out = buf.st_blocks;
+       return(0);
+}
+
+int file_type(const char *path, int *rdev)
+{
+       struct stat64 buf;
+
+       if(lstat64(path, &buf) < 0) 
+               return(-errno);
+       if(rdev != NULL) 
+               *rdev = buf.st_rdev;
+
+       if(S_ISDIR(buf.st_mode)) return(OS_TYPE_DIR);
+       else if(S_ISLNK(buf.st_mode)) return(OS_TYPE_SYMLINK);
+       else if(S_ISCHR(buf.st_mode)) return(OS_TYPE_CHARDEV);
+       else if(S_ISBLK(buf.st_mode)) return(OS_TYPE_BLOCKDEV);
+       else if(S_ISFIFO(buf.st_mode))return(OS_TYPE_FIFO);
+       else if(S_ISSOCK(buf.st_mode))return(OS_TYPE_SOCK);
+       else return(OS_TYPE_FILE);
+}
+
+int access_file(char *path, int r, int w, int x)
+{
+       int mode = 0;
+
+       if(r) mode = R_OK;
+       if(w) mode |= W_OK;
+       if(x) mode |= X_OK;
+       if(access(path, mode) != 0) return(-errno);
+       else return(0);
+}
+
+int open_file(char *path, int r, int w, int append)
+{
+       int mode = 0, fd;
+
+       if(r && !w) 
+               mode = O_RDONLY;
+       else if(!r && w) 
+               mode = O_WRONLY;
+       else if(r && w) 
+               mode = O_RDWR;
+       else panic("Impossible mode in open_file");
+
+       if(append)
+               mode |= O_APPEND;
+       fd = open64(path, mode);
+       if(fd < 0) return(-errno);
+       else return(fd);
+}
+
+void *open_dir(char *path, int *err_out)
+{
+       DIR *dir;
+
+       dir = opendir(path);
+       *err_out = errno;
+       if(dir == NULL) return(NULL);
+       return(dir);
+}
+
+char *read_dir(void *stream, unsigned long long *pos, 
+              unsigned long long *ino_out, int *len_out)
+{
+       DIR *dir = stream;
+       struct dirent *ent;
+
+       seekdir(dir, *pos);
+       ent = readdir(dir);
+       if(ent == NULL) return(NULL);
+       *len_out = strlen(ent->d_name);
+       *ino_out = ent->d_ino;
+       *pos = telldir(dir);
+       return(ent->d_name);
+}
+
+int read_file(int fd, unsigned long long *offset, char *buf, int len)
+{
+       int n;
+
+       n = pread64(fd, buf, len, *offset);
+       if(n < 0) return(-errno);
+       *offset += n;
+       return(n);
+}
+
+int write_file(int fd, unsigned long long *offset, const char *buf, int len)
+{
+       int n;
+
+       n = pwrite64(fd, buf, len, *offset);
+       if(n < 0) return(-errno);
+       *offset += n;
+       return(n);
+}
+
+int lseek_file(int fd, long long offset, int whence)
+{
+       int ret;
+
+       ret = lseek64(fd, offset, whence);
+       if(ret < 0) return(-errno);
+       return(0);
+}
+
+void close_file(void *stream)
+{
+       close(*((int *) stream));
+}
+
+void close_dir(void *stream)
+{
+       closedir(stream);
+}
+
+int file_create(char *name, int ur, int uw, int ux, int gr, 
+               int gw, int gx, int or, int ow, int ox)
+{
+       int mode, fd;
+
+       mode = 0;
+       mode |= ur ? S_IRUSR : 0;
+       mode |= uw ? S_IWUSR : 0;
+       mode |= ux ? S_IXUSR : 0;
+       mode |= gr ? S_IRGRP : 0;
+       mode |= gw ? S_IWGRP : 0;
+       mode |= gx ? S_IXGRP : 0;
+       mode |= or ? S_IROTH : 0;
+       mode |= ow ? S_IWOTH : 0;
+       mode |= ox ? S_IXOTH : 0;
+       fd = open64(name, O_CREAT | O_RDWR, mode);
+       if(fd < 0) 
+               return(-errno);
+       return(fd);
+}
+
+int set_attr(const char *file, struct hostfs_iattr *attrs)
+{
+       struct utimbuf buf;
+       int err, ma;
+
+       if(attrs->ia_valid & HOSTFS_ATTR_MODE){
+               if(chmod(file, attrs->ia_mode) != 0) return(-errno);
+       }
+       if(attrs->ia_valid & HOSTFS_ATTR_UID){
+               if(chown(file, attrs->ia_uid, -1)) return(-errno);
+       }
+       if(attrs->ia_valid & HOSTFS_ATTR_GID){
+               if(chown(file, -1, attrs->ia_gid)) return(-errno);
+       }
+       if(attrs->ia_valid & HOSTFS_ATTR_SIZE){
+               if(truncate(file, attrs->ia_size)) return(-errno);
+       }
+       ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET;
+       if((attrs->ia_valid & ma) == ma){
+               buf.actime = attrs->ia_atime.tv_sec;
+               buf.modtime = attrs->ia_mtime.tv_sec;
+               if(utime(file, &buf) != 0) return(-errno);
+       }
+       else {
+               struct timespec ts;
+
+               if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){
+                       err = stat_file(file, NULL, NULL, NULL, NULL, NULL, 
+                                       NULL, NULL, &ts, NULL, NULL, NULL);
+                       if(err != 0) 
+                               return(err);
+                       buf.actime = attrs->ia_atime.tv_sec;
+                       buf.modtime = ts.tv_sec;
+                       if(utime(file, &buf) != 0) 
+                               return(-errno);
+               }
+               if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){
+                       err = stat_file(file, NULL, NULL, NULL, NULL, NULL, 
+                                       NULL, &ts, NULL, NULL, NULL, NULL);
+                       if(err != 0) 
+                               return(err);
+                       buf.actime = ts.tv_sec;
+                       buf.modtime = attrs->ia_mtime.tv_sec;
+                       if(utime(file, &buf) != 0) 
+                               return(-errno);
+               }
+       }
+       if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ;
+       if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){
+               err = stat_file(file, NULL, NULL, NULL, NULL, NULL, NULL, 
+                               &attrs->ia_atime, &attrs->ia_mtime, NULL, 
+                               NULL, NULL);
+               if(err != 0) return(err);
+       }
+       return(0);
+}
+
+int make_symlink(const char *from, const char *to)
+{
+       int err;
+
+       err = symlink(to, from);
+       if(err) return(-errno);
+       return(0);
+}
+
+int unlink_file(const char *file)
+{
+       int err;
+
+       err = unlink(file);
+       if(err) return(-errno);
+       return(0);
+}
+
+int do_mkdir(const char *file, int mode)
+{
+       int err;
+
+       err = mkdir(file, mode);
+       if(err) return(-errno);
+       return(0);
+}
+
+int do_rmdir(const char *file)
+{
+       int err;
+
+       err = rmdir(file);
+       if(err) return(-errno);
+       return(0);
+}
+
+int do_mknod(const char *file, int mode, int dev)
+{
+       int err;
+
+       err = mknod(file, mode, dev);
+       if(err) return(-errno);
+       return(0);
+}
+
+int link_file(const char *to, const char *from)
+{
+       int err;
+
+       err = link(to, from);
+       if(err) return(-errno);
+       return(0);
+}
+
+int do_readlink(char *file, char *buf, int size)
+{
+       int n;
+
+       n = readlink(file, buf, size);
+       if(n < 0) 
+               return(-errno);
+       if(n < size) 
+               buf[n] = '\0';
+       return(n);
+}
+
+int rename_file(char *from, char *to)
+{
+       int err;
+
+       err = rename(from, to);
+       if(err < 0) return(-errno);
+       return(0);      
+}
+
+int do_statfs(char *root, long *bsize_out, long long *blocks_out, 
+             long long *bfree_out, long long *bavail_out, 
+             long long *files_out, long long *ffree_out,
+             void *fsid_out, int fsid_size, long *namelen_out, 
+             long *spare_out)
+{
+       struct statfs64 buf;
+       int err;
+
+       err = statfs64(root, &buf);
+       if(err < 0) return(-errno);
+       *bsize_out = buf.f_bsize;
+       *blocks_out = buf.f_blocks;
+       *bfree_out = buf.f_bfree;
+       *bavail_out = buf.f_bavail;
+       *files_out = buf.f_files;
+       *ffree_out = buf.f_ffree;
+       memcpy(fsid_out, &buf.f_fsid, 
+              sizeof(buf.f_fsid) > fsid_size ? fsid_size : 
+              sizeof(buf.f_fsid));
+       *namelen_out = buf.f_namelen;
+       spare_out[0] = buf.f_spare[0];
+       spare_out[1] = buf.f_spare[1];
+       spare_out[2] = buf.f_spare[2];
+       spare_out[3] = buf.f_spare[3];
+       spare_out[4] = buf.f_spare[4];
+       spare_out[5] = buf.f_spare[5];
+       return(0);
+}
+
+/*
+ * 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/hppfs/Makefile b/fs/hppfs/Makefile
new file mode 100644 (file)
index 0000000..e67f038
--- /dev/null
@@ -0,0 +1,19 @@
+# 
+# Copyright (C) 2002, 2003 Jeff Dike (jdike@karaya.com)
+# Licensed under the GPL
+#
+
+hppfs-objs := hppfs_kern.o
+
+obj-y = 
+obj-$(CONFIG_HPPFS) += hppfs.o
+
+clean:
+
+modules:
+
+fastdep:
+
+dep:
+
+archmrproper: clean
diff --git a/fs/hppfs/hppfs_kern.c b/fs/hppfs/hppfs_kern.c
new file mode 100644 (file)
index 0000000..ebf08cb
--- /dev/null
@@ -0,0 +1,811 @@
+/* 
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/dcache.h>
+#include <linux/statfs.h>
+#include <asm/uaccess.h>
+#include <asm/fcntl.h>
+#include "os.h"
+
+static int init_inode(struct inode *inode, struct dentry *dentry);
+
+struct hppfs_data {
+       struct list_head list;
+       char contents[PAGE_SIZE - sizeof(struct list_head)];
+};
+
+struct hppfs_private {
+       struct file proc_file;
+       int host_fd;
+       loff_t len;
+       struct hppfs_data *contents;
+};
+
+struct hppfs_inode_info {
+        struct dentry *proc_dentry;
+       struct inode vfs_inode;
+};
+
+static inline struct hppfs_inode_info *HPPFS_I(struct inode *inode)
+{
+       return(list_entry(inode, struct hppfs_inode_info, vfs_inode));
+}
+
+#define HPPFS_SUPER_MAGIC 0xb00000ee
+
+static struct super_operations hppfs_sbops;
+
+static int is_pid(struct dentry *dentry)
+{
+       struct super_block *sb;
+       int i;
+
+       sb = dentry->d_sb;
+       if((sb->s_op != &hppfs_sbops) || (dentry->d_parent != sb->s_root))
+               return(0);
+
+       for(i = 0; i < dentry->d_name.len; i++){
+               if(!isdigit(dentry->d_name.name[i]))
+                       return(0);
+       }
+       return(1);
+}
+
+static char *dentry_name(struct dentry *dentry, int extra)
+{
+       struct dentry *parent;
+       char *root, *name;
+       const char *seg_name;
+       int len, seg_len;
+
+       len = 0;
+       parent = dentry;
+       while(parent->d_parent != parent){
+               if(is_pid(parent))
+                       len += strlen("pid") + 1;
+               else len += parent->d_name.len + 1;
+               parent = parent->d_parent;
+       }
+       
+       root = "proc";
+       len += strlen(root);
+       name = kmalloc(len + extra + 1, GFP_KERNEL);
+       if(name == NULL) return(NULL);
+
+       name[len] = '\0';
+       parent = dentry;
+       while(parent->d_parent != parent){
+               if(is_pid(parent)){
+                       seg_name = "pid";
+                       seg_len = strlen("pid");
+               }
+               else {
+                       seg_name = parent->d_name.name;
+                       seg_len = parent->d_name.len;
+               }
+
+               len -= seg_len + 1;
+               name[len] = '/';
+               strncpy(&name[len + 1], seg_name, seg_len);
+               parent = parent->d_parent;
+       }
+       strncpy(name, root, strlen(root));
+       return(name);
+}
+
+struct dentry_operations hppfs_dentry_ops = {
+};
+
+static int file_removed(struct dentry *dentry, const char *file)
+{
+       char *host_file;
+       int extra, fd;
+
+       extra = 0;
+       if(file != NULL) extra += strlen(file) + 1;
+
+       host_file = dentry_name(dentry, extra + strlen("/remove"));
+       if(host_file == NULL){
+               printk("file_removed : allocation failed\n");
+               return(-ENOMEM);
+       }
+
+       if(file != NULL){
+               strcat(host_file, "/");
+               strcat(host_file, file);
+       }
+       strcat(host_file, "/remove");
+
+       fd = os_open_file(host_file, of_read(OPENFLAGS()), 0);
+       kfree(host_file);
+       if(fd > 0){
+               os_close_file(fd);
+               return(1);
+       }
+       return(0);
+}
+
+static void hppfs_read_inode(struct inode *ino)
+{
+       struct inode *proc_ino;
+
+       if(HPPFS_I(ino)->proc_dentry == NULL)
+               return;
+
+       proc_ino = HPPFS_I(ino)->proc_dentry->d_inode;
+       ino->i_uid = proc_ino->i_uid;
+       ino->i_gid = proc_ino->i_gid;
+       ino->i_atime = proc_ino->i_atime;
+       ino->i_mtime = proc_ino->i_mtime;
+       ino->i_ctime = proc_ino->i_ctime;
+       ino->i_ino = proc_ino->i_ino;
+       ino->i_mode = proc_ino->i_mode;
+       ino->i_nlink = proc_ino->i_nlink;
+       ino->i_size = proc_ino->i_size;
+       ino->i_blksize = proc_ino->i_blksize;
+       ino->i_blocks = proc_ino->i_blocks;
+}
+
+static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry, 
+                                  struct nameidata *nd)
+{
+       struct dentry *proc_dentry, *new, *parent;
+       struct inode *inode;
+       int err, deleted;
+
+       deleted = file_removed(dentry, NULL);
+       if(deleted < 0)
+               return(ERR_PTR(deleted));
+       else if(deleted)
+               return(ERR_PTR(-ENOENT));
+
+       err = -ENOMEM;
+       parent = HPPFS_I(ino)->proc_dentry;
+       down(&parent->d_inode->i_sem);
+       proc_dentry = d_lookup(parent, &dentry->d_name);
+       if(proc_dentry == NULL){
+               proc_dentry = d_alloc(parent, &dentry->d_name);
+               if(proc_dentry == NULL){
+                       up(&parent->d_inode->i_sem);
+                       goto out;
+               }
+               new = (*parent->d_inode->i_op->lookup)(parent->d_inode, 
+                                                      proc_dentry, NULL);
+               if(new){
+                       dput(proc_dentry);
+                       proc_dentry = new;
+               }
+       }
+       up(&parent->d_inode->i_sem);
+
+       if(IS_ERR(proc_dentry))
+               return(proc_dentry);
+
+       inode = iget(ino->i_sb, 0);
+       if(inode == NULL) 
+               goto out_dput;
+
+       err = init_inode(inode, proc_dentry);
+       if(err) 
+               goto out_put;
+       
+       hppfs_read_inode(inode);
+
+       d_add(dentry, inode);
+       dentry->d_op = &hppfs_dentry_ops;
+       return(NULL);
+
+ out_put:
+       iput(inode);
+ out_dput:
+       dput(proc_dentry);
+ out:
+       return(ERR_PTR(err));
+}
+
+static struct inode_operations hppfs_file_iops = {
+};
+
+static ssize_t read_proc(struct file *file, char *buf, ssize_t count, 
+                        loff_t *ppos, int is_user)
+{
+       ssize_t (*read)(struct file *, char *, size_t, loff_t *);
+       ssize_t n;
+
+       read = file->f_dentry->d_inode->i_fop->read;
+
+       if(!is_user)
+               set_fs(KERNEL_DS);
+               
+       n = (*read)(file, buf, count, &file->f_pos);
+
+       if(!is_user)
+               set_fs(USER_DS);
+
+       if(ppos) *ppos = file->f_pos;
+       return(n);
+}
+
+static ssize_t hppfs_read_file(int fd, char *buf, ssize_t count)
+{
+       ssize_t n;
+       int cur, err;
+       char *new_buf;
+
+       n = -ENOMEM;
+       new_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if(new_buf == NULL){
+               printk("hppfs_read_file : kmalloc failed\n");
+               goto out;
+       }
+       n = 0;
+       while(count > 0){
+               cur = min_t(ssize_t, count, PAGE_SIZE);
+               err = os_read_file(fd, new_buf, cur);
+               if(err < 0){
+                       printk("hppfs_read : read failed, errno = %d\n",
+                              count);
+                       n = err;
+                       goto out_free;
+               }
+               else if(err == 0)
+                       break;
+
+               if(copy_to_user(buf, new_buf, err)){
+                       n = -EFAULT;
+                       goto out_free;
+               }
+               n += err;
+               count -= err;
+       }
+ out_free:
+       kfree(new_buf);
+ out:
+       return(n);
+}
+
+static ssize_t hppfs_read(struct file *file, char *buf, size_t count, 
+                         loff_t *ppos)
+{
+       struct hppfs_private *hppfs = file->private_data;
+       struct hppfs_data *data;
+       loff_t off;
+       int err;
+
+       if(hppfs->contents != NULL){
+               if(*ppos >= hppfs->len) return(0);
+
+               data = hppfs->contents;
+               off = *ppos;
+               while(off >= sizeof(data->contents)){
+                       data = list_entry(data->list.next, struct hppfs_data,
+                                         list);
+                       off -= sizeof(data->contents);
+               }
+
+               if(off + count > hppfs->len)
+                       count = hppfs->len - off;
+               copy_to_user(buf, &data->contents[off], count);
+               *ppos += count;
+       }
+       else if(hppfs->host_fd != -1){
+               err = os_seek_file(hppfs->host_fd, *ppos);
+               if(err){
+                       printk("hppfs_read : seek failed, errno = %d\n", err);
+                       return(err);
+               }
+               count = hppfs_read_file(hppfs->host_fd, buf, count);
+               if(count > 0)
+                       *ppos += count;
+       }
+       else count = read_proc(&hppfs->proc_file, buf, count, ppos, 1);
+
+       return(count);
+}
+
+static ssize_t hppfs_write(struct file *file, const char *buf, size_t len, 
+                          loff_t *ppos)
+{
+       struct hppfs_private *data = file->private_data;
+       struct file *proc_file = &data->proc_file;
+       ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
+       int err;
+
+       write = proc_file->f_dentry->d_inode->i_fop->write;
+
+       proc_file->f_pos = file->f_pos;
+       err = (*write)(proc_file, buf, len, &proc_file->f_pos);
+       file->f_pos = proc_file->f_pos;
+
+       return(err);
+}
+
+static int open_host_sock(char *host_file, int *filter_out)
+{
+       char *end;
+       int fd;
+
+       end = &host_file[strlen(host_file)];
+       strcpy(end, "/rw");
+       *filter_out = 1;
+       fd = os_connect_socket(host_file);
+       if(fd > 0)
+               return(fd);
+
+       strcpy(end, "/r");
+       *filter_out = 0;
+       fd = os_connect_socket(host_file);
+       return(fd);
+}
+
+static void free_contents(struct hppfs_data *head)
+{
+       struct hppfs_data *data;
+       struct list_head *ele, *next;
+
+       if(head == NULL) return;
+
+       list_for_each_safe(ele, next, &head->list){
+               data = list_entry(ele, struct hppfs_data, list);
+               kfree(data);
+       }
+       kfree(head);
+}
+
+static struct hppfs_data *hppfs_get_data(int fd, int filter, 
+                                        struct file *proc_file, 
+                                        struct file *hppfs_file, 
+                                        loff_t *size_out)
+{
+       struct hppfs_data *data, *new, *head;
+       int n, err;
+
+       err = -ENOMEM;
+       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       if(data == NULL){
+               printk("hppfs_get_data : head allocation failed\n");
+               goto failed;
+       }
+
+       INIT_LIST_HEAD(&data->list);
+
+       head = data;
+       *size_out = 0;
+
+       if(filter){
+               while((n = read_proc(proc_file, data->contents,
+                                    sizeof(data->contents), NULL, 0)) > 0)
+                       os_write_file(fd, data->contents, n);
+               err = os_shutdown_socket(fd, 0, 1);
+               if(err){
+                       printk("hppfs_get_data : failed to shut down "
+                              "socket\n");
+                       goto failed_free;
+               }
+       }
+       while(1){
+               n = os_read_file(fd, data->contents, sizeof(data->contents));
+               if(n < 0){
+                       err = n;
+                       printk("hppfs_get_data : read failed, errno = %d\n",
+                              err);
+                       goto failed_free;
+               }
+               else if(n == 0)
+                       break;
+
+               *size_out += n;
+
+               if(n < sizeof(data->contents))
+                       break;
+
+               new = kmalloc(sizeof(*data), GFP_KERNEL);
+               if(new == 0){
+                       printk("hppfs_get_data : data allocation failed\n");
+                       err = -ENOMEM;
+                       goto failed_free;
+               }
+       
+               INIT_LIST_HEAD(&new->list);
+               list_add(&new->list, &data->list);
+               data = new;
+       }
+       return(head);
+
+ failed_free:
+       free_contents(head);
+ failed:               
+       return(ERR_PTR(err));
+}
+
+static struct hppfs_private *hppfs_data(void)
+{
+       struct hppfs_private *data;
+
+       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       if(data == NULL)
+               return(data);
+
+       *data = ((struct hppfs_private ) { .host_fd             = -1,
+                                          .len                 = -1,
+                                          .contents            = NULL } );
+       return(data);
+}
+
+static int file_mode(int fmode)
+{
+       if(fmode == (FMODE_READ | FMODE_WRITE))
+               return(O_RDWR);
+       if(fmode == FMODE_READ)
+               return(O_RDONLY);
+       if(fmode == FMODE_WRITE)
+               return(O_WRONLY);
+       return(0);
+}
+
+static int hppfs_open(struct inode *inode, struct file *file)
+{
+       struct hppfs_private *data;
+       struct dentry *proc_dentry;
+       char *host_file;
+       int err, fd, type, filter;
+
+       err = -ENOMEM;
+       data = hppfs_data();
+       if(data == NULL)
+               goto out;
+
+       host_file = dentry_name(file->f_dentry, strlen("/rw"));
+       if(host_file == NULL)
+               goto out_free2;
+
+       proc_dentry = HPPFS_I(inode)->proc_dentry;
+
+       /* XXX This isn't closed anywhere */
+       err = open_private_file(&data->proc_file, proc_dentry, 
+                               file_mode(file->f_mode));
+       if(err)
+               goto out_free1;
+
+       type = os_file_type(host_file);
+       if(type == OS_TYPE_FILE){
+               fd = os_open_file(host_file, of_read(OPENFLAGS()), 0);
+               if(fd >= 0) 
+                       data->host_fd = fd;
+               else printk("hppfs_open : failed to open '%s', errno = %d\n",
+                           host_file, -fd);
+
+               data->contents = NULL;
+       }
+       else if(type == OS_TYPE_DIR){
+               fd = open_host_sock(host_file, &filter);
+               if(fd > 0){
+                       data->contents = hppfs_get_data(fd, filter, 
+                                                       &data->proc_file, 
+                                                       file, &data->len);
+                       if(!IS_ERR(data->contents))
+                               data->host_fd = fd;
+               }
+               else printk("hppfs_open : failed to open a socket in "
+                           "'%s', errno = %d\n", host_file, -fd);
+       }
+       kfree(host_file);
+
+       file->private_data = data;
+       return(0);
+
+ out_free1:
+       kfree(host_file);
+ out_free2:
+       free_contents(data->contents);
+       kfree(data);
+ out:
+       return(err);
+}
+
+static int hppfs_dir_open(struct inode *inode, struct file *file)
+{
+       struct hppfs_private *data;
+       struct dentry *proc_dentry;
+       int err;
+
+       err = -ENOMEM;
+       data = hppfs_data();
+       if(data == NULL)
+               goto out;
+
+       proc_dentry = HPPFS_I(inode)->proc_dentry;
+       err = open_private_file(&data->proc_file, proc_dentry, 
+                               file_mode(file->f_mode));
+       if(err)
+               goto out_free;
+
+       file->private_data = data;
+       return(0);
+
+ out_free:
+       kfree(data);
+ out:
+       return(err);
+}
+
+static loff_t hppfs_llseek(struct file *file, loff_t off, int where)
+{
+       struct hppfs_private *data = file->private_data;
+       struct file *proc_file = &data->proc_file;
+       loff_t (*llseek)(struct file *, loff_t, int);
+       loff_t ret;
+
+       llseek = proc_file->f_dentry->d_inode->i_fop->llseek;
+       if(llseek != NULL){
+               ret = (*llseek)(proc_file, off, where);
+               if(ret < 0)
+                       return(ret);
+       }
+
+       return(default_llseek(file, off, where));
+}
+
+static struct file_operations hppfs_file_fops = {
+       .owner          = NULL,
+       .llseek         = hppfs_llseek,
+       .read           = hppfs_read,
+       .write          = hppfs_write,
+       .open           = hppfs_open,
+};
+
+struct hppfs_dirent {
+       void *vfs_dirent;
+       filldir_t filldir;
+       struct dentry *dentry;
+};
+
+static int hppfs_filldir(void *d, const char *name, int size, 
+                        loff_t offset, ino_t inode, unsigned int type)
+{
+       struct hppfs_dirent *dirent = d;
+
+       if(file_removed(dirent->dentry, name))
+               return(0);
+
+       return((*dirent->filldir)(dirent->vfs_dirent, name, size, offset, 
+                                 inode, type));
+}
+
+static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir)
+{
+       struct hppfs_private *data = file->private_data;
+       struct file *proc_file = &data->proc_file;
+       int (*readdir)(struct file *, void *, filldir_t);
+       struct hppfs_dirent dirent = ((struct hppfs_dirent)
+                                     { .vfs_dirent     = ent,
+                                       .filldir        = filldir,
+                                       .dentry         = file->f_dentry } );
+       int err;
+
+       readdir = proc_file->f_dentry->d_inode->i_fop->readdir;
+
+       proc_file->f_pos = file->f_pos;
+       err = (*readdir)(proc_file, &dirent, hppfs_filldir);
+       file->f_pos = proc_file->f_pos;
+
+       return(err);
+}
+
+static int hppfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+       return(0);
+}
+
+static struct file_operations hppfs_dir_fops = {
+       .owner          = NULL,
+       .readdir        = hppfs_readdir,
+       .open           = hppfs_dir_open,
+       .fsync          = hppfs_fsync,
+};
+
+static int hppfs_statfs(struct super_block *sb, struct kstatfs *sf)
+{
+       sf->f_blocks = 0;
+       sf->f_bfree = 0;
+       sf->f_bavail = 0;
+       sf->f_files = 0;
+       sf->f_ffree = 0;
+       sf->f_type = HPPFS_SUPER_MAGIC;
+       return(0);
+}
+
+static struct inode *hppfs_alloc_inode(struct super_block *sb)
+{
+       struct hppfs_inode_info *hi;
+
+       hi = kmalloc(sizeof(*hi), GFP_KERNEL);
+       if(hi == NULL) 
+               return(NULL);
+
+       *hi = ((struct hppfs_inode_info) { .proc_dentry = NULL });
+       inode_init_once(&hi->vfs_inode);
+       return(&hi->vfs_inode);
+}
+
+void hppfs_delete_inode(struct inode *ino)
+{
+       clear_inode(ino);
+}
+
+static void hppfs_destroy_inode(struct inode *inode)
+{
+       kfree(HPPFS_I(inode));
+}
+
+static struct super_operations hppfs_sbops = { 
+       .alloc_inode    = hppfs_alloc_inode,
+       .destroy_inode  = hppfs_destroy_inode,
+       .read_inode     = hppfs_read_inode,
+       .delete_inode   = hppfs_delete_inode,
+       .statfs         = hppfs_statfs,
+};
+
+static int hppfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+       struct file proc_file;
+       struct dentry *proc_dentry;
+       int (*readlink)(struct dentry *, char *, int);
+       int err, n;
+
+       proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry;
+       err = open_private_file(&proc_file, proc_dentry, O_RDONLY);
+       if(err) 
+               return(err);
+
+       readlink = proc_dentry->d_inode->i_op->readlink;
+       n = (*readlink)(proc_dentry, buffer, buflen);
+
+       close_private_file(&proc_file);
+       
+       return(n);
+}
+
+static int hppfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+       struct file proc_file;
+       struct dentry *proc_dentry;
+       int (*follow_link)(struct dentry *, struct nameidata *);
+       int err, n;
+
+       proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry;
+       err = open_private_file(&proc_file, proc_dentry, O_RDONLY);
+       if(err) 
+               return(err);
+
+       follow_link = proc_dentry->d_inode->i_op->follow_link;
+       n = (*follow_link)(proc_dentry, nd);
+
+       close_private_file(&proc_file);
+       
+       return(n);
+}
+
+static struct inode_operations hppfs_dir_iops = {
+       .lookup         = hppfs_lookup,
+};
+
+static struct inode_operations hppfs_link_iops = {
+       .readlink       = hppfs_readlink,
+       .follow_link    = hppfs_follow_link,
+};
+
+static int init_inode(struct inode *inode, struct dentry *dentry)
+{
+       if(S_ISDIR(dentry->d_inode->i_mode)){
+               inode->i_op = &hppfs_dir_iops;
+               inode->i_fop = &hppfs_dir_fops;
+       }
+       else if(S_ISLNK(dentry->d_inode->i_mode)){
+               inode->i_op = &hppfs_link_iops;
+               inode->i_fop = &hppfs_file_fops;
+       }
+       else {
+               inode->i_op = &hppfs_file_iops;
+               inode->i_fop = &hppfs_file_fops;
+       }
+
+       HPPFS_I(inode)->proc_dentry = dentry;
+
+       return(0);
+}
+
+static int hppfs_fill_super(struct super_block *sb, void *d, int silent)
+{
+       struct inode *root_inode;
+       struct file_system_type *procfs;
+       struct super_block *proc_sb;
+       int err;
+
+       err = -ENOENT;
+       procfs = get_fs_type("proc");
+       if(procfs == NULL) 
+               goto out;
+
+       if(list_empty(&procfs->fs_supers))
+               goto out;
+
+       proc_sb = list_entry(procfs->fs_supers.next, struct super_block,
+                            s_instances);
+       
+       sb->s_blocksize = 1024;
+       sb->s_blocksize_bits = 10;
+       sb->s_magic = HPPFS_SUPER_MAGIC;
+       sb->s_op = &hppfs_sbops;
+
+       root_inode = iget(sb, 0);
+       if(root_inode == NULL)
+               goto out;
+
+       err = init_inode(root_inode, proc_sb->s_root);
+       if(err)
+               goto out_put;
+
+       err = -ENOMEM;
+       sb->s_root = d_alloc_root(root_inode);
+       if(sb->s_root == NULL)
+               goto out_put;
+
+       hppfs_read_inode(root_inode);
+
+       return(0);
+
+ out_put:
+       iput(root_inode);
+ out:
+       return(err);
+}
+
+static struct super_block *hppfs_read_super(struct file_system_type *type,
+                                            int flags, const char *dev_name,
+                                            void *data)
+{
+       return(get_sb_nodev(type, flags, data, hppfs_fill_super));
+}
+
+static struct file_system_type hppfs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "hppfs",
+       .get_sb         = hppfs_read_super,
+       .kill_sb        = kill_anon_super,
+       .fs_flags       = 0,
+};
+
+static int __init init_hppfs(void)
+{
+       return(register_filesystem(&hppfs_type));
+}
+
+static void __exit exit_hppfs(void)
+{
+       unregister_filesystem(&hppfs_type);
+}
+
+module_init(init_hppfs)
+module_exit(exit_hppfs)
+MODULE_LICENSE("GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/include/asm-um/cpufeature.h b/include/asm-um/cpufeature.h
new file mode 100644 (file)
index 0000000..fb7bd42
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __UM_CPUFEATURE_H
+#define __UM_CPUFEATURE_H
+
+#include "asm/arch/cpufeature.h"
+
+#endif
diff --git a/include/asm-um/local.h b/include/asm-um/local.h
new file mode 100644 (file)
index 0000000..9a280c5
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __UM_LOCAL_H
+#define __UM_LOCAL_H
+
+#include "asm/arch/local.h"
+
+#endif
diff --git a/include/asm-um/module-generic.h b/include/asm-um/module-generic.h
new file mode 100644 (file)
index 0000000..5a265f5
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __UM_MODULE_GENERIC_H
+#define __UM_MODULE_GENERIC_H
+
+#include "asm/arch/module.h"
+
+#endif
diff --git a/include/asm-um/sections.h b/include/asm-um/sections.h
new file mode 100644 (file)
index 0000000..6b0231e
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _UM_SECTIONS_H
+#define _UM_SECTIONS_H
+
+/* nothing to see, move along */
+#include <asm-generic/sections.h>
+
+#endif
diff --git a/include/linux/ghash.h b/include/linux/ghash.h
new file mode 100644 (file)
index 0000000..a247988
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * include/linux/ghash.h -- generic hashing with fuzzy retrieval
+ *
+ * (C) 1997 Thomas Schoebel-Theuer
+ *
+ * The algorithms implemented here seem to be a completely new invention,
+ * and I'll publish the fundamentals in a paper.
+ */
+
+#ifndef _GHASH_H
+#define _GHASH_H
+/* HASHSIZE _must_ be a power of two!!! */
+
+
+#define DEF_HASH_FUZZY_STRUCTS(NAME,HASHSIZE,TYPE) \
+\
+struct NAME##_table {\
+       TYPE * hashtable[HASHSIZE];\
+       TYPE * sorted_list;\
+       int nr_entries;\
+};\
+\
+struct NAME##_ptrs {\
+       TYPE * next_hash;\
+       TYPE * prev_hash;\
+       TYPE * next_sorted;\
+       TYPE * prev_sorted;\
+};
+
+#define DEF_HASH_FUZZY(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\
+\
+LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+       int ix = HASHFN(elem->KEY);\
+       TYPE ** base = &tbl->hashtable[ix];\
+       TYPE * ptr = *base;\
+       TYPE * prev = NULL;\
+\
+       tbl->nr_entries++;\
+       while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\
+               base = &ptr->PTRS.next_hash;\
+               prev = ptr;\
+               ptr = *base;\
+       }\
+       elem->PTRS.next_hash = ptr;\
+       elem->PTRS.prev_hash = prev;\
+       if(ptr) {\
+               ptr->PTRS.prev_hash = elem;\
+       }\
+       *base = elem;\
+\
+       ptr = prev;\
+       if(!ptr) {\
+               ptr = tbl->sorted_list;\
+               prev = NULL;\
+       } else {\
+               prev = ptr->PTRS.prev_sorted;\
+       }\
+       while(ptr) {\
+               TYPE * next = ptr->PTRS.next_hash;\
+               if(next && KEYCMP(next->KEY, elem->KEY)) {\
+                       prev = ptr;\
+                       ptr = next;\
+               } else if(KEYCMP(ptr->KEY, elem->KEY)) {\
+                       prev = ptr;\
+                       ptr = ptr->PTRS.next_sorted;\
+               } else\
+                       break;\
+       }\
+       elem->PTRS.next_sorted = ptr;\
+       elem->PTRS.prev_sorted = prev;\
+       if(ptr) {\
+               ptr->PTRS.prev_sorted = elem;\
+       }\
+       if(prev) {\
+               prev->PTRS.next_sorted = elem;\
+       } else {\
+               tbl->sorted_list = elem;\
+       }\
+}\
+\
+LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+       TYPE * next = elem->PTRS.next_hash;\
+       TYPE * prev = elem->PTRS.prev_hash;\
+\
+       tbl->nr_entries--;\
+       if(next)\
+               next->PTRS.prev_hash = prev;\
+       if(prev)\
+               prev->PTRS.next_hash = next;\
+       else {\
+               int ix = HASHFN(elem->KEY);\
+               tbl->hashtable[ix] = next;\
+       }\
+\
+       next = elem->PTRS.next_sorted;\
+       prev = elem->PTRS.prev_sorted;\
+       if(next)\
+               next->PTRS.prev_sorted = prev;\
+       if(prev)\
+               prev->PTRS.next_sorted = next;\
+       else\
+               tbl->sorted_list = next;\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+       int ix = hashfn(pos);\
+       TYPE * ptr = tbl->hashtable[ix];\
+       while(ptr && KEYCMP(ptr->KEY, pos))\
+               ptr = ptr->PTRS.next_hash;\
+       if(ptr && !KEYEQ(ptr->KEY, pos))\
+               ptr = NULL;\
+       return ptr;\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash_fuzzy(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+       int ix;\
+       int offset;\
+       TYPE * ptr;\
+       TYPE * next;\
+\
+       ptr = tbl->sorted_list;\
+       if(!ptr || KEYCMP(pos, ptr->KEY))\
+               return NULL;\
+       ix = HASHFN(pos);\
+       offset = HASHSIZE;\
+       do {\
+               offset >>= 1;\
+               next = tbl->hashtable[(ix+offset) & ((HASHSIZE)-1)];\
+               if(next && (KEYCMP(next->KEY, pos) || KEYEQ(next->KEY, pos))\
+                  && KEYCMP(ptr->KEY, next->KEY))\
+                       ptr = next;\
+       } while(offset);\
+\
+       for(;;) {\
+               next = ptr->PTRS.next_hash;\
+               if(next) {\
+                       if(KEYCMP(next->KEY, pos)) {\
+                               ptr = next;\
+                               continue;\
+                       }\
+               }\
+               next = ptr->PTRS.next_sorted;\
+               if(next && KEYCMP(next->KEY, pos)) {\
+                       ptr = next;\
+                       continue;\
+               }\
+               return ptr;\
+       }\
+       return NULL;\
+}
+
+/* LINKAGE - empty or "static", depending on whether you want the definitions to
+ *     be public or not
+ * NAME - a string to stick in names to make this hash table type distinct from
+ *     any others
+ * HASHSIZE - number of buckets
+ * TYPE - type of data contained in the buckets - must be a structure, one 
+ *     field is of type NAME_ptrs, another is the hash key
+ * PTRS - TYPE must contain a field of type NAME_ptrs, PTRS is the name of that
+ *     field
+ * KEYTYPE - type of the key field within TYPE
+ * KEY - name of the key field within TYPE
+ * KEYCMP - pointer to function that compares KEYTYPEs to each other - the
+ *     prototype is int KEYCMP(KEYTYPE, KEYTYPE), it returns zero for equal, 
+ *     non-zero for not equal
+ * HASHFN - the hash function - the prototype is int HASHFN(KEYTYPE),
+ *     it returns a number in the range 0 ... HASHSIZE - 1
+ * Call DEF_HASH_STRUCTS, define your hash table as a NAME_table, then call
+ * DEF_HASH.
+ */
+
+#define DEF_HASH_STRUCTS(NAME,HASHSIZE,TYPE) \
+\
+struct NAME##_table {\
+       TYPE * hashtable[HASHSIZE];\
+       int nr_entries;\
+};\
+\
+struct NAME##_ptrs {\
+       TYPE * next_hash;\
+       TYPE * prev_hash;\
+};
+
+#define DEF_HASH(LINKAGE,NAME,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,HASHFN)\
+\
+LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+       int ix = HASHFN(elem->KEY);\
+       TYPE ** base = &tbl->hashtable[ix];\
+       TYPE * ptr = *base;\
+       TYPE * prev = NULL;\
+\
+       tbl->nr_entries++;\
+       while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\
+               base = &ptr->PTRS.next_hash;\
+               prev = ptr;\
+               ptr = *base;\
+       }\
+       elem->PTRS.next_hash = ptr;\
+       elem->PTRS.prev_hash = prev;\
+       if(ptr) {\
+               ptr->PTRS.prev_hash = elem;\
+       }\
+       *base = elem;\
+}\
+\
+LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+       TYPE * next = elem->PTRS.next_hash;\
+       TYPE * prev = elem->PTRS.prev_hash;\
+\
+       tbl->nr_entries--;\
+       if(next)\
+               next->PTRS.prev_hash = prev;\
+       if(prev)\
+               prev->PTRS.next_hash = next;\
+       else {\
+               int ix = HASHFN(elem->KEY);\
+               tbl->hashtable[ix] = next;\
+       }\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+       int ix = HASHFN(pos);\
+       TYPE * ptr = tbl->hashtable[ix];\
+       while(ptr && KEYCMP(ptr->KEY, pos))\
+               ptr = ptr->PTRS.next_hash;\
+       return ptr;\
+}
+
+#endif
diff --git a/include/linux/proc_mm.h b/include/linux/proc_mm.h
new file mode 100644 (file)
index 0000000..254f8b4
--- /dev/null
@@ -0,0 +1,48 @@
+/* 
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __PROC_MM_H
+#define __PROC_MM_H
+
+#include "linux/sched.h"
+
+#define MM_MMAP 54
+#define MM_MUNMAP 55
+#define MM_MPROTECT 56
+#define MM_COPY_SEGMENTS 57
+
+struct mm_mmap {
+       unsigned long addr;
+       unsigned long len;
+       unsigned long prot;
+       unsigned long flags;
+       unsigned long fd;
+       unsigned long offset;
+};
+
+struct mm_munmap {
+       unsigned long addr;
+       unsigned long len;      
+};
+
+struct mm_mprotect {
+       unsigned long addr;
+       unsigned long len;
+        unsigned int prot;
+};
+
+struct proc_mm_op {
+       int op;
+       union {
+               struct mm_mmap mmap;
+               struct mm_munmap munmap;
+               struct mm_mprotect mprotect;
+               int copy_segments;
+       } u;
+};
+
+extern struct mm_struct *proc_mm_get_mm(int fd);
+
+#endif
diff --git a/mm/proc_mm.c b/mm/proc_mm.c
new file mode 100644 (file)
index 0000000..5ceb506
--- /dev/null
@@ -0,0 +1,174 @@
+/* 
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/mm.h"
+#include "linux/init.h"
+#include "linux/proc_fs.h"
+#include "linux/proc_mm.h"
+#include "linux/file.h"
+#include "asm/uaccess.h"
+#include "asm/mmu_context.h"
+
+static struct file_operations proc_mm_fops;
+
+struct mm_struct *proc_mm_get_mm(int fd)
+{
+       struct mm_struct *ret = ERR_PTR(-EBADF);
+       struct file *file;
+
+       file = fget(fd);
+       if (!file)
+               goto out;
+
+       ret = ERR_PTR(-EINVAL);
+       if(file->f_op != &proc_mm_fops)
+               goto out_fput;
+
+       ret = file->private_data;
+ out_fput:
+       fput(file);
+ out:
+       return(ret);
+}
+
+extern long do_mmap2(struct mm_struct *mm, unsigned long addr, 
+                    unsigned long len, unsigned long prot, 
+                    unsigned long flags, unsigned long fd,
+                    unsigned long pgoff);
+
+static ssize_t write_proc_mm(struct file *file, const char *buffer,
+                            size_t count, loff_t *ppos)
+{
+       struct mm_struct *mm = file->private_data;
+       struct proc_mm_op req;
+       int n, ret;
+
+       if(count > sizeof(req))
+               return(-EINVAL);
+
+       n = copy_from_user(&req, buffer, count);
+       if(n != 0)
+               return(-EFAULT);
+
+       ret = count;
+       switch(req.op){
+       case MM_MMAP: {
+               struct mm_mmap *map = &req.u.mmap;
+
+               ret = do_mmap2(mm, map->addr, map->len, map->prot, 
+                              map->flags, map->fd, map->offset >> PAGE_SHIFT);
+               if((ret & ~PAGE_MASK) == 0)
+                       ret = count;
+       
+               break;
+       }
+       case MM_MUNMAP: {
+               struct mm_munmap *unmap = &req.u.munmap;
+
+               down_write(&mm->mmap_sem);
+               ret = do_munmap(mm, unmap->addr, unmap->len);
+               up_write(&mm->mmap_sem);
+
+               if(ret == 0)
+                       ret = count;
+               break;
+       }
+       case MM_MPROTECT: {
+               struct mm_mprotect *protect = &req.u.mprotect;
+
+               ret = do_mprotect(mm, protect->addr, protect->len, 
+                                 protect->prot);
+               if(ret == 0)
+                       ret = count;
+               break;
+       }
+
+       case MM_COPY_SEGMENTS: {
+               struct mm_struct *from = proc_mm_get_mm(req.u.copy_segments);
+
+               if(IS_ERR(from)){
+                       ret = PTR_ERR(from);
+                       break;
+               }
+
+               mm_copy_segments(from, mm);
+               break;
+       }
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return(ret);
+}
+
+static int open_proc_mm(struct inode *inode, struct file *file)
+{
+       struct mm_struct *mm = mm_alloc();
+       int ret;
+
+       ret = -ENOMEM;
+       if(mm == NULL)
+               goto out_mem;
+
+       ret = init_new_context(current, mm);
+       if(ret)
+               goto out_free;
+
+       spin_lock(&mmlist_lock);
+       list_add(&mm->mmlist, &current->mm->mmlist);
+       mmlist_nr++;
+       spin_unlock(&mmlist_lock);
+
+       file->private_data = mm;
+
+       return(0);
+
+ out_free:
+       mmput(mm);
+ out_mem:
+       return(ret);
+}
+
+static int release_proc_mm(struct inode *inode, struct file *file)
+{
+       struct mm_struct *mm = file->private_data;
+
+       mmput(mm);
+       return(0);
+}
+
+static struct file_operations proc_mm_fops = {
+       .open           = open_proc_mm,
+       .release        = release_proc_mm,
+       .write          = write_proc_mm,
+};
+
+static int make_proc_mm(void)
+{
+       struct proc_dir_entry *ent;
+
+       ent = create_proc_entry("mm", 0222, &proc_root);
+       if(ent == NULL){
+               printk("make_proc_mm : Failed to register /proc/mm\n");
+               return(0);
+       }
+       ent->proc_fops = &proc_mm_fops;
+
+       return(0);
+}
+
+__initcall(make_proc_mm);
+
+/*
+ * 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:
+ */