aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fcntl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r--fs/fcntl.c53
1 files changed, 27 insertions, 26 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 2e40799daad6..ac4f7db9f134 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -63,31 +63,35 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
63 return -EINVAL; 63 return -EINVAL;
64 64
65 spin_lock(&files->file_lock); 65 spin_lock(&files->file_lock);
66 if (!(file = fcheck(oldfd)))
67 goto out_unlock;
68 get_file(file); /* We are now finished with oldfd */
69
70 err = expand_files(files, newfd); 66 err = expand_files(files, newfd);
67 file = fcheck(oldfd);
68 if (unlikely(!file))
69 goto Ebadf;
71 if (unlikely(err < 0)) { 70 if (unlikely(err < 0)) {
72 if (err == -EMFILE) 71 if (err == -EMFILE)
73 err = -EBADF; 72 goto Ebadf;
74 goto out_fput; 73 goto out_unlock;
75 } 74 }
76 75 /*
77 /* To avoid races with open() and dup(), we will mark the fd as 76 * We need to detect attempts to do dup2() over allocated but still
78 * in-use in the open-file bitmap throughout the entire dup2() 77 * not finished descriptor. NB: OpenBSD avoids that at the price of
79 * process. This is quite safe: do_close() uses the fd array 78 * extra work in their equivalent of fget() - they insert struct
80 * entry, not the bitmap, to decide what work needs to be 79 * file immediately after grabbing descriptor, mark it larval if
81 * done. --sct */ 80 * more work (e.g. actual opening) is needed and make sure that
82 /* Doesn't work. open() might be there first. --AV */ 81 * fget() treats larval files as absent. Potentially interesting,
83 82 * but while extra work in fget() is trivial, locking implications
84 /* Yes. It's a race. In user space. Nothing sane to do */ 83 * and amount of surgery on open()-related paths in VFS are not.
84 * FreeBSD fails with -EBADF in the same situation, NetBSD "solution"
85 * deadlocks in rather amusing ways, AFAICS. All of that is out of
86 * scope of POSIX or SUS, since neither considers shared descriptor
87 * tables and this condition does not arise without those.
88 */
85 err = -EBUSY; 89 err = -EBUSY;
86 fdt = files_fdtable(files); 90 fdt = files_fdtable(files);
87 tofree = fdt->fd[newfd]; 91 tofree = fdt->fd[newfd];
88 if (!tofree && FD_ISSET(newfd, fdt->open_fds)) 92 if (!tofree && FD_ISSET(newfd, fdt->open_fds))
89 goto out_fput; 93 goto out_unlock;
90 94 get_file(file);
91 rcu_assign_pointer(fdt->fd[newfd], file); 95 rcu_assign_pointer(fdt->fd[newfd], file);
92 FD_SET(newfd, fdt->open_fds); 96 FD_SET(newfd, fdt->open_fds);
93 if (flags & O_CLOEXEC) 97 if (flags & O_CLOEXEC)
@@ -98,17 +102,14 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
98 102
99 if (tofree) 103 if (tofree)
100 filp_close(tofree, files); 104 filp_close(tofree, files);
101 err = newfd;
102out:
103 return err;
104out_unlock:
105 spin_unlock(&files->file_lock);
106 goto out;
107 105
108out_fput: 106 return newfd;
107
108Ebadf:
109 err = -EBADF;
110out_unlock:
109 spin_unlock(&files->file_lock); 111 spin_unlock(&files->file_lock);
110 fput(file); 112 return err;
111 goto out;
112} 113}
113 114
114asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) 115asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)