Hi,

here is the latest version of my tmpfs patch against 2.4.1

Have fun
                Christoph

diff -uNr 2.4.1/Documentation/Changes 2.4.1-tmpfs/Documentation/Changes
--- 2.4.1/Documentation/Changes Tue Jan 30 11:06:59 2001
+++ 2.4.1-tmpfs/Documentation/Changes   Thu Feb  1 22:04:13 2001
@@ -114,20 +114,6 @@
 DevFS is now in the kernel.  See Documentation/filesystems/devfs/* in
 the kernel source tree for all the gory details.
 
-System V shared memory is now implemented via a virtual filesystem.
-You do not have to mount it to use it. SYSV shared memory limits are
-set via /proc/sys/kernel/shm{max,all,mni}.  You should mount the
-filesystem under /dev/shm to be able to use POSIX shared
-memory. Adding the following line to /etc/fstab should take care of
-things:
-
-none           /dev/shm        shm             defaults        0 0
-
-Remember to create the directory that you intend to mount shm on if
-necessary (The entry is automagically created if you use devfs). You
-can set limits for the number of blocks and inodes used by the
-filesystem with the mount options nr_blocks and nr_inodes.
-
 The Logical Volume Manager (LVM) is now in the kernel.  If you want to
 use this, you'll need to install the necessary LVM toolset.
 
diff -uNr 2.4.1/Documentation/Configure.help 2.4.1-tmpfs/Documentation/Configure.help
--- 2.4.1/Documentation/Configure.help  Tue Jan 30 11:06:59 2001
+++ 2.4.1-tmpfs/Documentation/Configure.help    Thu Feb  1 22:06:30 2001
@@ -2739,14 +2739,6 @@
   section 6.4 of the Linux Programmer's Guide, available from
   http://www.linuxdoc.org/docs.html#guide .
 
-  Shared memory is now implemented using a new (minimal) virtual file
-  system. To mount it automatically at system startup just add the
-  following line to your /etc/fstab:
-
-  none /dev/shm        shm     defaults        0 0
-
-  Saying Y here enlarges your kernel by about 18 KB. Just say Y.
-
 BSD Process Accounting
 CONFIG_BSD_PROCESS_ACCT
   If you say Y here, a user level program will be able to instruct the
@@ -10914,23 +10906,44 @@
 
   If unsure, say N.
    
+Virtual memory file system support
+CONFIG_TMPFS
+  Tmpfs is a file system which keeps all files in virtual memory.
+
+  In contrast to RAM disks, which get allocated a fixed amount of
+  physical RAM, tmpfs grows and shrinks to accommodate the files it
+  contains and is able to swap unneeded pages out to swap space.
+
+  Everything is "virtual" in the sense that no files will be created
+  on your hard drive; if you reboot, everything in tmpfs will be
+  lost.
+  
+  You should mount the filesystem somewhere to be able to use
+  POSIX shared memory. Adding the following line to /etc/fstab should
+  take care of things:
+
+  tmpfs                /dev/shm        tmpfs           defaults        0 0
+
+  Remember to create the directory that you intend to mount tmpfs on
+  if necessary (/dev/shm is automagically created if you use devfs).
+
+  You can set limits for the number of blocks and inodes used by the
+  filesystem with the mount options "size", "nr_blocks" and
+  "nr_inodes". These parameters accept a suffix k, m or g for kilo,
+  mega and giga and can be changed on remount.
+  
+  The initial permissions of the root directory can be set with the
+  mount option "mode".
+
 Simple RAM-based file system support
 CONFIG_RAMFS
   Ramfs is a file system which keeps all files in RAM. It allows
   read and write access.
 
-  In contrast to RAM disks, which get allocated a fixed amount of RAM,
-  ramfs grows and shrinks to accommodate the files it contains.
+  It is more of an programming example than a useable filesystem. If
+  you need a file system which lives in RAM with limit checking use
+  tmpfs.
 
-  Before you can use this RAM-based file system, it has to be mounted,
-  meaning it has to be given a location in the directory hierarchy. If
-  you want to use the location /ramfiles for example, you would have
-  to create that directory first and then mount the file system by
-  saying "mount -t ramfs ramfs /ramfiles" or the equivalent line in
-  /etc/fstab. Everything is "virtual" in the sense that no files will
-  be created on your hard drive; if you reboot, everything in
-  /ramfiles will be lost.
-  
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt. The module will be
diff -uNr 2.4.1/arch/i386/kernel/setup.c 2.4.1-tmpfs/arch/i386/kernel/setup.c
--- 2.4.1/arch/i386/kernel/setup.c      Tue Jan 30 11:07:00 2001
+++ 2.4.1-tmpfs/arch/i386/kernel/setup.c        Thu Feb  1 22:02:48 2001
@@ -559,7 +559,7 @@
                                 * blow away any automatically generated
                                 * size
                                 */
