diff -urN old/include/linux/shmem_fs.h new/include/linux/shmem_fs.h
--- old/include/linux/shmem_fs.h	Wed May 23 07:44:24 2001
+++ new/include/linux/shmem_fs.h	Wed May 23 07:45:03 2001
@@ -33,6 +33,13 @@
 	unsigned long max_inodes;   /* How many inodes are allowed */
 	unsigned long free_inodes;  /* How many are left for allocation */
 	spinlock_t    stat_lock;
+
+  /* Improved resource limits */
+  unsigned long reserved_blocks;  /* Blocks reserved for root */
+  unsigned long reserved_inodes;  /* Inodes reserved for root */
+  unsigned long reserved_mem;     /* Memory reserved for rest of system */
+  uid_t reserved_uid;             /* User with acces to reserved blocks
+				   * and inodes */
 };
 
 #endif
diff -urN old/mm/shmem.c new/mm/shmem.c
--- old/mm/shmem.c	Wed May 23 07:44:02 2001
+++ new/mm/shmem.c	Wed May 23 07:48:38 2001
@@ -14,6 +14,13 @@
  * which makes it a completely usable filesystem.
  */
 
+/*
+ * Improved resource limits.
+ *
+ * Copyright (C) 2001 Kasper Dupont <kasperd@daimi.au.dk>
+ *
+ */
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -49,6 +56,34 @@
 
 #define BLOCKS_PER_PAGE (PAGE_SIZE/512)
 
+/* shmem_sb_sys_free - compute free space in super block and system
+ *
+ * @shmem_sb: Super block to get parameters from
+ *
+ * Compute amount of memory we can be allowed to use, it is the
+ * minimum of two different limits. The first limit is the free
+ * space according to super block. The second limit is the free
+ * memory in system - the reserved memory according to super block.
+ *
+ * Must be called with shmem_sb->stat_lock held.
+ */
+
+static inline unsigned long shmem_sb_sys_free(struct shmem_sb_info * shmem_sb)
+{
+	unsigned long free_mem = 
+		nr_free_pages() + 
+		nr_swap_pages +
+		atomic_read(&buffermem_pages);
+	unsigned long reserved_mem = 
+		shmem_sb->reserved_mem;
+	unsigned long free_blocks = 
+		shmem_sb->free_blocks;
+
+	if (free_mem<reserved_mem) return 0;
+	if (free_blocks < free_mem-reserved_mem) return free_blocks;
+	return free_mem-reserved_mem;
+}
+
 /*
  * shmem_recalc_inode - recalculate the size of an inode
  *
@@ -353,12 +388,19 @@
 		info->swapped--;
 		spin_unlock (&info->lock);
 	} else {
+		unsigned long currently_free;
+		struct shmem_sb_info * shmem_sb = &inode->i_sb->u.shmem_sb;
 		spin_unlock (&info->lock);
-		spin_lock (&inode->i_sb->u.shmem_sb.stat_lock);
-		if (inode->i_sb->u.shmem_sb.free_blocks == 0)
+		spin_lock (&shmem_sb->stat_lock);
+		currently_free=shmem_sb_sys_free(shmem_sb);
+		if (currently_free == 0)
+			goto no_space;
+		if ((currently_free <= shmem_sb->reserved_blocks)&&
+		    (current->fsuid != shmem_sb->reserved_uid)&&
+		    (!capable(CAP_SYS_RESOURCE)))
 			goto no_space;
-		inode->i_sb->u.shmem_sb.free_blocks--;
-		spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
+		shmem_sb->free_blocks--;
+		spin_unlock (&shmem_sb->stat_lock);
 
 		/* Ok, get a new page.  We don't have to worry about the
 		 * info->lock spinlock here: we cannot race against
@@ -506,6 +548,13 @@
 		spin_unlock (&sb->u.shmem_sb.stat_lock);
 		return NULL;
 	}
+	if ((sb->u.shmem_sb.free_inodes <= sb->u.shmem_sb.reserved_inodes)
+	    &&(current->fsuid != sb->u.shmem_sb.reserved_uid)
+	    &&(!capable(CAP_SYS_RESOURCE)))
+	{
+		spin_unlock (&sb->u.shmem_sb.stat_lock);
+		return NULL;
+	}
 	sb->u.shmem_sb.free_inodes--;
 	spin_unlock (&sb->u.shmem_sb.stat_lock);
 
@@ -758,21 +807,22 @@
 
 static int shmem_statfs(struct super_block *sb, struct statfs *buf)
 {
+	struct shmem_sb_info *info = &sb->u.shmem_sb;
 	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) {
-		/*
-		 * This is only a guestimate and not honoured.
-		 * We need it to make some programs happy which like to
-		 * test the free space of a file system.
-		 */
-		buf->f_bavail = buf->f_bfree = nr_free_pages() + nr_swap_pages + atomic_read(&buffermem_pages);
-		buf->f_blocks = buf->f_bfree + ULONG_MAX - sb->u.shmem_sb.free_blocks;
-	} else {
-		buf->f_blocks = sb->u.shmem_sb.max_blocks;
-		buf->f_bavail = buf->f_bfree = sb->u.shmem_sb.free_blocks;
-	}
+	spin_lock (&info->stat_lock);
+	/*
+	 * This is only a guestimate and not honoured.
+	 * We need it to make some programs happy which like to
+	 * test the free space of a file system.
+	 */
+	buf->f_bfree = shmem_sb_sys_free(info);
+	if (buf->f_bfree < info->reserved_blocks)
+		buf->f_bavail=0;
+	else
+		buf->f_bavail=buf->f_bfree-info->reserved_blocks;
+	buf->f_blocks = buf->f_bfree + info->max_blocks - info->free_blocks;
+
 	buf->f_files = sb->u.shmem_sb.max_inodes;
 	buf->f_ffree = sb->u.shmem_sb.free_inodes;
 	spin_unlock (&sb->u.shmem_sb.stat_lock);
