aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fcntl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r--fs/fcntl.c140
1 files changed, 41 insertions, 99 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 61d625136813..ac4f7db9f134 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -49,73 +49,6 @@ static int get_close_on_exec(unsigned int fd)
49 return res; 49 return res;
50} 50}
51 51
52/*
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);
67repeat:
68 fdt = files_fdtable(files);
69 /*
70 * Someone might have closed fd's in the range
71 * orig_start..fdt->next_fd
72 */
73 start = orig_start;
74 if (start < files->next_fd)
75 start = files->next_fd;
76
77 newfd = start;
78 if (start < fdt->max_fds)
79 newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
80 fdt->max_fds, start);
81
82 error = expand_files(files, newfd);
83 if (error < 0)
84 goto out;
85
86 /*
87 * If we needed to expand the fs array we
88 * might have blocked - try again.
89 */
90 if (error)
91 goto repeat;
92
93 if (start <= files->next_fd)
94 files->next_fd = newfd + 1;
95
96 FD_SET(newfd, fdt->open_fds);
97 if (cloexec)
98 FD_SET(newfd, fdt->close_on_exec);
99 else
100 FD_CLR(newfd, fdt->close_on_exec);
101 error = newfd;
102
103out:
104 spin_unlock(&files->file_lock);
105 return error;
106}
107
108static int dupfd(struct file *file, unsigned int start, int cloexec)
109{
110 int fd = locate_fd(start, cloexec);
111 if (fd >= 0)
112 fd_install(fd, file);
113 else
114 fput(file);
115
116 return fd;
117}
118
119asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) 52asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
120{ 53{
121 int err = -EBADF; 54 int err = -EBADF;
@@ -130,31 +63,35 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
130 return -EINVAL; 63 return -EINVAL;
131 64
132 spin_lock(&files->file_lock); 65 spin_lock(&files->file_lock);
133 if (!(file = fcheck(oldfd)))
134 goto out_unlock;
135 get_file(file); /* We are now finished with oldfd */
136
137 err = expand_files(files, newfd); 66 err = expand_files(files, newfd);
67 file = fcheck(oldfd);
68 if (unlikely(!file))
69 goto Ebadf;
138 if (unlikely(err < 0)) { 70 if (unlikely(err < 0)) {
139 if (err == -EMFILE) 71 if (err == -EMFILE)
140 err = -EBADF; 72 goto Ebadf;
141 goto out_fput; 73 goto out_unlock;
142 } 74 }
143 75 /*
144 /* 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
145 * in-use in the open-file bitmap throughout the entire dup2() 77 * not finished descriptor. NB: OpenBSD avoids that at the price of
146 * process. This is quite safe: do_close() uses the fd array 78 * extra work in their equivalent of fget() - they insert struct
147 * entry, not the bitmap, to decide what work needs to be 79 * file immediately after grabbing descriptor, mark it larval if
148 * done. --sct */ 80 * more work (e.g. actual opening) is needed and make sure that
149 /* Doesn't work. open() might be there first. --AV */ 81 * fget() treats larval files as absent. Potentially interesting,
150 82 * but while extra work in fget() is trivial, locking implications
151 /* 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 */
152 err = -EBUSY; 89 err = -EBUSY;
153 fdt = files_fdtable(files); 90 fdt = files_fdtable(files);
154 tofree = fdt->fd[newfd]; 91 tofree = fdt->fd[newfd];
155 if (!tofree && FD_ISSET(newfd, fdt->open_fds)) 92 if (!tofree && FD_ISSET(newfd, fdt->open_fds))
156 goto out_fput; 93 goto out_unlock;
157 94 get_file(file);
158 rcu_assign_pointer(fdt->fd[newfd], file); 95 rcu_assign_pointer(fdt->fd[newfd], file);
159 FD_SET(newfd, fdt->open_fds); 96 FD_SET(newfd, fdt->open_fds);
160 if (flags & O_CLOEXEC) 97 if (flags & O_CLOEXEC)
@@ -165,17 +102,14 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
165 102
166 if (tofree) 103 if (tofree)
167 filp_close(tofree, files); 104 filp_close(tofree, files);
168 err = newfd;
169out:
170 return err;
171out_unlock:
172 spin_unlock(&files->file_lock);
173 goto out;
174 105
175out_fput: 106 return newfd;
107
108Ebadf:
109 err = -EBADF;
110out_unlock:
176 spin_unlock(&files->file_lock); 111 spin_unlock(&files->file_lock);
177 fput(file); 112 return err;
178 goto out;
179} 113}
180 114
181asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) 115asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
@@ -194,10 +128,15 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
194asmlinkage long sys_dup(unsigned int fildes) 128asmlinkage long sys_dup(unsigned int fildes)
195{ 129{
196 int ret = -EBADF; 130 int ret = -EBADF;
197 struct file * file = fget(fildes); 131 struct file *file = fget(fildes);
198 132
199 if (file) 133 if (file) {
200 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 }
201 return ret; 140 return ret;
202} 141}
203 142
@@ -322,8 +261,11 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
322 case F_DUPFD_CLOEXEC: 261 case F_DUPFD_CLOEXEC:
323 if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) 262 if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
324 break; 263 break;
325 get_file(filp); 264 err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0);
326 err = dupfd(filp, arg, cmd == F_DUPFD_CLOEXEC); 265 if (err >= 0) {
266 get_file(filp);
267 fd_install(err, filp);
268 }
327 break; 269 break;
328 case F_GETFD: 270 case F_GETFD:
329 err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; 271 err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;