--- arch/alpha/kernel/process.c.~1~	Wed Jan 20 19:08:43 1999
+++ arch/alpha/kernel/process.c	Fri Mar 19 01:00:51 1999
@@ -55,7 +55,6 @@
 unsigned long init_user_stack[1024] = { STACK_MAGIC, };
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
-static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
--- arch/i386/kernel/init_task.c.~1~	Sun Sep 13 20:16:22 1998
+++ arch/i386/kernel/init_task.c	Fri Mar 19 01:00:55 1999
@@ -7,7 +7,6 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
-static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
--- arch/m68k/kernel/process.c.~1~	Tue Jan 19 18:58:34 1999
+++ arch/m68k/kernel/process.c	Fri Mar 19 01:00:55 1999
@@ -40,7 +40,6 @@
  */
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
-static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
--- arch/mips/kernel/init_task.c.~1~	Fri May  8 08:13:23 1998
+++ arch/mips/kernel/init_task.c	Fri Mar 19 01:00:55 1999
@@ -6,7 +6,6 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
-static struct files * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
--- arch/mips/kernel/irixioctl.c.~1~	Tue Oct 20 21:52:54 1998
+++ arch/mips/kernel/irixioctl.c	Fri Mar 19 01:00:55 1999
@@ -33,7 +33,7 @@
 {
 	struct file *filp;
 
-	if(fd >= NR_OPEN || !(filp = current->files->fd[fd]))
+	if(fd >= current->files->max_fds || !(filp = current->files->fd[fd]))
 		return ((struct tty_struct *) 0);
 	if(filp->private_data) {
 		struct tty_struct *ttyp = (struct tty_struct *) filp->private_data;
--- arch/mips/kernel/sysirix.c.~1~	Thu Nov  5 17:58:29 1998
+++ arch/mips/kernel/sysirix.c	Fri Mar 19 01:00:55 1999
@@ -788,7 +788,8 @@
 	error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statfs));
 	if (error)
 		goto out;
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd])) {
+	if (fd >= current->files->max_fds ||
+	    !(file = current->files->fd[fd])) {
 		error = -EBADF;
 		goto out;
 	}
@@ -1111,7 +1112,8 @@
 
 	lock_kernel();
 	if(!(flags & MAP_ANONYMOUS)) {
-		if(fd >= NR_OPEN || !(file = current->files->fd[fd])) {
+		if(fd >= current->files->max_fds ||
+		   !(file = current->files->fd[fd])) {
 			retval = -EBADF;
 			goto out;
 		}
@@ -1583,7 +1585,8 @@
 	error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs));
 	if (error)
 		goto out;
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd])) {
+	if (fd >= current->files->max_fds ||
+	    !(file = current->files->fd[fd])) {
 		error = -EBADF;
 		goto out;
 	}
@@ -1727,7 +1730,8 @@
 	}
 
 	if(!(flags & MAP_ANONYMOUS)) {
-		if(fd >= NR_OPEN || !(file = current->files->fd[fd])) {
+		if(fd >= current->files->max_fds || 
+		   !(file = current->files->fd[fd])) {
 			error = -EBADF;
 			goto out;
 		}
@@ -1879,7 +1883,8 @@
 	error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs));
 	if (error)
 		goto out;
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd])) {
+	if (fd >= current->files->max_fds ||
+	    !(file = current->files->fd[fd])) {
 		error = -EBADF;
 		goto out;
 	}
@@ -2040,7 +2045,8 @@
 	       current->pid, fd, dirent, count, eob);
 #endif
 	error = -EBADF;
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	if (fd >= current->files->max_fds ||
+	    !(file = current->files->fd[fd]))
 		goto out;
 
 	dentry = file->f_dentry;
@@ -2151,7 +2157,8 @@
 	       current->pid, fd, dirent, cnt);
 #endif
 	error = -EBADF;
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	if (fd >= current->files->max_fds ||
+	    !(file = current->files->fd[fd]))
 		goto out;
 
 	dentry = file->f_dentry;
@@ -2212,7 +2219,8 @@
 	       current->pid, fd, dirent, cnt);
 #endif
 	error = -EBADF;
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	if (fd >= current->files->max_fds ||
+	    !(file = current->files->fd[fd]))
 		goto out;
 
 	dentry = file->f_dentry;
--- arch/ppc/kernel/process.c.~1~	Thu Jan  7 20:06:57 1999
+++ arch/ppc/kernel/process.c	Fri Mar 19 01:00:56 1999
@@ -67,7 +67,6 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
-static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 
--- arch/ppc/kernel/syscalls.c.~1~	Wed Sep 30 18:14:17 1998
+++ arch/ppc/kernel/syscalls.c	Fri Mar 19 01:00:56 1999
@@ -205,7 +205,8 @@
 
 	lock_kernel();
 	if (!(flags & MAP_ANONYMOUS)) {
-		if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+		if (fd >= current->files->max_fds ||
+		    !(file = current->files->fd[fd]))
 			goto out;
 	}
 	
