aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fcntl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r--fs/fcntl.c184
1 files changed, 69 insertions, 115 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 330a7d782591..ac4f7db9f134 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -49,145 +49,94 @@ static int get_close_on_exec(unsigned int fd)
49 return res; 49 return res;
50} 50}
51 51
52/* 52asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
53 * locate_fd finds a free file descriptor in the open_fds fdset,
54 * expanding the fd arrays if necessary. Must be called with the
55 * file_lock held for write.
56 */
57
58static int locate_fd(unsigned int orig_start, int cloexec)
59{
60 struct files_struct *files = current->files;
61 unsigned int newfd;
62 unsigned int start;
63 int error;
64 struct fdtable *fdt;
65
66 spin_lock(&files->file_lock);
67
68 error = -EINVAL;
69 if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
70 goto out;
71
72repeat:
73 fdt = files_fdtable(files);
74 /*
75 * Someone might have closed fd's in the range
76 * orig_start..fdt->next_fd
77 */
78 start = orig_start;
79 if (start < files->next_fd)
80 start = files->next_fd;
81
82 newfd = start;
83 if (start < fdt->max_fds)
84 newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
85 fdt->max_fds, start);
86
87 error = -EMFILE;
88 if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
89 goto out;
90
91 error = expand_files(files, newfd);
92 if (error < 0)
93 goto out;
94
95 /*
96 * If we needed to expand the fs array we
97 * might have blocked - try again.
98 */
99 if (error)
100 goto repeat;
101
102 if (start <= files->next_fd)
103 files->next_fd = newfd + 1;
104
105 FD_SET(newfd, fdt->open_fds);
106 if (cloexec)
107 FD_SET(newfd, fdt->close_on_exec);
108 else
109 FD_CLR(newfd, fdt->close_on_exec);
110 error = newfd;
111
112out:
113 spin_unlock(&files->file_lock);
114 return error;
115}
116
117static int dupfd(struct file *file, unsigned int start, int cloexec)
118{
119 int fd = locate_fd(start, cloexec);
120 if (fd >= 0)
121 fd_install(fd, file);
122 else
123 fput(file);
124
125 return fd;
126}
127
128asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
129{ 53{
130 int err = -EBADF; 54 int err = -EBADF;
131 struct file * file, *tofree; 55 struct file * file, *tofree;
132 struct files_struct * files = current->files; 56 struct files_struct * files = current->files;
133 struct fdtable *fdt; 57 struct fdtable *fdt;
134 58
135 spin_lock(&files->file_lock); 59 if ((flags & ~O_CLOEXEC) != 0)
136 if (!(file = fcheck(oldfd))) 60 return -EINVAL;
137 goto out_unlock;
138 err = newfd;
139 if (newfd == oldfd)
140 goto out_unlock;
141 err = -EBADF;
142 if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
143 goto out_unlock;
144 get_file(file); /* We are now finished with oldfd */
145
146 err = expand_files(files, newfd);
147 if (err < 0)
148 goto out_fput;
149 61
150 /* To avoid races with open() and dup(), we will mark the fd as 62 if (unlikely(oldfd == newfd))
151 * in-use in the open-file bitmap throughout the entire dup2() 63 return -EINVAL;
152 * process. This is quite safe: do_close() uses the fd array
153 * entry, not the bitmap, to decide what work needs to be
154 * done. --sct */
155 /* Doesn't work. open() might be there first. --AV */
156 64
157 /* Yes. It's a race. In user space. Nothing sane to do */ 65 spin_lock(&files->file_lock);
66 err = expand_files(files, newfd);
67 file = fcheck(oldfd);
68 if (unlikely(!file))
69 goto Ebadf;
70 if (unlikely(err < 0)) {
71 if (err == -EMFILE)
72 goto Ebadf;
73 goto out_unlock;
74 }
75 /*
76 * We need to detect attempts to do dup2() over allocated but still
77 * not finished descriptor. NB: OpenBSD avoids that at the price of
78 * extra work in their equivalent of fget() - they insert struct
79 * file immediately after grabbing descriptor, mark it larval if
80 * more work (e.g. actual opening) is needed and make sure that
81 * fget() treats larval files as absent. Potentially interesting,
82 * but while extra work in fget() is trivial, locking implications
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 */
158 err = -EBUSY; 89 err = -EBUSY;
159 fdt = files_fdtable(files); 90 fdt = files_fdtable(files);
160 tofree = fdt->fd[newfd]; 91 tofree = fdt->fd[newfd];
161 if (!tofree && FD_ISSET(newfd, fdt->open_fds)) 92 if (!tofree && FD_ISSET(newfd, fdt->open_fds))
162 goto out_fput; 93 goto out_unlock;
163 94 get_file(file);
164 rcu_assign_pointer(fdt->fd[newfd], file); 95 rcu_assign_pointer(fdt->fd[newfd], file);
165 FD_SET(newfd, fdt->open_fds); 96 FD_SET(newfd, fdt->open_fds);
166 FD_CLR(newfd, fdt->close_on_exec); 97 if (flags & O_CLOEXEC)
98 FD_SET(newfd, fdt->close_on_exec);
99 else
100 FD_CLR(newfd, fdt->close_on_exec);
167 spin_unlock(&files->file_lock); 101 spin_unlock(&files->file_lock);
168 102
169 if (tofree) 103 if (tofree)
170 filp_close(tofree, files); 104 filp_close(tofree, files);
171 err = newfd; 105
172out: 106 return newfd;
173 return err; 107
108Ebadf:
109 err = -EBADF;
174out_unlock: 110out_unlock:
175 spin_unlock(&files->file_lock); 111 spin_unlock(&files->file_lock);
176 goto out; 112 return err;
113}
177 114
178out_fput: 115asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
179 spin_unlock(&files->file_lock); 116{
180 fput(file); 117 if (unlikely(newfd == oldfd)) { /* corner case */
181 goto out; 118 struct files_struct *files = current->files;
119 rcu_read_lock();
120 if (!fcheck_files(files, oldfd))
121 oldfd = -EBADF;
122 rcu_read_unlock();
123 return oldfd;
124 }
125 return sys_dup3(oldfd, newfd, 0);
182} 126}
183 127
184asmlinkage long sys_dup(unsigned int fildes) 128asmlinkage long sys_dup(unsigned int fildes)
185{ 129{
186 int ret = -EBADF; 130 int ret = -EBADF;
187 struct file * file = fget(fildes); 131 struct file *file = fget(fildes);
188 132
189 if (file) 133 if (file) {
190 ret = dupfd(file, 0, 0); 134 ret = get_unused_fd();
135 if (ret >= 0)
136 fd_install(ret, file);
137 else
138 fput(file);
139 }
191 return ret; 140 return ret;
192} 141}
193 142
@@ -310,8 +259,13 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
310 switch (cmd) { 259 switch (cmd) {
311 case F_DUPFD: 260 case F_DUPFD:
312 case F_DUPFD_CLOEXEC: 261 case F_DUPFD_CLOEXEC:
313 get_file(filp); 262 if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
314 err = dupfd(filp, arg, cmd == F_DUPFD_CLOEXEC); 263 break;
264 err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0);
265 if (err >= 0) {
266 get_file(filp);
267 fd_install(err, filp);
268 }
315 break; 269 break;
316 case F_GETFD: 270 case F_GETFD:
317 err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; 271 err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;