-                               unsigned long start_at, mem_size;
+                               unsigned long long start_at, mem_size;
  
                                if (usermem == 0) {
                                        /* first time in: zap the whitelist
diff -uNr 2.4.1/fs/Config.in 2.4.1-tmpfs/fs/Config.in
--- 2.4.1/fs/Config.in  Tue Jan 30 11:07:11 2001
+++ 2.4.1-tmpfs/fs/Config.in    Thu Feb  1 22:06:30 2001
@@ -31,6 +31,7 @@
        int 'JFFS debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_JFFS_FS_VERBOSE 0
 fi
 tristate 'Compressed ROM file system support' CONFIG_CRAMFS
+bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
 tristate 'Simple RAM-based file system support' CONFIG_RAMFS
 
 tristate 'ISO 9660 CDROM file system support' CONFIG_ISO9660_FS
diff -uNr 2.4.1/include/linux/fs.h 2.4.1-tmpfs/include/linux/fs.h
--- 2.4.1/include/linux/fs.h    Thu Feb  1 22:19:59 2001
+++ 2.4.1-tmpfs/include/linux/fs.h      Thu Feb  1 22:23:28 2001
@@ -1213,6 +1213,7 @@
        }
        return inode;
 }
+extern void remove_suid(struct inode *inode);
 
 extern void insert_inode_hash(struct inode *);
 extern void remove_inode_hash(struct inode *);
@@ -1260,6 +1261,7 @@
 int block_truncate_page(struct address_space *, loff_t, get_block_t *);
 
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
+extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long 
+offset, unsigned long size);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
 extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *);
 extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, 
read_actor_t);
diff -uNr 2.4.1/include/linux/kernel.h 2.4.1-tmpfs/include/linux/kernel.h
--- 2.4.1/include/linux/kernel.h        Sun Dec 17 12:54:01 2000
+++ 2.4.1-tmpfs/include/linux/kernel.h  Thu Feb  1 22:02:48 2001
@@ -62,7 +62,7 @@
 extern int vsprintf(char *buf, const char *, va_list);
 extern int get_option(char **str, int *pint);
 extern char *get_options(char *str, int nints, int *ints);
-extern unsigned long memparse(char *ptr, char **retptr);
+extern unsigned long long memparse(char *ptr, char **retptr);
 extern void dev_probe_lock(void);
 extern void dev_probe_unlock(void);
 
diff -uNr 2.4.1/include/linux/mm.h 2.4.1-tmpfs/include/linux/mm.h
--- 2.4.1/include/linux/mm.h    Thu Feb  1 22:09:11 2001
+++ 2.4.1-tmpfs/include/linux/mm.h      Thu Feb  1 22:23:33 2001
@@ -200,8 +200,8 @@
                                        smp_mb__before_clear_bit(); \
                                        if (!test_and_clear_bit(PG_locked, 
&(page)->flags)) BUG(); \
                                        smp_mb__after_clear_bit(); \
-                                       if (waitqueue_active(&page->wait)) \
-                                               wake_up(&page->wait); \
+                                       if (waitqueue_active(&(page)->wait)) \
+                                               wake_up(&(page)->wait); \
                                } while (0)
 #define PageError(page)                test_bit(PG_error, &(page)->flags)
 #define SetPageError(page)     set_bit(PG_error, &(page)->flags)
diff -uNr 2.4.1/include/linux/shmem_fs.h 2.4.1-tmpfs/include/linux/shmem_fs.h
--- 2.4.1/include/linux/shmem_fs.h      Tue Jan  2 21:58:11 2001
+++ 2.4.1-tmpfs/include/linux/shmem_fs.h        Thu Feb  1 22:12:49 2001
@@ -19,6 +19,7 @@
 
 struct shmem_inode_info {
        spinlock_t      lock;
+       unsigned long   max_index;
        swp_entry_t     i_direct[SHMEM_NR_DIRECT]; /* for the first blocks */
        swp_entry_t   **i_indirect; /* doubly indirect blocks */
        unsigned long   swapped;
diff -uNr 2.4.1/lib/cmdline.c 2.4.1-tmpfs/lib/cmdline.c
--- 2.4.1/lib/cmdline.c Mon Aug 28 11:42:45 2000
+++ 2.4.1-tmpfs/lib/cmdline.c   Thu Feb  1 22:02:48 2001
@@ -93,9 +93,9 @@
  *     megabyte, or one gigabyte, respectively.
  */
 
-unsigned long memparse (char *ptr, char **retptr)
+unsigned long long memparse (char *ptr, char **retptr)
 {
-       unsigned long ret = simple_strtoul (ptr, retptr, 0);
+       unsigned long long ret = simple_strtoul (ptr, retptr, 0);
 
        switch (**retptr) {
        case 'G':
diff -uNr 2.4.1/mm/filemap.c 2.4.1-tmpfs/mm/filemap.c
--- 2.4.1/mm/filemap.c  Tue Jan 30 11:07:14 2001
+++ 2.4.1-tmpfs/mm/filemap.c    Thu Feb  1 22:22:33 2001
@@ -1209,7 +1209,7 @@
        UPDATE_ATIME(inode);
 }
 
-static int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long 
offset, unsigned long size)
+int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long 
+offset, unsigned long size)
 {
        char *kaddr;
        unsigned long left, count = desc->count;
@@ -2408,7 +2408,7 @@
        return page;
 }
 
