diff options
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r-- | fs/fcntl.c | 140 |
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 | |||
58 | static 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 | repeat: | ||
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 | |||
103 | out: | ||
104 | spin_unlock(&files->file_lock); | ||
105 | return error; | ||
106 | } | ||
107 | |||
108 | static 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 | |||
119 | asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) | 52 | asmlinkage 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; | ||
169 | out: | ||
170 | return err; | ||
171 | out_unlock: | ||
172 | spin_unlock(&files->file_lock); | ||
173 | goto out; | ||
174 | 105 | ||
175 | out_fput: | 106 | return newfd; |
107 | |||
108 | Ebadf: | ||
109 | err = -EBADF; | ||
110 | out_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 | ||
181 | asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) | 115 | asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) |
@@ -194,10 +128,15 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) | |||
194 | asmlinkage long sys_dup(unsigned int fildes) | 128 | asmlinkage 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; |