From 9dec3c4d306b09b31331e475e895bb9674e16d81 Mon Sep 17 00:00:00 2001
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Thu, 8 May 2008 21:02:45 -0400
Subject: [PATCH] [PATCH] dup_fd() part 2

use alloc_fdtable() instead of expand_files(), get rid of pointless
grabbing newf->file_lock, kill magic in copy_fdtable() that used to
be there only to skip copying when called from dup_fd().

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/file.c | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/fs/file.c b/fs/file.c
index 7dbadaaf00f..6491b2b5bc3 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -119,8 +119,6 @@ static void copy_fdtable(struct fdtable *nfdt, struct fdtable *ofdt)
 	unsigned int cpy, set;
 
 	BUG_ON(nfdt->max_fds < ofdt->max_fds);
-	if (ofdt->max_fds == 0)
-		return;
 
 	cpy = ofdt->max_fds * sizeof(struct file *);
 	set = (nfdt->max_fds - ofdt->max_fds) * sizeof(struct file *);
@@ -327,14 +325,24 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
 	 * Note: we're not a clone task, so the open count won't change.
 	 */
 	if (open_files > new_fdt->max_fds) {
-		new_fdt->max_fds = 0;
 		spin_unlock(&oldf->file_lock);
-		spin_lock(&newf->file_lock);
-		*errorp = expand_files(newf, open_files-1);
-		spin_unlock(&newf->file_lock);
-		if (*errorp < 0)
+
+		new_fdt = alloc_fdtable(open_files - 1);
+		if (!new_fdt) {
+			*errorp = -ENOMEM;
+			goto out_release;
+		}
+
+		/* beyond sysctl_nr_open; nothing to do */
+		if (unlikely(new_fdt->max_fds < open_files)) {
+			free_fdarr(new_fdt);
+			free_fdset(new_fdt);
+			kfree(new_fdt);
+			*errorp = -EMFILE;
 			goto out_release;
-		new_fdt = files_fdtable(newf);
+		}
+		rcu_assign_pointer(files->fdt, new_fdt);
+
 		/*
 		 * Reacquire the oldf lock and a pointer to its fd table
 		 * who knows it may have a new bigger fd table. We need
-- 
2.20.1