aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fcntl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r--fs/fcntl.c60
1 files changed, 39 insertions, 21 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 6fbc9d8fcc36..863b46e0d78a 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -16,6 +16,7 @@
16#include <linux/security.h> 16#include <linux/security.h>
17#include <linux/ptrace.h> 17#include <linux/ptrace.h>
18#include <linux/signal.h> 18#include <linux/signal.h>
19#include <linux/rcupdate.h>
19 20
20#include <asm/poll.h> 21#include <asm/poll.h>
21#include <asm/siginfo.h> 22#include <asm/siginfo.h>
@@ -24,21 +25,25 @@
24void fastcall set_close_on_exec(unsigned int fd, int flag) 25void fastcall set_close_on_exec(unsigned int fd, int flag)
25{ 26{
26 struct files_struct *files = current->files; 27 struct files_struct *files = current->files;
28 struct fdtable *fdt;
27 spin_lock(&files->file_lock); 29 spin_lock(&files->file_lock);
30 fdt = files_fdtable(files);
28 if (flag) 31 if (flag)
29 FD_SET(fd, files->close_on_exec); 32 FD_SET(fd, fdt->close_on_exec);
30 else 33 else
31 FD_CLR(fd, files->close_on_exec); 34 FD_CLR(fd, fdt->close_on_exec);
32 spin_unlock(&files->file_lock); 35 spin_unlock(&files->file_lock);
33} 36}
34 37
35static inline int get_close_on_exec(unsigned int fd) 38static inline int get_close_on_exec(unsigned int fd)
36{ 39{
37 struct files_struct *files = current->files; 40 struct files_struct *files = current->files;
41 struct fdtable *fdt;
38 int res; 42 int res;
39 spin_lock(&files->file_lock); 43 rcu_read_lock();
40 res = FD_ISSET(fd, files->close_on_exec); 44 fdt = files_fdtable(files);
41 spin_unlock(&files->file_lock); 45 res = FD_ISSET(fd, fdt->close_on_exec);
46 rcu_read_unlock();
42 return res; 47 return res;
43} 48}
44 49
@@ -54,24 +59,26 @@ static int locate_fd(struct files_struct *files,
54 unsigned int newfd; 59 unsigned int newfd;
55 unsigned int start; 60 unsigned int start;
56 int error; 61 int error;
62 struct fdtable *fdt;
57 63
58 error = -EINVAL; 64 error = -EINVAL;
59 if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) 65 if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
60 goto out; 66 goto out;
61 67
62repeat: 68repeat:
69 fdt = files_fdtable(files);
63 /* 70 /*
64 * Someone might have closed fd's in the range 71 * Someone might have closed fd's in the range
65 * orig_start..files->next_fd 72 * orig_start..fdt->next_fd
66 */ 73 */
67 start = orig_start; 74 start = orig_start;
68 if (start < files->next_fd) 75 if (start < fdt->next_fd)
69 start = files->next_fd; 76 start = fdt->next_fd;
70 77
71 newfd = start; 78 newfd = start;
72 if (start < files->max_fdset) { 79 if (start < fdt->max_fdset) {
73 newfd = find_next_zero_bit(files->open_fds->fds_bits, 80 newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
74 files->max_fdset, start); 81 fdt->max_fdset, start);
75 } 82 }
76 83
77 error = -EMFILE; 84 error = -EMFILE;
@@ -89,9 +96,15 @@ repeat:
89 if (error) 96 if (error)
90 goto repeat; 97 goto repeat;
91 98
92 if (start <= files->next_fd) 99 /*
93 files->next_fd = newfd + 1; 100 * We reacquired files_lock, so we are safe as long as
94 101 * we reacquire the fdtable pointer and use it while holding
102 * the lock, no one can free it during that time.
103 */
104 fdt = files_fdtable(files);
105 if (start <= fdt->next_fd)
106 fdt->next_fd = newfd + 1;
107
95 error = newfd; 108 error = newfd;
96 109
97out: 110out:
@@ -101,13 +114,16 @@ out:
101static int dupfd(struct file *file, unsigned int start) 114static int dupfd(struct file *file, unsigned int start)
102{ 115{
103 struct files_struct * files = current->files; 116 struct files_struct * files = current->files;
117 struct fdtable *fdt;
104 int fd; 118 int fd;
105 119
106 spin_lock(&files->file_lock); 120 spin_lock(&files->file_lock);
107 fd = locate_fd(files, file, start); 121 fd = locate_fd(files, file, start);
108 if (fd >= 0) { 122 if (fd >= 0) {
109 FD_SET(fd, files->open_fds); 123 /* locate_fd() may have expanded fdtable, load the ptr */
110 FD_CLR(fd, files->close_on_exec); 124 fdt = files_fdtable(files);
125 FD_SET(fd, fdt->open_fds);
126 FD_CLR(fd, fdt->close_on_exec);
111 spin_unlock(&files->file_lock); 127 spin_unlock(&files->file_lock);
112 fd_install(fd, file); 128 fd_install(fd, file);
113 } else { 129 } else {
@@ -123,6 +139,7 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
123 int err = -EBADF; 139 int err = -EBADF;
124 struct file * file, *tofree; 140 struct file * file, *tofree;
125 struct files_struct * files = current->files; 141 struct files_struct * files = current->files;
142 struct fdtable *fdt;
126 143
127 spin_lock(&files->file_lock); 144 spin_lock(&files->file_lock);
128 if (!(file = fcheck(oldfd))) 145 if (!(file = fcheck(oldfd)))
@@ -148,13 +165,14 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
148 165
149 /* Yes. It's a race. In user space. Nothing sane to do */ 166 /* Yes. It's a race. In user space. Nothing sane to do */
150 err = -EBUSY; 167 err = -EBUSY;
151 tofree = files->fd[newfd]; 168 fdt = files_fdtable(files);
152 if (!tofree && FD_ISSET(newfd, files->open_fds)) 169 tofree = fdt->fd[newfd];
170 if (!tofree && FD_ISSET(newfd, fdt->open_fds))
153 goto out_fput; 171 goto out_fput;
154 172
155 files->fd[newfd] = file; 173 rcu_assign_pointer(fdt->fd[newfd], file);
156 FD_SET(newfd, files->open_fds); 174 FD_SET(newfd, fdt->open_fds);
157 FD_CLR(newfd, files->close_on_exec); 175 FD_CLR(newfd, fdt->close_on_exec);
158 spin_unlock(&files->file_lock); 176 spin_unlock(&files->file_lock);
159 177
160 if (tofree) 178 if (tofree)