-static inline void remove_suid(struct inode *inode)
+inline void remove_suid(struct inode *inode)
 {
        unsigned int mode;
 
diff -uNr 2.4.1/mm/shmem.c 2.4.1-tmpfs/mm/shmem.c
--- 2.4.1/mm/shmem.c    Tue Jan 30 11:07:14 2001
+++ 2.4.1-tmpfs/mm/shmem.c      Thu Feb  1 22:15:33 2001
@@ -1,5 +1,5 @@
 /*
- * Resizable simple shmem filesystem for Linux.
+ * Resizable virtual memory filesystem for Linux.
  *
  * Copyright (C) 2000 Linus Torvalds.
  *              2000 Transmeta Corp.
@@ -9,14 +9,12 @@
  */
 
 /*
- * This shared memory handling is heavily based on the ramfs. It
- * extends the ramfs by the ability to use swap which would makes it a
- * completely usable filesystem.
- *
- * But read and write are not supported (yet)
- *
+ * This virtual memory filesystem is heavily based on the ramfs. It
+ * extends ramfs by the ability to use swap and honor resource limits
+ * which makes it a completely usable filesystem.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
@@ -31,7 +29,8 @@
 
 #include <asm/uaccess.h>
 
-#define SHMEM_MAGIC    0x01021994
+/* This magic number is used in glibc for posix shared memory */
+#define TMPFS_MAGIC    0x01021994
 
 #define ENTRIES_PER_PAGE (PAGE_SIZE/sizeof(unsigned long))
 #define NR_SINGLE (ENTRIES_PER_PAGE + SHMEM_NR_DIRECT)
@@ -42,33 +41,70 @@
 static struct inode_operations shmem_inode_operations;
 static struct file_operations shmem_dir_operations;
 static struct inode_operations shmem_dir_inode_operations;
-static struct vm_operations_struct shmem_shared_vm_ops;
-static struct vm_operations_struct shmem_private_vm_ops;
+static struct inode_operations shmem_symlink_inode_operations;
+static struct vm_operations_struct shmem_vm_ops;
 
 LIST_HEAD (shmem_inodes);
 static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED;
 
+/*
+ * shmem_recalc_inode - recalculate the size of an inode
+ *
+ * @inode: inode to recalc
+ *
+ * We have to calculate the free blocks since the mm can drop pages
+ * behind our back
+ *
+ * But we know that normally
+ * inodes->i_blocks == inode->i_mapping->nrpages + info->swapped
+ *
+ * So the mm freed 
+ * inodes->i_blocks - (inode->i_mapping->nrpages + info->swapped)
+ *
+ * It has to be called with the spinlock held.
+ */
+
+static void shmem_recalc_inode(struct inode * inode)
+{
+       unsigned long freed;
+
+       freed = inode->i_blocks -
+               (inode->i_mapping->nrpages + inode->u.shmem_i.swapped);
+       if (freed){
+               struct shmem_sb_info * info = &inode->i_sb->u.shmem_sb;
+               inode->i_blocks -= freed;
+               spin_lock (&info->stat_lock);
+               info->free_blocks += freed;
+               spin_unlock (&info->stat_lock);
+       }
+}
+
 static swp_entry_t * shmem_swp_entry (struct shmem_inode_info *info, unsigned long 
index) 
 {
+       unsigned long offset;
+
        if (index < SHMEM_NR_DIRECT)
                return info->i_direct+index;
 
        index -= SHMEM_NR_DIRECT;
-       if (index >= ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)
-               return NULL;
+       offset = index % ENTRIES_PER_PAGE;
+       index /= ENTRIES_PER_PAGE;
+
+       if (index >= ENTRIES_PER_PAGE)
+               return ERR_PTR(-EFBIG);
 
        if (!info->i_indirect) {
                info->i_indirect = (swp_entry_t **) get_zeroed_page(GFP_USER);
                if (!info->i_indirect)
-                       return NULL;
+                       return ERR_PTR(-ENOMEM);
        }
-       if(!(info->i_indirect[index/ENTRIES_PER_PAGE])) {
-               info->i_indirect[index/ENTRIES_PER_PAGE] = (swp_entry_t *) 
get_zeroed_page(GFP_USER);
-               if (!info->i_indirect[index/ENTRIES_PER_PAGE])
-                       return NULL;
+       if(!(info->i_indirect[index])) {
+               info->i_indirect[index] = (swp_entry_t *) get_zeroed_page(GFP_USER);
+               if (!info->i_indirect[index])
+                       return ERR_PTR(-ENOMEM);
        }
        
-       return info->i_indirect[index/ENTRIES_PER_PAGE]+index%ENTRIES_PER_PAGE;
+       return info->i_indirect[index]+offset;
 }
 
 static int shmem_free_swp(swp_entry_t *dir, unsigned int count)
@@ -98,7 +134,6 @@
  * @dir:       pointer to swp_entries 
  * @size:      number of entries in dir
  * @start:     offset to start from
- * @inode:     inode for statistics
  * @freed:     counter for freed pages
  *
  * It frees the swap entries from dir+start til dir+size
@@ -108,7 +143,7 @@
 
 static unsigned long 
 shmem_truncate_part (swp_entry_t * dir, unsigned long size, 
-                    unsigned long start, struct inode * inode, unsigned long *freed) {
+                    unsigned long start, unsigned long *freed) {
        if (start > size)
                return start - size;
        if (dir)
@@ -117,56 +152,27 @@
        return 0;
 }
 
-/*
- * shmem_recalc_inode - recalculate the size of an inode
- *
- * @inode: inode to recalc
- *
- * We have to calculate the free blocks since the mm can drop pages
- * behind our back
- *
- * But we know that normally
- * inodes->i_blocks == inode->i_mapping->nrpages + info->swapped
- *
- * So the mm freed 
- * inodes->i_blocks - (inode->i_mapping->nrpages + info->swapped)
- *
- * It has to be called with the spinlock held.
- */
-
-static void shmem_recalc_inode(struct inode * inode)
-{
-       unsigned long freed;
-
-       freed = inode->i_blocks -
-               (inode->i_mapping->nrpages + inode->u.shmem_i.swapped);
-       if (freed){
-               struct shmem_sb_info * info = &inode->i_sb->u.shmem_sb;
-               inode->i_blocks -= freed;
-               spin_lock (&info->stat_lock);
-               info->free_blocks += freed;
-               spin_unlock (&info->stat_lock);
-       }
-}
-
 static void shmem_truncate (struct inode * inode)
 {
        int clear_base;
-       unsigned long start;
+       unsigned long index, start;
        unsigned long freed = 0;
-       swp_entry_t **base, **ptr;
+       swp_entry_t **base, **ptr, **last;
        struct shmem_inode_info * info = &inode->u.shmem_i;
 
        spin_lock (&info->lock);
-       start = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       if (index > info->max_index)
+               goto out;
 
-       start = shmem_truncate_part (info->i_direct, SHMEM_NR_DIRECT, start, inode, 
&freed);
+       start = shmem_truncate_part (info->i_direct, SHMEM_NR_DIRECT, index, &freed);
 
        if (!(base = info->i_indirect))
-               goto out;;
+               goto out;
 
        clear_base = 1;
-       for (ptr = base; ptr < base + ENTRIES_PER_PAGE; ptr++) {
+       last = base + ((info->max_index - SHMEM_NR_DIRECT + ENTRIES_PER_PAGE - 1) / 
+ENTRIES_PER_PAGE);
+       for (ptr = base; ptr < last; ptr++) {
                if (!start) {
                        if (!*ptr)
                                continue;
@@ -176,16 +182,16 @@
                        continue;
                }
                clear_base = 0;
-               start = shmem_truncate_part (*ptr, ENTRIES_PER_PAGE, start, inode, 
&freed);
+               start = shmem_truncate_part (*ptr, ENTRIES_PER_PAGE, start, &freed);
        }
 
-       if (!clear_base) 
-               goto out;
-
-       free_page ((unsigned long)base);
-       info->i_indirect = 0;
+       if (clear_base) {
+               free_page ((unsigned long)base);
+               info->i_indirect = 0;
+       }
 
 out:
+       info->max_index = index;
        info->swapped -= freed;
        shmem_recalc_inode(inode);
        spin_unlock (&info->lock);
@@ -225,16 +231,16 @@
 
        spin_lock(&info->lock);
        shmem_recalc_inode(page->mapping->host);
-       entry = shmem_swp_entry (info, page->index);
-       if (!entry)     /* this had been allocted on page allocation */
+       entry = shmem_swp_entry(info, page->index);
+       if (IS_ERR(entry))      /* this had been allocted on page allocation */
                BUG();
        error = -EAGAIN;
        if (entry->val) {
-                __swap_free(swap, 2);
+               __swap_free(swap, 2);
                goto out;
-        }
+       }
 
-        *entry = swap;
+       *entry = swap;
        error = 0;
        /* Remove the from the page cache */
        lru_cache_del(page);
@@ -252,43 +258,27 @@
 }
 
 /*
- * shmem_nopage - either get the page from swap or allocate a new one
+ * shmem_getpage_locked - either get the page from swap or allocate a new one
  *
  * If we allocate a new one we do not mark it dirty. That's up to the
  * vm. If we swap it in we mark it dirty since we also free the swap
  * entry since a page cannot live in both the swap and page cache
  */
-struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int 
no_share)
+static struct page * shmem_getpage_locked(struct inode * inode, unsigned long idx)
 {
-       unsigned long size;
-       struct page * page;
-       unsigned int idx;
-       swp_entry_t *entry;
-       struct inode * inode = vma->vm_file->f_dentry->d_inode;
        struct address_space * mapping = inode->i_mapping;
        struct shmem_inode_info *info;
+       struct page * page;
+       swp_entry_t *entry;
 
-       idx = (address - vma->vm_start) >> PAGE_SHIFT;
-       idx += vma->vm_pgoff;
-
-       down (&inode->i_sem);
-       size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-       page = NOPAGE_SIGBUS;
-       if ((idx >= size) && (vma->vm_mm == current->mm))
-               goto out;
-
-       /* retry, we may have slept */
-       page = __find_lock_page(mapping, idx, page_hash (mapping, idx));
+       page = find_lock_page(mapping, idx);;
        if (page)
-               goto cached_page;
+               return page;
 
        info = &inode->u.shmem_i;
        entry = shmem_swp_entry (info, idx);
-       if (!entry)
-               goto oom;
-       spin_lock (&info->lock);
-       shmem_recalc_inode(inode);
-       spin_unlock (&info->lock);
+       if (IS_ERR(entry))
+               return (void *)entry;
        if (entry->val) {
                unsigned long flags;
 
@@ -300,15 +290,15 @@
                        page = read_swap_cache(*entry);
                        unlock_kernel();
                        if (!page) 
-                               goto oom;
+                               return ERR_PTR(-ENOMEM);
                }
 
                /* We have to this with page locked to prevent races */
+               lock_page(page);
                spin_lock (&info->lock);
                swap_free(*entry);
-               lock_page(page);
-               delete_from_swap_cache_nolock(page);
                *entry = (swp_entry_t) {0};
+               delete_from_swap_cache_nolock(page);
                flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << 
PG_referenced) | (1 << PG_arch_1));
                page->flags = flags | (1 << PG_dirty);
                add_to_page_cache_locked(page, mapping, idx);