--- arch/sparc/kernel/init_task.c.~1~	Tue Oct 27 17:52:20 1998
+++ arch/sparc/kernel/init_task.c	Fri Mar 19 01:00:56 1999
@@ -6,7 +6,6 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
-static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
--- arch/sparc64/kernel/init_task.c.~1~	Wed Apr 15 01:44:20 1998
+++ arch/sparc64/kernel/init_task.c	Fri Mar 19 01:00:56 1999
@@ -6,7 +6,6 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
-static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
--- arch/sparc64/solaris/timod.c.~1~	Wed Apr 15 01:44:21 1998
+++ arch/sparc64/solaris/timod.c	Fri Mar 19 01:00:56 1999
@@ -866,7 +866,7 @@
 
 	SOLD("entry");
 	lock_kernel();
-	if(fd >= NR_OPEN) goto out;
+	if(fd >= current->files->max_fds) goto out;
 
 	filp = current->files->fd[fd];
 	if(!filp) goto out;
@@ -933,7 +933,7 @@
 
 	SOLD("entry");
 	lock_kernel();
-	if(fd >= NR_OPEN) goto out;
+	if(fd >= current->files->max_fds) goto out;
 
 	filp = current->files->fd[fd];
 	if(!filp) goto out;
--- fs/Makefile.~1~	Mon Aug 31 21:01:35 1998
+++ fs/Makefile	Fri Mar 19 01:00:56 1999
@@ -13,7 +13,7 @@
 O_OBJS    = open.o read_write.o devices.o file_table.o buffer.o \
 		super.o  block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
 		ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
-		dcache.o inode.o attr.o bad_inode.o $(BINFMTS) 
+		dcache.o inode.o attr.o bad_inode.o file.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
--- fs/exec.c.~1~	Mon Jan 18 21:47:38 1999
+++ fs/exec.c	Fri Mar 19 01:00:56 1999
@@ -479,10 +479,10 @@
 		unsigned long set, i;
 
 		i = j * __NFDBITS;
-		if (i >= files->max_fds)
+		if (i >= files->max_fds || i >= files->max_fdset)
 			break;
-		set = files->close_on_exec.fds_bits[j];
-		files->close_on_exec.fds_bits[j] = 0;
+		set = files->close_on_exec->fds_bits[j];
+		files->close_on_exec->fds_bits[j] = 0;
 		j++;
 		for ( ; set ; i++,set >>= 1) {
 			if (set & 1)
--- fs/fcntl.c.~1~	Fri Nov 13 18:07:26 1998
+++ fs/fcntl.c	Fri Mar 19 01:00:56 1999
@@ -12,14 +12,15 @@
 
 extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
 
-static inline int dupfd(unsigned int fd, unsigned int arg)
+static inline int dupfd(unsigned int fd, unsigned int start)
 {
 	struct files_struct * files = current->files;
 	struct file * file;
+	unsigned int newfd;
 	int error;
 
 	error = -EINVAL;
-	if (arg >= NR_OPEN)
+	if (start >= NR_OPEN)
 		goto out;
 
 	error = -EBADF;
@@ -27,15 +28,39 @@
 	if (!file)
 		goto out;
 
+repeat:
 	error = -EMFILE;
-	arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg);
-	if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+	if (start < files->next_fd)
+		start = files->next_fd;
+	/* At this point, start MUST be <= max_fdset */
+#if 1
+	if (start > files->max_fdset)
+		printk (KERN_ERR "dupfd: fd %d, max %d\n", 
+			start, files->max_fdset);
+#endif
+	newfd = find_next_zero_bit(files->open_fds->fds_bits, 
+				files->max_fdset,
+				start);
+	if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
 		goto out_putf;
-	FD_SET(arg, &files->open_fds);
-	FD_CLR(arg, &files->close_on_exec);
-	fd_install(arg, file);
-	error = arg;
+
+	error = expand_files(files, newfd);
+	if (error < 0)
+		goto out_putf;
+	if (error) /* If we might have blocked, try again. */
+		goto repeat;
+
+	FD_SET(newfd, files->open_fds);
+	FD_CLR(newfd, files->close_on_exec);
+	if (start <= files->next_fd)
+		files->next_fd = newfd + 1;
+	fd_install(newfd, file);
+	error = newfd;
 out:
+#ifdef FDSET_DEBUG	
+	if (error < 0)
+		printk (KERN_ERR __FUNCTION__ ": return %d\n", error);
+#endif
 	return error;
 
 out_putf:
@@ -48,18 +73,30 @@
 	int err = -EBADF;
 
 	lock_kernel();
+#ifdef FDSET_DEBUG	
+	printk (KERN_ERR __FUNCTION__ " 0: oldfd = %d, newfd = %d\n", 
+		oldfd, newfd);
+#endif
 	if (!fcheck(oldfd))
 		goto out;
+	if (newfd >= NR_OPEN)
+		goto out;	/* following POSIX.1 6.2.1 */
+
 	err = newfd;
 	if (newfd == oldfd)
 		goto out;
-	err = -EBADF;
-	if (newfd >= NR_OPEN)
-		goto out;	/* following POSIX.1 6.2.1 */
 
+	/* We must be able to do the fd setting inside dupfd() without
+           blocking after the sys_close(). */
+	if ((err = expand_files(current->files, newfd)) < 0)
+		goto out;
+	
 	sys_close(newfd);
 	err = dupfd(oldfd, newfd);
 out:
+#ifdef FDSET_DEBUG	
+	printk (KERN_ERR __FUNCTION__ ": return %d\n", err);
+#endif
 	unlock_kernel();
 	return err;
 }
