diff options
Diffstat (limited to 'fs/file_table.c')
-rw-r--r-- | fs/file_table.c | 42 |
1 files changed, 40 insertions, 2 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index 986ff4ed0a7c..7a0a9b872251 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
@@ -42,6 +42,7 @@ static inline void file_free_rcu(struct rcu_head *head) | |||
42 | static inline void file_free(struct file *f) | 42 | static inline void file_free(struct file *f) |
43 | { | 43 | { |
44 | percpu_counter_dec(&nr_files); | 44 | percpu_counter_dec(&nr_files); |
45 | file_check_state(f); | ||
45 | call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); | 46 | call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); |
46 | } | 47 | } |
47 | 48 | ||
@@ -199,6 +200,18 @@ int init_file(struct file *file, struct vfsmount *mnt, struct dentry *dentry, | |||
199 | file->f_mapping = dentry->d_inode->i_mapping; | 200 | file->f_mapping = dentry->d_inode->i_mapping; |
200 | file->f_mode = mode; | 201 | file->f_mode = mode; |
201 | file->f_op = fop; | 202 | file->f_op = fop; |
203 | |||
204 | /* | ||
205 | * These mounts don't really matter in practice | ||
206 | * for r/o bind mounts. They aren't userspace- | ||
207 | * visible. We do this for consistency, and so | ||
208 | * that we can do debugging checks at __fput() | ||
209 | */ | ||
210 | if ((mode & FMODE_WRITE) && !special_file(dentry->d_inode->i_mode)) { | ||
211 | file_take_write(file); | ||
212 | error = mnt_want_write(mnt); | ||
213 | WARN_ON(error); | ||
214 | } | ||
202 | return error; | 215 | return error; |
203 | } | 216 | } |
204 | EXPORT_SYMBOL(init_file); | 217 | EXPORT_SYMBOL(init_file); |
@@ -211,6 +224,31 @@ void fput(struct file *file) | |||
211 | 224 | ||
212 | EXPORT_SYMBOL(fput); | 225 | EXPORT_SYMBOL(fput); |
213 | 226 | ||
227 | /** | ||
228 | * drop_file_write_access - give up ability to write to a file | ||
229 | * @file: the file to which we will stop writing | ||
230 | * | ||
231 | * This is a central place which will give up the ability | ||
232 | * to write to @file, along with access to write through | ||
233 | * its vfsmount. | ||
234 | */ | ||
235 | void drop_file_write_access(struct file *file) | ||
236 | { | ||
237 | struct vfsmount *mnt = file->f_path.mnt; | ||
238 | struct dentry *dentry = file->f_path.dentry; | ||
239 | struct inode *inode = dentry->d_inode; | ||
240 | |||
241 | put_write_access(inode); | ||
242 | |||
243 | if (special_file(inode->i_mode)) | ||
244 | return; | ||
245 | if (file_check_writeable(file) != 0) | ||
246 | return; | ||
247 | mnt_drop_write(mnt); | ||
248 | file_release_write(file); | ||
249 | } | ||
250 | EXPORT_SYMBOL_GPL(drop_file_write_access); | ||
251 | |||
214 | /* __fput is called from task context when aio completion releases the last | 252 | /* __fput is called from task context when aio completion releases the last |
215 | * last use of a struct file *. Do not use otherwise. | 253 | * last use of a struct file *. Do not use otherwise. |
216 | */ | 254 | */ |
@@ -236,10 +274,10 @@ void __fput(struct file *file) | |||
236 | if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL)) | 274 | if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL)) |
237 | cdev_put(inode->i_cdev); | 275 | cdev_put(inode->i_cdev); |
238 | fops_put(file->f_op); | 276 | fops_put(file->f_op); |
239 | if (file->f_mode & FMODE_WRITE) | ||
240 | put_write_access(inode); | ||
241 | put_pid(file->f_owner.pid); | 277 | put_pid(file->f_owner.pid); |
242 | file_kill(file); | 278 | file_kill(file); |
279 | if (file->f_mode & FMODE_WRITE) | ||
280 | drop_file_write_access(file); | ||
243 | file->f_path.dentry = NULL; | 281 | file->f_path.dentry = NULL; |
244 | file->f_path.mnt = NULL; | 282 | file->f_path.mnt = NULL; |
245 | file_free(file); | 283 | file_free(file); |