diff options
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r-- | fs/fcntl.c | 60 |
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 @@ | |||
24 | void fastcall set_close_on_exec(unsigned int fd, int flag) | 25 | void 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 | ||
35 | static inline int get_close_on_exec(unsigned int fd) | 38 | static 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 | ||
62 | repeat: | 68 | repeat: |
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 | ||
97 | out: | 110 | out: |
@@ -101,13 +114,16 @@ out: | |||
101 | static int dupfd(struct file *file, unsigned int start) | 114 | static 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) |