@@ -71,6 +108,10 @@
 	lock_kernel();
 	ret = dupfd(fildes, 0);
 	unlock_kernel();
+#ifdef FDSET_DEBUG	
+	if (ret < 0)
+		printk (KERN_ERR __FUNCTION__ ": return %d\n", ret);
+#endif
 	return ret;
 }
 
@@ -111,19 +152,20 @@
 	filp = fget(fd);
 	if (!filp)
 		goto out;
+
 	err = 0;
 	switch (cmd) {
 		case F_DUPFD:
 			err = dupfd(fd, arg);
 			break;
 		case F_GETFD:
-			err = FD_ISSET(fd, &current->files->close_on_exec);
+			err = FD_ISSET(fd, current->files->close_on_exec);
 			break;
 		case F_SETFD:
 			if (arg&1)
-				FD_SET(fd, &current->files->close_on_exec);
+				FD_SET(fd, current->files->close_on_exec);
 			else
-				FD_CLR(fd, &current->files->close_on_exec);
+				FD_CLR(fd, current->files->close_on_exec);
 			break;
 		case F_GETFL:
 			err = filp->f_flags;
@@ -151,7 +193,6 @@
 			err = filp->f_owner.pid;
 			break;
 		case F_SETOWN:
-			err = 0;
 			filp->f_owner.pid = arg;
 			filp->f_owner.uid = current->uid;
 			filp->f_owner.euid = current->euid;
@@ -171,10 +212,9 @@
 			break;
 		default:
 			/* sockets need a few special fcntls. */
+			err = -EINVAL;
 			if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
 				err = sock_fcntl (filp, cmd, arg);
-			else
-				err = -EINVAL;
 			break;
 	}
 	fput(filp);