@@ -323,19 +313,70 @@
                /* Ok, get a new page */
                page = page_cache_alloc();
                if (!page)
-                       goto oom;
-               clear_user_highpage(page, address);
+                       return ERR_PTR(-ENOMEM);
+               clear_highpage(page);
                inode->i_blocks++;
                add_to_page_cache (page, mapping, idx);
        }
        /* We have the page */
-       SetPageUptodate (page);
+       SetPageUptodate(page);
        if (info->locked)
                page_cache_get(page);
+       return page;
+no_space:
+       spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
+       return ERR_PTR(-ENOSPC);
+}
 
-cached_page:
-       UnlockPage (page);
-       up(&inode->i_sem);
+static int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr)
+{
+       struct address_space * mapping = inode->i_mapping;
+       int error;
+
+       *ptr = NOPAGE_SIGBUS;
+       if (inode->i_size <= (loff_t) idx * PAGE_CACHE_SIZE)
+               return -EFAULT;
+
+       *ptr = __find_get_page(mapping, idx, page_hash(mapping, idx));
+       if (*ptr) {
+               if (Page_Uptodate(*ptr))
+                       return 0;
+               page_cache_release(*ptr);
+       }
+
+       down (&inode->i_sem);
+       /* retest we may have slept */
+       if (inode->i_size < (loff_t) idx * PAGE_CACHE_SIZE)
+               goto sigbus;
+       *ptr = shmem_getpage_locked(inode, idx);
+       if (IS_ERR (*ptr))
+               goto failed;
+       UnlockPage(*ptr);
+       up (&inode->i_sem);
+       return 0;
+failed:
+       up (&inode->i_sem);
+       error = PTR_ERR(*ptr);
+       *ptr = NOPAGE_OOM;
+       if (error != -EFBIG)
+               *ptr = NOPAGE_SIGBUS;
+       return error;
+sigbus:
+       *ptr = NOPAGE_SIGBUS;
+       return -EFAULT;
+}
+
+struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int 
+no_share)
+{
+       struct page * page;
+       unsigned int idx;
+       struct inode * inode = vma->vm_file->f_dentry->d_inode;
+
+       idx = (address - vma->vm_start) >> PAGE_SHIFT;
+       idx += vma->vm_pgoff;
+
+       if (shmem_getpage(inode, idx, &page))
+               return page;
 
        if (no_share) {
                struct page *new_page = page_cache_alloc();
@@ -351,13 +392,45 @@
 
        flush_page_to_ram (page);
        return(page);
-no_space:
-       spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
-oom:
-       page = NOPAGE_OOM;
-out:
+}
+
+void shmem_lock(struct file * file, int lock)
+{
+       struct inode * inode = file->f_dentry->d_inode;
+       struct shmem_inode_info * info = &inode->u.shmem_i;
+       struct page * page;
+       unsigned long idx, size;
+
+       if (info->locked == lock)
+               return;
+       down(&inode->i_sem);
+       info->locked = lock;
+       size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       for (idx = 0; idx < size; idx++) {
+               page = find_lock_page(inode->i_mapping, idx);
+               if (!page)
+                       continue;
+               if (!lock) {
+                       /* release the extra count and our reference */
+                       page_cache_release(page);
+                       page_cache_release(page);
+               }
+               UnlockPage(page);
+       }
        up(&inode->i_sem);
-       return page;
+}
+
+static int shmem_mmap(struct file * file, struct vm_area_struct * vma)
+{
+       struct vm_operations_struct * ops;
+       struct inode *inode = file->f_dentry->d_inode;
+
+       ops = &shmem_vm_ops;
+       if (!inode->i_sb || !S_ISREG(inode->i_mode))
+               return -EACCES;
+       UPDATE_ATIME(inode);
+       vma->vm_ops = ops;
+       return 0;
 }
 
 struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev)