@@ -977,7 +1027,8 @@
 	return res;
 }
 
-static int shmem_parse_options(char *options, int *mode, unsigned long * blocks, unsigned long *inodes)
+static int shmem_parse_options(char *options,int *mode,
+			       struct shmem_sb_info *args)
 {
 	char *this_char, *value;
 
@@ -989,22 +1040,22 @@
 			*value++ = 0;
 		if (!strcmp(this_char,"size")) {
 			unsigned long long size;
-			if (!value || !*value || !blocks)
+			if (!value || !*value)
 				return 1;
 			size = memparse(value,&value);
 			if (*value)
 				return 1;
-			*blocks = size >> PAGE_CACHE_SHIFT;
+			args->max_blocks = size >> PAGE_CACHE_SHIFT;
 		} else if (!strcmp(this_char,"nr_blocks")) {
-			if (!value || !*value || !blocks)
+			if (!value || !*value)
 				return 1;
-			*blocks = memparse(value,&value);
+			args->max_blocks = memparse(value,&value);
 			if (*value)
 				return 1;
 		} else if (!strcmp(this_char,"nr_inodes")) {
-			if (!value || !*value || !inodes)
+			if (!value || !*value)
 				return 1;
-			*inodes = memparse(value,&value);
+			args->max_inodes = memparse(value,&value);
 			if (*value)
 				return 1;
 		} else if (!strcmp(this_char,"mode")) {
@@ -1013,36 +1064,84 @@
 			*mode = simple_strtoul(value,&value,8);
 			if (*value)
 				return 1;
+		} else if (!strcmp(this_char,"reserved_blocks")) {
+			if (!value || !*value)
+				return 1;
+			args->reserved_blocks = memparse(value,&value);
+			if (*value)
+				return 1;
+		} else if (!strcmp(this_char,"reserved_inodes")) {
+			if (!value || !*value)
+				return 1;
+			args->reserved_inodes = memparse(value,&value);
+			if (*value)
+				return 1;
+		} else if (!strcmp(this_char,"reserved_mem")) {
+			if (!value || !*value)
+				return 1;
+			args->reserved_mem = memparse(value,&value);
+			if (*value)
+				return 1;
+		} else if (!strcmp(this_char,"reserved_uid")) {
+			if (!value || !*value)
+				return 1;
+			args->reserved_uid = memparse(value,&value);
+			if (*value)
+				return 1;
 		}
 		else
 			return 1;
 	}