--- fs/file.c.~1~	Fri Mar 19 01:00:56 1999
+++ fs/file.c	Fri Mar 19 01:00:56 1999
@@ -0,0 +1,224 @@
+/*
+ *  linux/fs/open.c
+ *
+ *  Copyright (C) 1998, Stephen Tweedie and Bill Hawes
+ *
+ *  Manage the dynamic fd arrays in the process files_struct.
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+
+#include <asm/bitops.h>
+
+
+/*
+ * Allocate an fd array, using get_free_page() if possible.
+ * Note: the array isn't cleared at allocation time.
+ */
+struct file ** alloc_fd_array(int num)
+{
+	struct file **new_fds;
+	int size = num * sizeof(struct file *);
+
+	if (size < PAGE_SIZE)
+		new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
+	else if (size == PAGE_SIZE)
+		new_fds = (struct file **) __get_free_page(GFP_KERNEL);
+	else
+		new_fds = (struct file **) vmalloc(size);
+	return new_fds;
+}
+
+void free_fd_array(struct file **array, int num)
+{
+	int size = num * sizeof(struct file *);
+
+	if (!array) {
+		printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num);
+		return;
+	}
+
+	if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */
+		return;
+	else if (size < PAGE_SIZE)
+		kfree(array);
+	else if (size == PAGE_SIZE)
+		free_page((unsigned long) array);
+	else
+		vfree(array);
+}
+
+/*
+ * Expand the fd array in the files_struct.
+ */
+
+int expand_fd_array(struct files_struct *files, int nr)
+{
+	struct file **new_fds;
+	int error, nfds;
+
+	
+	error = -EMFILE;
+	if (files->max_fds >= NR_OPEN || nr > NR_OPEN)
+		goto out;
+
+	nfds = files->max_fds;
+
+	/* 
+	 * Expand to the max in easy steps, and keep expanding it until
+	 * we have enough for the requested fd array size. 
+	 */
+
+	do {
+#if NR_OPEN_DEFAULT < 256
+		if (nfds < 256)
+			nfds = 256;
+		else 
+#endif
+		if (nfds < (PAGE_SIZE / sizeof(struct file *)))
+			nfds = PAGE_SIZE / sizeof(struct file *);
+		else {
+			nfds = nfds * 2;
+			if (nfds > NR_OPEN)
+				nfds = NR_OPEN;
+		}
+	} while (nfds < nr);
+
+	error = -ENOMEM;
+	new_fds = alloc_fd_array(nfds);
+	if (!new_fds)
+		goto out;
+
+	/* Copy the existing array and install the new pointer */
+
+	if (nfds > files->max_fds) {
+		struct file **old_fds;
+		int i = files->max_fds;
+		
+		old_fds = files->fd;
+		files->fd = new_fds;
+		files->max_fds = nfds;
+		/* Don't copy/clear the array if we are creating a new
+		   fd array for fork() */
+		if (i) {
+			memcpy(new_fds, old_fds, i * sizeof(struct file *));
+			/* clear the remainder of the array */
+			memset(&new_fds[i], 0,
+			       (nfds-i) * sizeof(struct file *)); 
+			free_fd_array(old_fds, i);
+		}
+	} else {
+		/* Somebody expanded the array while we slept ... */
+		free_fd_array(new_fds, nfds);
+	}
+	error = 0;
+out:
+	return error;
+}
+
+/*
+ * Allocate an fdset array, using get_free_page() if possible.
+ * Note: the array isn't cleared at allocation time.
+ */
+fd_set * alloc_fdset(int num)
+{
+	fd_set *new_fdset;
+	int size = num / 8;
+
+	if (size < PAGE_SIZE)
+		new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL);
+	else if (size == PAGE_SIZE)
+		new_fdset = (fd_set *) __get_free_page(GFP_KERNEL);
+	else
+		new_fdset = (fd_set *) vmalloc(size);
+	return new_fdset;
+}
+
+void free_fdset(fd_set *array, int num)
+{
+	int size = num / 8;
+
+	if (!array) {
+		printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num);
+		return;
+	}
+	
+	if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */
+		return;
+	else if (size < PAGE_SIZE)
+		kfree(array);
+	else if (size == PAGE_SIZE)
+		free_page((unsigned long) array);
+	else
+		vfree(array);
+}
+
+/*
+ * Expand the fdset in the files_struct.
+ */
+int expand_fdset(struct files_struct *files, int nr)
+{
+	fd_set *new_openset = 0, *new_execset = 0;
+	int error, nfds = 0;
+
+	error = -EMFILE;
+	if (files->max_fdset >= NR_OPEN || nr > NR_OPEN)
+		goto out;
+
+	nfds = files->max_fdset;
+	/* Expand to the max in easy steps */
+	do {
+		if (nfds < (PAGE_SIZE * 8))
+			nfds = PAGE_SIZE * 8;
+		else {
+			nfds = nfds * 2;
+			if (nfds > NR_OPEN)
+				nfds = NR_OPEN;
+		}
+	} while (nfds < nr);
+
+	error = -ENOMEM;
+	new_openset = alloc_fdset(nfds);
+	new_execset = alloc_fdset(nfds);
+	if (!new_openset || !new_execset)
+		goto out;
+
+	error = 0;
+	
+	/* Copy the existing tables and install the new pointers */
+	if (nfds > files->max_fdset) {
+		int i = files->max_fdset / (sizeof(unsigned long) * 8);
+		int count = (nfds - files->max_fdset) / 8;
+
+		/* 
+		 * Don't copy the entire array if the current fdset is
+		 * not yet initialised.  
+		 */
+		if (i) {
+			memcpy (new_openset, files->open_fds, files->max_fdset/8);
+			memcpy (new_execset, files->close_on_exec, files->max_fdset/8);
+			memset (&new_openset->fds_bits[i], 0, count);
+			memset (&new_execset->fds_bits[i], 0, count);
+		}
+
+		free_fdset (files->close_on_exec, files->max_fdset);
+		free_fdset (files->open_fds, files->max_fdset);
+		files->max_fdset = nfds;
+		files->open_fds = new_openset;
+		files->close_on_exec = new_execset;
+		return 0;
+	} 
+	/* Somebody expanded the array while we slept ... */
+
+out:
+	if (new_openset)
+		free_fdset(new_openset, nfds);
+	if (new_execset)
+		free_fdset(new_execset, nfds);
+	return error;
+}
+
--- fs/ioctl.c.~1~	Fri Nov 13 18:07:26 1998
+++ fs/ioctl.c	Fri Mar 19 01:00:56 1999
@@ -52,11 +52,11 @@
 	error = 0;
 	switch (cmd) {
 		case FIOCLEX:
-			FD_SET(fd, &current->files->close_on_exec);
+			FD_SET(fd, current->files->close_on_exec);
 			break;
 
 		case FIONCLEX:
-			FD_CLR(fd, &current->files->close_on_exec);
+			FD_CLR(fd, current->files->close_on_exec);
 			break;
 
 		case FIONBIO:
--- fs/open.c.~1~	Wed Mar 17 16:12:22 1999
+++ fs/open.c	Fri Mar 19 01:00:56 1999
@@ -682,9 +682,13 @@
 {
 	struct files_struct * files = current->files;
 	int fd, error;
-
+	
+repeat:
 	error = -EMFILE;
-	fd = find_first_zero_bit(&files->open_fds, NR_OPEN);
+
+	fd = find_next_zero_bit(files->open_fds, 
+				current->files->max_fdset, 
+				files->next_fd);
 	/*
 	 * N.B. For clone tasks sharing a files structure, this test
 	 * will limit the total number of files that can be opened.
@@ -692,10 +696,27 @@
 	if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
 		goto out;
 
-	/* Check here for fd > files->max_fds to do dynamic expansion */
+	/* Do we need to expand the fdset array? */
+	if (fd >= current->files->max_fdset) {
+		error = expand_fdset(files, 0);
+		if (!error)
+			goto repeat;
+		goto out;
+	}
+	
+	/* 
+	 * Check whether we need to expand the fd array.
+	 */
+	if (fd >= files->max_fds) {
+		error = expand_fd_array(files, 0);
+		if (!error)
+			goto repeat;
+		goto out;
+	}
 
-	FD_SET(fd, &files->open_fds);
-	FD_CLR(fd, &files->close_on_exec);
+	FD_SET(fd, files->open_fds);
+	FD_CLR(fd, files->close_on_exec);
+	files->next_fd = fd + 1;
 #if 1
 	/* Sanity check */
 	if (files->fd[fd] != NULL) {
@@ -706,12 +727,18 @@
 	error = fd;
 
 out:
+#ifdef FDSET_DEBUG	
+	if (error < 0)
+		printk (KERN_ERR __FUNCTION__ ": return %d\n", error);
+#endif
 	return error;
 }
 
 inline void put_unused_fd(unsigned int fd)
 {
-	FD_CLR(fd, &current->files->open_fds);
+	FD_CLR(fd, current->files->open_fds);
+	if (fd < current->files->next_fd)
+		current->files->next_fd = fd;
 }
 
 asmlinkage int sys_open(const char * filename, int flags, int mode)
@@ -816,7 +843,7 @@
 		struct files_struct * files = current->files;
 		files->fd[fd] = NULL;
 		put_unused_fd(fd);
-		FD_CLR(fd, &files->close_on_exec);
+		FD_CLR(fd, files->close_on_exec);
 		error = close_fp(filp, files);
 	}
 	unlock_kernel();
--- fs/proc/array.c.~1~	Wed Mar 17 16:12:27 1999
+++ fs/proc/array.c	Fri Mar 19 01:00:56 1999
@@ -709,11 +709,13 @@
 		"PPid:\t%d\n"
 		"Uid:\t%d\t%d\t%d\t%d\n"
 		"Gid:\t%d\t%d\t%d\t%d\n"
+		"FDSize:\t%d\n"
 		"Groups:\t",
 		get_task_state(p),
 		p->pid, p->p_pptr->pid,
 		p->uid, p->euid, p->suid, p->fsuid,
-		p->gid, p->egid, p->sgid, p->fsgid);
+		p->gid, p->egid, p->sgid, p->fsgid,
+		p->files ? p->files->max_fds : 0);
 
 	for (g = 0; g < p->ngroups; g++)
 		buffer += sprintf(buffer, "%d ", p->groups[g]);