@@ -396,7 +469,8 @@
                        inode->i_fop = &shmem_dir_operations;
                        break;
                case S_IFLNK:
-                       BUG();
+                       inode->i_op = &shmem_symlink_inode_operations;
+                       break;
                }
                spin_lock (&shmem_ilock);
                list_add (&inode->u.shmem_i.list, &shmem_inodes);
@@ -405,9 +479,221 @@
        return inode;
 }
 
+#ifdef CONFIG_TMPFS
+static ssize_t
+shmem_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos)
+{
+       struct inode    *inode = file->f_dentry->d_inode; 
+       unsigned long   limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+       loff_t          pos;
+       struct page     *page;
+       unsigned long   written;
+       long            status;
+       int             err;
+
+
+       down(&inode->i_sem);
+
+       pos = *ppos;
+       err = -EINVAL;
+       if (pos < 0)
+               goto out;
+
+       err = file->f_error;
+       if (err) {
+               file->f_error = 0;
+               goto out;
+       }
+
+       written = 0;
+
+       if (file->f_flags & O_APPEND)
+               pos = inode->i_size;
+
+       /*
+        * Check whether we've reached the file size limit.
+        */
+       err = -EFBIG;
+       if (limit != RLIM_INFINITY) {
+               if (pos >= limit) {
+                       send_sig(SIGXFSZ, current, 0);
+                       goto out;
+               }
+               if (count > limit - pos) {
+                       send_sig(SIGXFSZ, current, 0);
+                       count = limit - pos;
+               }
+       }
+
+       status  = 0;
+       if (count) {
+               remove_suid(inode);
+               inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+       }
+
+       while (count) {
+               unsigned long bytes, index, offset;
+               char *kaddr;
+               int deactivate = 1;
+
+               /*
+                * Try to find the page in the cache. If it isn't there,
+                * allocate a free page.
+                */
+               offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+               index = pos >> PAGE_CACHE_SHIFT;
+               bytes = PAGE_CACHE_SIZE - offset;
+               if (bytes > count) {
+                       bytes = count;
+                       deactivate = 0;
+               }
+
+               /*
+                * Bring in the user page that we will copy from _first_.
+                * Otherwise there's a nasty deadlock on copying from the
+                * same page as we're writing to, without it being marked
+                * up-to-date.
+                */
+               { volatile unsigned char dummy;
+                       __get_user(dummy, buf);
+                       __get_user(dummy, buf+bytes-1);
+               }
+
+               page = shmem_getpage_locked(inode, index);
+               status = PTR_ERR(page);
+               if (IS_ERR(page))
+                       break;
+
+               /* We have exclusive IO access to the page.. */
+               if (!PageLocked(page)) {
+                       PAGE_BUG(page);
+               }
+
+               kaddr = kmap(page);
+// can this do a truncated write? cr
+               status = copy_from_user(kaddr+offset, buf, bytes);
+               kunmap(page);
+               if (status)
+                       goto fail_write;
+
+               flush_dcache_page(page);
+               if (bytes > 0) {
+                       SetPageDirty(page);
+                       written += bytes;
+                       count -= bytes;
+                       pos += bytes;
+                       buf += bytes;
+                       if (pos > inode->i_size) 
+                               inode->i_size = pos;
+                       if (inode->u.shmem_i.max_index < index)
+                               inode->u.shmem_i.max_index = index;
+
+               }
+unlock:
+               /* Mark it unlocked again and drop the page.. */
+               UnlockPage(page);
+               if (deactivate)
+                       deactivate_page(page);
+               page_cache_release(page);
+
+               if (status < 0)
+                       break;
+       }
+       *ppos = pos;
+
+       err = written ? written : status;
+out:
+       up(&inode->i_sem);
+       return err;
+fail_write:
+       status = -EFAULT;
+       ClearPageUptodate(page);
+       kunmap(page);
+       goto unlock;
+}
+
+static void do_shmem_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * 
+desc)
+{
+       struct inode *inode = filp->f_dentry->d_inode;
+       struct address_space *mapping = inode->i_mapping;
+       unsigned long index, offset;
+       int nr = 1;
+
+       index = *ppos >> PAGE_CACHE_SHIFT;
+       offset = *ppos & ~PAGE_CACHE_MASK;
+
+       while (nr && desc->count) {
+               struct page *page;
+               unsigned long end_index, nr;
+
+               end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+               if (index > end_index)
+                       break;
+               nr = PAGE_CACHE_SIZE;
+               if (index == end_index) {
+                       nr = inode->i_size & ~PAGE_CACHE_MASK;
+                       if (nr <= offset)
+                               break;
+               }
+
+               nr = nr - offset;
+
+               if ((desc->error = shmem_getpage(inode, index, &page)))
+                       break;
+
+               if (mapping->i_mmap_shared != NULL)
+                       flush_dcache_page(page);
+
+               /*
+                * Ok, we have the page, and it's up-to-date, so
+                * now we can copy it to user space...
+                *
+                * The actor routine returns how many bytes were actually used..
+                * NOTE! This may not be the same as how much of a user buffer
+                * we filled up (we may be padding etc), so we can only update
+                * "pos" here (the actor routine has to update the user buffer
+                * pointers and the remaining count).
+                */
+               nr = file_read_actor(desc, page, offset, nr);
+               offset += nr;
+               index += offset >> PAGE_CACHE_SHIFT;
+               offset &= ~PAGE_CACHE_MASK;
+       
+               page_cache_release(page);
+       }
+
+       *ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
+       UPDATE_ATIME(inode);
+}
+
+static ssize_t shmem_file_read(struct file * filp, char * buf, size_t count, loff_t 
+*ppos)
+{
+       ssize_t retval;
+
+       retval = -EFAULT;
+       if (access_ok(VERIFY_WRITE, buf, count)) {
+               retval = 0;
+
+               if (count) {
+                       read_descriptor_t desc;
+
+                       desc.written = 0;
+                       desc.count = count;
+                       desc.buf = buf;
+                       desc.error = 0;
+                       do_shmem_file_read(filp, ppos, &desc);
+
+                       retval = desc.written;
+                       if (!retval)
+                               retval = desc.error;
+               }
+       }
+       return retval;
+}
+
 static int shmem_statfs(struct super_block *sb, struct statfs *buf)
 {
-       buf->f_type = SHMEM_MAGIC;
+       buf->f_type = TMPFS_MAGIC;
        buf->f_bsize = PAGE_CACHE_SIZE;
        spin_lock (&sb->u.shmem_sb.stat_lock);
        if (sb->u.shmem_sb.max_blocks != ULONG_MAX || 
@@ -422,32 +708,6 @@
        return 0;
 }
 
-void shmem_lock(struct file * file, int lock)
-{
-       struct inode * inode = file->f_dentry->d_inode;
-       struct shmem_inode_info * info = &inode->u.shmem_i;
-       struct page * page;
-       unsigned long idx, size;
-
-       if (info->locked == lock)
-               return;
-       down(&inode->i_sem);
-       info->locked = lock;
-       size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-       for (idx = 0; idx < size; idx++) {
-               page = find_lock_page(inode->i_mapping, idx);
-               if (!page)
-                       continue;
-               if (!lock) {
-                       /* release the extra count and our reference */
-                       page_cache_release(page);
-                       page_cache_release(page);
-               }
-               UnlockPage(page);
-       }
-       up(&inode->i_sem);
-}
-
 /*
  * Lookup the data. This is trivial - if the dentry didn't already
  * exist, we know it is negative.
@@ -575,19 +835,66 @@
        return error;
 }
 
-static int shmem_mmap(struct file * file, struct vm_area_struct * vma)
+static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * 
+symname)
 {
-       struct vm_operations_struct * ops;
-       struct inode *inode = file->f_dentry->d_inode;
+       int error;
+       int len;
+       struct inode *inode;
+       struct page *page;
+       char *kaddr;
 
-       ops = &shmem_private_vm_ops;
-       if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
-               ops = &shmem_shared_vm_ops;
-       if (!inode->i_sb || !S_ISREG(inode->i_mode))
-               return -EACCES;
-       UPDATE_ATIME(inode);
-       vma->vm_ops = ops;
+       error = shmem_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0);
+       if (error)
+               return error;
+
+       len = strlen(symname);
+       if (len > PAGE_SIZE)
+               return -ENAMETOOLONG;
+               
+       inode = dentry->d_inode;
+       down(&inode->i_sem);
+       page = shmem_getpage_locked(inode, 0);
+       if (IS_ERR(page))
+               goto fail;
+       kaddr = kmap(page);
+       memcpy(kaddr, symname, len);
+       kunmap(page);
+       inode->i_size = len;
+       SetPageDirty(page);
+       UnlockPage(page);
+       page_cache_release(page);
+       up(&inode->i_sem);
        return 0;
+fail:
+       up(&inode->i_sem);
+       return PTR_ERR(page);
+}
+
+static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+       struct page * page;
+       int res = shmem_getpage(dentry->d_inode, 0, &page);
+
+       if (res)
+               return res;
+
+       res = vfs_readlink(dentry,buffer,buflen, kmap(page));
+       kunmap(page);
+       page_cache_release(page);
+       return res;
+}
+
+static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+       struct page * page;
+       int res = shmem_getpage(dentry->d_inode, 0, &page);
+       if (res)
+               return res;
+
+       res = vfs_follow_link(nd, kmap(page));
+       kunmap(page);
+       page_cache_release(page);
+       return res;
 }
 
 static int shmem_parse_options(char *options, int *mode, unsigned long * blocks, 
unsigned long *inodes)
@@ -600,16 +907,24 @@
        for ( ; this_char; this_char = strtok(NULL,",")) {
                if ((value = strchr(this_char,'=')) != NULL)
                        *value++ = 0;
-               if (!strcmp(this_char,"nr_blocks")) {
+               if (!strcmp(this_char,"size")) {
+                       unsigned long long size;
                        if (!value || !*value || !blocks)
                                return 1;
-                       *blocks = simple_strtoul(value,&value,0);
+                       size = memparse(value,&value);
+                       if (*value)
+                               return 1;
+                       *blocks = size >> PAGE_CACHE_SHIFT;
+               } else if (!strcmp(this_char,"nr_blocks")) {
+                       if (!value || !*value || !blocks)
+                               return 1;
+                       *blocks = memparse(value,&value);
                        if (*value)
                                return 1;
                } else if (!strcmp(this_char,"nr_inodes")) {
                        if (!value || !*value || !inodes)
                                return 1;
-                       *inodes = simple_strtoul(value,&value,0);
+                       *inodes = memparse(value,&value);
                        if (*value)
                                return 1;
                } else if (!strcmp(this_char,"mode")) {
@@ -622,10 +937,38 @@
                else
                        return 1;
        }
-
        return 0;
 }
 
+static int shmem_remount_fs (struct super_block *sb, int *flags, char *data)
+{
+       int error;
+       unsigned long max_blocks, blocks;
+       unsigned long max_inodes, inodes;
+       struct shmem_sb_info *info = &sb->u.shmem_sb;
+
+       if (shmem_parse_options (data, NULL, &max_blocks, &max_inodes))
+               return -EINVAL;
+
+       spin_lock(&info->stat_lock);
+       blocks = info->max_blocks - info->free_blocks;
+       inodes = info->max_inodes - info->free_inodes;
+       error = -EINVAL;
+       if (max_blocks < blocks)
+               goto out;
+       if (max_inodes < inodes)
+               goto out;
+       error = 0;
+       info->max_blocks  = max_blocks;
+       info->free_blocks = max_blocks - blocks;
+       info->max_inodes  = max_inodes;
+       info->free_inodes = max_inodes - inodes;
+out:
+       spin_unlock(&info->stat_lock);
+       return error;
+}
+#endif
+
 static struct super_block *shmem_read_super(struct super_block * sb, void * data, int 
silent)
 {
        struct inode * inode;
@@ -634,10 +977,12 @@
        unsigned long inodes = ULONG_MAX;       /* unlimited */
        int mode   = S_IRWXUGO | S_ISVTX;
 
+#ifdef CONFIG_TMPFS
        if (shmem_parse_options (data, &mode, &blocks, &inodes)) {
-               printk(KERN_ERR "shmem fs invalid option\n");
+               printk(KERN_ERR "tmpfs invalid option\n");
                return NULL;
        }
+#endif
 
        spin_lock_init (&sb->u.shmem_sb.stat_lock);
        sb->u.shmem_sb.max_blocks = blocks;
@@ -646,7 +991,7 @@
        sb->u.shmem_sb.free_inodes = inodes;
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
-       sb->s_magic = SHMEM_MAGIC;
+       sb->s_magic = TMPFS_MAGIC;
        sb->s_op = &shmem_ops;
        inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
        if (!inode)
@@ -661,103 +1006,102 @@
        return sb;
 }
 