+
+	if (args->max_blocks<5) args->max_blocks=5;
+	if (args->max_inodes<9) args->max_inodes=9;
+	if (args->reserved_blocks > args->max_blocks-2)
+		args->reserved_blocks = args->max_blocks-2;
+	if (args->reserved_inodes > args->max_inodes-4)
+		args->reserved_inodes = args->max_inodes-4;
+
 	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;
+	unsigned long blocks;
+	unsigned long inodes;
 	struct shmem_sb_info *info = &sb->u.shmem_sb;
+	struct shmem_sb_info args;
+
+	/* FIXME: We unlock the stats while parsing options.
+	 *        Is that a good thing to do? In fact I don't
+	 *        think it makes any difference. Somebody should
+	 *        consider this closely, can anything go wrong,
+	 *        can it be made more efficient.
+	 */
+
+	spin_lock(&info->stat_lock);
+	args=*info;
+	spin_unlock(&info->stat_lock);
 
-	if (shmem_parse_options (data, NULL, &max_blocks, &max_inodes))
+	if (shmem_parse_options (data, NULL, &args))
 		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)
+	if (args.max_blocks < blocks)
 		goto out;
-	if (max_inodes < inodes)
+	if (args.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;
+	info->max_blocks      = args.max_blocks;
+	info->free_blocks     = args.max_blocks - blocks;
+	info->max_inodes      = args.max_inodes;
+	info->free_inodes     = args.max_inodes - inodes;
+	info->reserved_blocks = args.reserved_blocks;
+	info->reserved_inodes = args.reserved_inodes;
+	info->reserved_mem    = args.reserved_mem;
+	info->reserved_uid    = args.reserved_uid;
 out:
 	spin_unlock(&info->stat_lock);
 	return error;
@@ -1058,22 +1157,27 @@
 {
 	struct inode * inode;
 	struct dentry * root;
-	unsigned long blocks = ULONG_MAX;	/* unlimited */
-	unsigned long inodes = ULONG_MAX;	/* unlimited */
+	struct shmem_sb_info * info = &sb->u.shmem_sb;
 	int mode   = S_IRWXUGO | S_ISVTX;
 
+	info->max_blocks=ULONG_MAX;	/* unlimited */
+	info->max_inodes=ULONG_MAX;	/* unlimited */
+	info->reserved_blocks = 0;
+	info->reserved_inodes = 0;
+	info->reserved_mem = 8192;	/* 32MB */
+	info->reserved_uid = 0;		/* root */
+
 #ifdef CONFIG_TMPFS
-	if (shmem_parse_options (data, &mode, &blocks, &inodes)) {
+	if (shmem_parse_options (data, &mode, info )) {
 		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;
-	sb->u.shmem_sb.free_blocks = blocks;
-	sb->u.shmem_sb.max_inodes = inodes;
-	sb->u.shmem_sb.free_inodes = inodes;
+	spin_lock_init (&info->stat_lock);
+	info->free_blocks = info->max_blocks;
+	info->free_inodes = info->max_inodes;
+
 	sb->s_maxbytes = (unsigned long long)(SHMEM_NR_DIRECT + (ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)) << PAGE_CACHE_SHIFT;
 	sb->s_blocksize = PAGE_CACHE_SIZE;
 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