--- fs/select.c.~1~	Tue Jan 19 18:43:08 1999
+++ fs/select.c	Fri Mar 19 01:02:15 1999
@@ -107,7 +107,7 @@
 	/* handle last in-complete long-word first */
 	set = ~(~0UL << (n & (__NFDBITS-1)));
 	n /= __NFDBITS;
-	open_fds = current->files->open_fds.fds_bits+n;
+	open_fds = current->files->open_fds->fds_bits+n;
 	max = 0;
 	if (set) {
 		set &= BITS(fds, n);
@@ -379,7 +379,7 @@
 	lock_kernel();
 	/* Do a sanity check on nfds ... */
 	err = -EINVAL;
-	if (nfds > NR_OPEN)
+	if (nfds > current->files->max_fds)
 		goto out;
 
 	if (timeout) {
--- include/asm-alpha/resource.h.~1~	Fri Aug 23 13:30:14 1996
+++ include/asm-alpha/resource.h	Fri Mar 19 01:00:56 1999
@@ -28,7 +28,7 @@
     {_STK_LIM, _STK_LIM},			/* RLIMIT_STACK */	\
     {       0, LONG_MAX},			/* RLIMIT_CORE */	\
     {LONG_MAX, LONG_MAX},			/* RLIMIT_RSS */	\
-    { NR_OPEN,  NR_OPEN},			/* RLIMIT_NOFILE */	\
+    {INR_OPEN, INR_OPEN},			/* RLIMIT_NOFILE */	\
     {LONG_MAX, LONG_MAX},			/* RLIMIT_AS */		\
     {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	/* RLIMIT_NPROC */	\
     {LONG_MAX, LONG_MAX},			/* RLIMIT_MEMLOCK */	\
--- include/asm-arm/resource.h.~1~	Wed Jan 21 00:39:43 1998
+++ include/asm-arm/resource.h	Fri Mar 19 01:00:56 1999
@@ -29,7 +29,7 @@
 	{        0, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 	{ MAX_TASKS_PER_USER, MAX_TASKS_PER_USER },	\
-	{ NR_OPEN, NR_OPEN },				\
+	{ INR_OPEN, INR_OPEN },				\
 	{ LONG_MAX, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 }
--- include/asm-i386/resource.h.~1~	Thu Nov 19 05:06:56 1998
+++ include/asm-i386/resource.h	Fri Mar 19 01:00:56 1999
@@ -29,7 +29,7 @@
 	{        0, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 	{ MAX_TASKS_PER_USER, MAX_TASKS_PER_USER },	\
-	{ NR_OPEN, NR_OPEN },				\
+	{ INR_OPEN, INR_OPEN },				\
 	{ LONG_MAX, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 }
--- include/asm-m68k/resource.h.~1~	Tue Jan  5 19:20:43 1999
+++ include/asm-m68k/resource.h	Fri Mar 19 01:00:56 1999
@@ -29,7 +29,7 @@
   {       0, LONG_MAX}, \
   {LONG_MAX, LONG_MAX}, \
   {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \
-  {NR_OPEN, NR_OPEN},   \
+  {INR_OPEN, INR_OPEN}, \
   {LONG_MAX, LONG_MAX}, \
   {LONG_MAX, LONG_MAX}  \
 }
--- include/asm-mips/resource.h.~1~	Thu Jun 26 20:33:40 1997
+++ include/asm-mips/resource.h	Fri Mar 19 01:00:56 1999
@@ -35,7 +35,7 @@
 	{LONG_MAX, LONG_MAX},				\
 	{_STK_LIM, _STK_LIM},				\
 	{       0, LONG_MAX},				\
-	{NR_OPEN, NR_OPEN},				\
+	{INR_OPEN, INR_OPEN},				\
 	{LONG_MAX, LONG_MAX},				\
 	{LONG_MAX, LONG_MAX},				\
 	{MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	\
--- include/asm-ppc/resource.h.~1~	Mon Dec 21 16:37:24 1998
+++ include/asm-ppc/resource.h	Fri Mar 19 01:00:56 1999
@@ -25,7 +25,7 @@
     {       0, LONG_MAX},			/* RLIMIT_CORE */	\
     {LONG_MAX, LONG_MAX},			/* RLIMIT_RSS */	\
     {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	/* RLIMIT_NPROC */	\
-    { NR_OPEN,  NR_OPEN},			/* RLIMIT_NOFILE */	\
+    {INR_OPEN, INR_OPEN},			/* RLIMIT_NOFILE */	\
     {LONG_MAX, LONG_MAX},			/* RLIMIT_MEMLOCK */	\
     {LONG_MAX, LONG_MAX},			/* RLIMIT_AS */		\
 }
--- include/asm-sparc/resource.h.~1~	Fri Dec 13 09:37:40 1996
+++ include/asm-sparc/resource.h	Fri Mar 19 01:00:56 1999
@@ -31,7 +31,7 @@
     {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},				\
     {LONG_MAX, LONG_MAX}, {_STK_LIM, _STK_LIM},				\
     {       0, LONG_MAX}, {LONG_MAX, LONG_MAX},				\
-    {NR_OPEN, NR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	\
+    {INR_OPEN, INR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	\
     {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}                          \
 }
 
--- include/asm-sparc64/resource.h.~1~	Thu Jun 26 20:33:40 1997
+++ include/asm-sparc64/resource.h	Fri Mar 19 01:00:56 1999
@@ -30,7 +30,7 @@
     {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},				\
     {LONG_MAX, LONG_MAX}, {_STK_LIM, _STK_LIM},				\
     {       0, LONG_MAX}, {LONG_MAX, LONG_MAX},				\
-    {NR_OPEN, NR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	\
+    {INR_OPEN, INR_OPEN}, {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER},	\
     {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}                          \
 }
 
--- include/linux/fs.h.~1~	Thu Mar 18 15:18:01 1999
+++ include/linux/fs.h	Fri Mar 19 01:00:56 1999
@@ -27,17 +27,19 @@
 
 
 /*
- * It's silly to have NR_OPEN bigger than NR_FILE, but I'll fix
- * that later. Anyway, now the file code is no longer dependent
- * on bitmaps in unsigned longs, but uses the new fd_set structure..
+ * It's silly to have NR_OPEN bigger than NR_FILE, but you can change
+ * the file limit at runtime and only root can increase the per-process
+ * nr_file rlimit, so it's safe to set up a ridiculously high absolute
+ * upper limit on files-per-process.
  *
  * Some programs (notably those using select()) may have to be 
- * recompiled to take full advantage of the new limits..
+ * recompiled to take full advantage of the new limits..  
  */
 
 /* Fixed constants first: */
 #undef NR_OPEN
-#define NR_OPEN 1024
+#define NR_OPEN (1024*1024)	/* Absolute upper limit on fd num */
+#define INR_OPEN 1024		/* Initial setting for nfile rlimits */
 
 #define BLOCK_SIZE_BITS 10
 #define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
@@ -702,7 +704,11 @@
 #define __getname()	((char *) __get_free_page(GFP_KERNEL))
 #define putname(name)	free_page((unsigned long)(name))
 
+#if 1
 extern void kill_fasync(struct fasync_struct *fa, int sig);
+#else
+extern void kill_fasync(struct fasync_struct *fa, int sig, int band);
+#endif
 extern int register_blkdev(unsigned int, const char *, struct file_operations *);
 extern int unregister_blkdev(unsigned int major, const char * name);
 extern int blkdev_open(struct inode * inode, struct file * filp);
--- include/linux/limits.h.~1~	Tue Dec  2 21:44:40 1997
+++ include/linux/limits.h	Fri Mar 19 01:00:56 1999
@@ -1,7 +1,7 @@
 #ifndef _LINUX_LIMITS_H
 #define _LINUX_LIMITS_H
 
-#define NR_OPEN		1024
+#define NR_OPEN	        1024
 
 #define NGROUPS_MAX       32	/* supplemental group IDs are available */
 #define ARG_MAX       131072	/* # bytes of args + environ for exec() */
--- include/linux/sched.h.~1~	Thu Mar 18 15:18:01 1999
+++ include/linux/sched.h	Fri Mar 19 01:04:12 1999
@@ -123,22 +123,38 @@
 asmlinkage void schedule(void);
 
 /*
+ * The default fd array needs to be at least BITS_PER_LONG,
+ * as this is the granularity returned by copy_fdset().
+ */
+#define NR_OPEN_DEFAULT BITS_PER_LONG
+
+/*
  * Open file table structure
  */
 struct files_struct {
 	atomic_t count;
 	int max_fds;
+	int max_fdset;
+	int next_fd;
 	struct file ** fd;	/* current fd array */
-	fd_set close_on_exec;
-	fd_set open_fds;
+	fd_set *close_on_exec;
+	fd_set *open_fds;
+	fd_set close_on_exec_init;
+	fd_set open_fds_init;
+	struct file * fd_array[NR_OPEN_DEFAULT];
 };
 
 #define INIT_FILES { \
 	ATOMIC_INIT(1), \
-	NR_OPEN, \
-	&init_fd_array[0], \
+	NR_OPEN_DEFAULT, \
+	__FD_SETSIZE, \
+	0, \
+	&init_files.fd_array[0], \
+	&init_files.close_on_exec_init, \
+	&init_files.open_fds_init, \
 	{ { 0, } }, \
-	{ { 0, } } \
+	{ { 0, } }, \
+	{ NULL, } \
 }
 
 struct fs_struct {
@@ -616,6 +632,45 @@
 extern void mmput(struct mm_struct *);
 /* Remove the current tasks stale references to the old mm_struct */
 extern void mm_release(void);
+
+/*
+ * Routines for handling the fd arrays
+ */
+extern struct file ** alloc_fd_array(int);
+extern int expand_fd_array(struct files_struct *, int nr);
+extern void free_fd_array(struct file **, int);
+
+extern fd_set *alloc_fdset(int);
+extern int expand_fdset(struct files_struct *, int nr);
+extern void free_fdset(fd_set *, int);
+
+/* Expand files.  Return <0 on error; 0 nothing done; 1 files expanded,
+ * we may have blocked. */
+static inline int expand_files(struct files_struct *files, int nr)
+{
+	int err, expand = 0;
+#ifdef FDSET_DEBUG	
+	printk (KERN_ERR __FUNCTION__ " %d: nr = %d\n", current->pid, nr);
+#endif
+	
+	if (nr >= files->max_fdset) {
+		expand = 1;
+		if ((err = expand_fdset(files, nr)))
+			goto out;
+	}
+	if (nr >= files->max_fds) {
+		expand = 1;
+		if ((err = expand_fd_array(files, nr)))
+			goto out;
+	}
+	err = expand;
+ out:
+#ifdef FDSET_DEBUG	
+	if (err)
+		printk (KERN_ERR __FUNCTION__ " %d: return %d\n", current->pid, err);
+#endif
+	return err;
+}
 
 extern int  copy_thread(int, unsigned long, unsigned long, struct task_struct *, struct pt_regs *);
 extern void flush_thread(void);
--- kernel/exit.c.~1~	Mon Jan 18 17:55:31 1999
+++ kernel/exit.c	Fri Mar 19 01:00:56 1999
@@ -159,11 +159,11 @@
 
 	j = 0;
 	for (;;) {
-		unsigned long set = files->open_fds.fds_bits[j];
+		unsigned long set;
 		i = j * __NFDBITS;
-		j++;
-		if (i >= files->max_fds)
+		if (i >= files->max_fdset || i >= files->max_fds)
 			break;
+		set = files->open_fds->fds_bits[j++];
 		while (set) {
 			if (set & 1) {
 				struct file * file = files->fd[i];
@@ -189,12 +189,14 @@
 		if (atomic_dec_and_test(&files->count)) {
 			close_files(files);
 			/*
-			 * Free the fd array as appropriate ...
+			 * Free the fd and fdset arrays if we expanded them.
 			 */
-			if (NR_OPEN * sizeof(struct file *) == PAGE_SIZE)
-				free_page((unsigned long) files->fd);
-			else
-				kfree(files->fd);
+			if (files->fd != &files->fd_array[0])
+				free_fd_array(files->fd, files->max_fds);
+			if (files->max_fdset > __FD_SETSIZE) {
+				free_fdset(files->open_fds, files->max_fdset);
+				free_fdset(files->close_on_exec, files->max_fdset);
+			}
 			kmem_cache_free(files_cachep, files);
 		}
 	}
--- kernel/fork.c.~1~	Wed Mar 17 16:12:28 1999
+++ kernel/fork.c	Fri Mar 19 01:19:05 1999
@@ -375,32 +375,11 @@
 	return 0;
 }
 
-/*
- * Copy a fd_set and compute the maximum fd it contains. 
- */
-static inline int __copy_fdset(unsigned long *d, unsigned long *src)
-{
-	int i; 
-	unsigned long *p = src; 
-	unsigned long *max = src; 
-
-	for (i = __FDSET_LONGS; i; --i) {
-		if ((*d++ = *p++) != 0) 
-			max = p; 
-	}
-	return (max - src)*sizeof(long)*8; 
-}
-
-static inline int copy_fdset(fd_set *dst, fd_set *src)
-{
-	return __copy_fdset(dst->fds_bits, src->fds_bits);  
-}
-
 static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
 {
 	struct files_struct *oldf, *newf;
 	struct file **old_fds, **new_fds;
-	int size, i, error = 0;
+	int nfds, size, i, error = 0;
 
 	/*
 	 * A background process may not have any files ...
@@ -420,25 +399,74 @@
 	if (!newf) 
 		goto out;
 
-	/*
-	 * Allocate the fd array, using get_free_page() if possible.
-	 * Eventually we want to make the array size variable ...
-	 */
-	size = NR_OPEN * sizeof(struct file *);
-	if (size == PAGE_SIZE)
-		new_fds = (struct file **) __get_free_page(GFP_KERNEL);
-	else
-		new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
-	if (!new_fds)
-		goto out_release;
+	size = oldf->max_fdset;
+	nfds = NR_OPEN_DEFAULT;
 
+#ifdef FDSET_DEBUG	
+	printk (KERN_ERR __FUNCTION__ " size = %d/%d\n",
+		oldf->max_fds, oldf->max_fdset);
+#endif
 	atomic_set(&newf->count, 1);
-	newf->max_fds = NR_OPEN;
-	newf->fd = new_fds;
-	newf->close_on_exec = oldf->close_on_exec;
-	i = copy_fdset(&newf->open_fds, &oldf->open_fds);
 
+	newf->next_fd	    = 0;
+	newf->max_fds	    = NR_OPEN_DEFAULT;
+	newf->max_fdset	    = __FD_SETSIZE;
+	newf->close_on_exec = &newf->close_on_exec_init;
+	newf->open_fds	    = &newf->open_fds_init;
+	newf->fd	    = &newf->fd_array[0];
+
+	/* Even if the old fdset gets grown here, we'll only copy "size" fds */
+	if (size > __FD_SETSIZE) {
+		newf->max_fdset = 0;
+		error = expand_fdset(newf, size);
+		if (error)
+			goto out_release;
+	}
+	memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, size/8);
+	memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, size/8);
+	if (newf->max_fdset > size) {
+		int left = (newf->max_fdset-size)/8;
+		int start = size / (8 * sizeof(unsigned long));
+		
+		memset(&newf->open_fds->fds_bits[start], 0, left);
+		memset(&newf->close_on_exec->fds_bits[start], 0, left);
+	}
+
+	/* Find the last open fd */
+	for (i = size/(8*sizeof(long)); i > 0; ) {
+		if (newf->open_fds->fds_bits[--i])
+			break;
+	}
+	i = (i+1) * 8 * sizeof(long);
+
+#ifdef FDSET_DEBUG	
+	printk (KERN_ERR __FUNCTION__ " first-free = %d/%d\n", i, size);
+#endif
+
+	/* Do a sanity check ... */
+	if (i > oldf->max_fds)
+		printk(KERN_ERR 
+		       "copy_files: pid %d, open files %d exceeds max %d!\n",
+		       current->pid, i, oldf->max_fds);
+
+	/*
+	 * Check whether we need to allocate a larger fd array.
+	 * Note: we're not a clone task, so the open count won't
+	 * change.
+	 */
+	if (i > NR_OPEN_DEFAULT) {
+		newf->max_fds = 0;
+		error = expand_fd_array(newf, i);
+		if (error)
+			goto out_release;
+		nfds = newf->max_fds;
+	}
+
+	/* compute the remainder to be cleared */
+	size = (nfds - i) * sizeof(struct file *);
 	old_fds = oldf->fd;
+	new_fds = newf->fd;
+
 	for (; i != 0; i--) {
 		struct file *f = *old_fds++;
 		*new_fds = f;
@@ -447,14 +475,20 @@
 		new_fds++;
 	}
 	/* This is long word aligned thus could use a optimized version */ 
-	memset(new_fds, 0, (char *)newf->fd + size - (char *)new_fds); 
+	memset(new_fds, 0, size); 
       
 	tsk->files = newf;
 	error = 0;
 out:
+#ifdef FDSET_DEBUG	
+	if (error)
+		printk (KERN_ERR "copy_files: return %d\n", error);
+#endif
 	return error;
 
 out_release:
+	free_fdset (newf->close_on_exec, newf->max_fdset);
+	free_fdset (newf->open_fds, newf->max_fdset);
 	kmem_cache_free(files_cachep, newf);
 	goto out;
 }