-static int shmem_remount_fs (struct super_block *sb, int *flags, char *data)
-{
-       int error;
-       unsigned long max_blocks, blocks;
-       unsigned long max_inodes, inodes;
-       struct shmem_sb_info *info = &sb->u.shmem_sb;
-
-       if (shmem_parse_options (data, NULL, &max_blocks, &max_inodes))
-               return -EINVAL;
-
-       spin_lock(&info->stat_lock);
-       blocks = info->max_blocks - info->free_blocks;
-       inodes = info->max_inodes - info->free_inodes;
-       error = -EINVAL;
-       if (max_blocks < blocks)
-               goto out;
-       if (max_inodes < inodes)
-               goto out;
-       error = 0;
-       info->max_blocks  = max_blocks;
-       info->free_blocks = max_blocks - blocks;
-       info->max_inodes  = max_inodes;
-       info->free_inodes = max_inodes - inodes;
-out:
-       spin_unlock(&info->stat_lock);
-       return error;
-}
-
 static struct address_space_operations shmem_aops = {
        writepage: shmem_writepage
 };
 
 static struct file_operations shmem_file_operations = {
-       mmap:           shmem_mmap
+       mmap:   shmem_mmap,
+#ifdef CONFIG_TMPFS
+       read:   shmem_file_read,
+       write:  shmem_file_write
+#endif
 };
 
 static struct inode_operations shmem_inode_operations = {
        truncate:       shmem_truncate,
 };
 
+static struct inode_operations shmem_symlink_inode_operations = {
+       truncate:       shmem_truncate,
+#ifdef CONFIG_TMPFS
+       readlink:       shmem_readlink,
+       follow_link:    shmem_follow_link,
+#endif
+};
+
 static struct file_operations shmem_dir_operations = {
        read:           generic_read_dir,
        readdir:        dcache_readdir,
 };
 
 static struct inode_operations shmem_dir_inode_operations = {
+#ifdef CONFIG_TMPFS
        create:         shmem_create,
        lookup:         shmem_lookup,
        link:           shmem_link,
        unlink:         shmem_unlink,
+       symlink:        shmem_symlink,
        mkdir:          shmem_mkdir,
        rmdir:          shmem_rmdir,
        mknod:          shmem_mknod,
        rename:         shmem_rename,
+#endif
 };
 
 static struct super_operations shmem_ops = {
+#ifdef CONFIG_TMPFS
        statfs:         shmem_statfs,
        remount_fs:     shmem_remount_fs,
+#endif
        delete_inode:   shmem_delete_inode,
        put_inode:      force_delete,   
 };
 
-static struct vm_operations_struct shmem_private_vm_ops = {
-       nopage: shmem_nopage,
-};
-
-static struct vm_operations_struct shmem_shared_vm_ops = {
+static struct vm_operations_struct shmem_vm_ops = {
        nopage: shmem_nopage,
 };
 
+#ifdef CONFIG_TMPFS
+/* type "shm" will be tagged obsolete in 2.5 */
 static DECLARE_FSTYPE(shmem_fs_type, "shm", shmem_read_super, FS_LITTER);
+static DECLARE_FSTYPE(tmpfs_fs_type, "tmpfs", shmem_read_super, FS_LITTER);
+#else
+static DECLARE_FSTYPE(tmpfs_fs_type, "tmpfs", shmem_read_super, FS_LITTER|FS_NOMOUNT);
+#endif
 
 static int __init init_shmem_fs(void)
 {
        int error;
        struct vfsmount * res;
 
+       if ((error = register_filesystem(&tmpfs_fs_type))) {
+               printk (KERN_ERR "Could not register tmpfs\n");
+               return error;
+       }
+#ifdef CONFIG_TMPFS
        if ((error = register_filesystem(&shmem_fs_type))) {
-               printk (KERN_ERR "Could not register shmem fs\n");
+               printk (KERN_ERR "Could not register shm fs\n");
                return error;
        }
-
-       res = kern_mount(&shmem_fs_type);
+       devfs_mk_dir (NULL, "shm", NULL);
+#endif
+       res = kern_mount(&tmpfs_fs_type);
        if (IS_ERR (res)) {
-               printk (KERN_ERR "could not kern_mount shmem fs\n");
-               unregister_filesystem(&shmem_fs_type);
+               printk (KERN_ERR "could not kern_mount tmpfs\n");
+               unregister_filesystem(&tmpfs_fs_type);
                return PTR_ERR(res);
        }
 
-       devfs_mk_dir (NULL, "shm", NULL);
        return 0;
 }
 
 static void __exit exit_shmem_fs(void)
 {
+#ifdef CONFIG_TMPFS
        unregister_filesystem(&shmem_fs_type);
+#endif
+       unregister_filesystem(&tmpfs_fs_type);
 }
 
 module_init(init_shmem_fs)
@@ -853,7 +1197,7 @@
        this.name = name;
        this.len = strlen(name);
        this.hash = 0; /* will go */
-       root = shmem_fs_type.kern_mnt->mnt_root;
+       root = tmpfs_fs_type.kern_mnt->mnt_root;
        dentry = d_alloc(root, &this);
        if (!dentry)
                goto out;
@@ -870,7 +1214,8 @@
 
        d_instantiate(dentry, inode);
        dentry->d_inode->i_size = size;
-       file->f_vfsmnt = mntget(shmem_fs_type.kern_mnt);
+       shmem_truncate(inode);
+       file->f_vfsmnt = mntget(tmpfs_fs_type.kern_mnt);
        file->f_dentry = dentry;
        file->f_op = &shmem_file_operations;
        file->f_mode = FMODE_WRITE | FMODE_READ;
@@ -901,6 +1246,8 @@
        if (vma->vm_file)
                fput (vma->vm_file);
        vma->vm_file = file;
-       vma->vm_ops = &shmem_shared_vm_ops;
+       vma->vm_ops = &shmem_vm_ops;
        return 0;
 }
+
+EXPORT_SYMBOL(shmem_file_setup);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
Please read the FAQ at http://www.tux.org/lkml/

Reply via email to