diff options
author | Jan Kara <jack@suse.cz> | 2012-06-12 10:20:35 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-07-31 01:40:38 -0400 |
commit | eb04c28288bb0098d0e75d81ba2a575239de71d8 (patch) | |
tree | 82912fd7c78be269560a8af7d8516918c279255b /fs | |
parent | 5accdf82ba25cacefd6c1867f1704beb4d244cdd (diff) |
fs: Add freezing handling to mnt_want_write() / mnt_drop_write()
Most of places where we want freeze protection coincides with the places where
we also have remount-ro protection. So make mnt_want_write() and
mnt_drop_write() (and their _file alternative) prevent freezing as well.
For the few cases that are really interested only in remount-ro protection
provide new function variants.
BugLink: https://bugs.launchpad.net/bugs/897421
Tested-by: Kamal Mostafa <kamal@canonical.com>
Tested-by: Peter M. Petrakis <peter.petrakis@canonical.com>
Tested-by: Dann Frazier <dann.frazier@canonical.com>
Tested-by: Massimo Morana <massimo.morana@canonical.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/file_table.c | 2 | ||||
-rw-r--r-- | fs/inode.c | 4 | ||||
-rw-r--r-- | fs/internal.h | 4 | ||||
-rw-r--r-- | fs/namespace.c | 97 | ||||
-rw-r--r-- | fs/open.c | 2 |
5 files changed, 85 insertions, 24 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index b54bf7fd0b15..701985e4ccda 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
@@ -217,7 +217,7 @@ static void drop_file_write_access(struct file *file) | |||
217 | return; | 217 | return; |
218 | if (file_check_writeable(file) != 0) | 218 | if (file_check_writeable(file) != 0) |
219 | return; | 219 | return; |
220 | mnt_drop_write(mnt); | 220 | __mnt_drop_write(mnt); |
221 | file_release_write(file); | 221 | file_release_write(file); |
222 | } | 222 | } |
223 | 223 | ||
diff --git a/fs/inode.c b/fs/inode.c index 775cbabd4fa5..006c85ca06eb 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -1660,11 +1660,11 @@ int file_update_time(struct file *file) | |||
1660 | return 0; | 1660 | return 0; |
1661 | 1661 | ||
1662 | /* Finally allowed to write? Takes lock. */ | 1662 | /* Finally allowed to write? Takes lock. */ |
1663 | if (mnt_want_write_file(file)) | 1663 | if (__mnt_want_write_file(file)) |
1664 | return 0; | 1664 | return 0; |
1665 | 1665 | ||
1666 | ret = update_time(inode, &now, sync_it); | 1666 | ret = update_time(inode, &now, sync_it); |
1667 | mnt_drop_write_file(file); | 1667 | __mnt_drop_write_file(file); |
1668 | 1668 | ||
1669 | return ret; | 1669 | return ret; |
1670 | } | 1670 | } |
diff --git a/fs/internal.h b/fs/internal.h index a6fd56c68b11..371bcc4b1697 100644 --- a/fs/internal.h +++ b/fs/internal.h | |||
@@ -61,6 +61,10 @@ extern void __init mnt_init(void); | |||
61 | 61 | ||
62 | extern struct lglock vfsmount_lock; | 62 | extern struct lglock vfsmount_lock; |
63 | 63 | ||
64 | extern int __mnt_want_write(struct vfsmount *); | ||
65 | extern int __mnt_want_write_file(struct file *); | ||
66 | extern void __mnt_drop_write(struct vfsmount *); | ||
67 | extern void __mnt_drop_write_file(struct file *); | ||
64 | 68 | ||
65 | /* | 69 | /* |
66 | * fs_struct.c | 70 | * fs_struct.c |
diff --git a/fs/namespace.c b/fs/namespace.c index c53d3381b0d0..4d31f73e2561 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -283,24 +283,22 @@ static int mnt_is_readonly(struct vfsmount *mnt) | |||
283 | } | 283 | } |
284 | 284 | ||
285 | /* | 285 | /* |
286 | * Most r/o checks on a fs are for operations that take | 286 | * Most r/o & frozen checks on a fs are for operations that take discrete |
287 | * discrete amounts of time, like a write() or unlink(). | 287 | * amounts of time, like a write() or unlink(). We must keep track of when |
288 | * We must keep track of when those operations start | 288 | * those operations start (for permission checks) and when they end, so that we |
289 | * (for permission checks) and when they end, so that | 289 | * can determine when writes are able to occur to a filesystem. |
290 | * we can determine when writes are able to occur to | ||
291 | * a filesystem. | ||
292 | */ | 290 | */ |
293 | /** | 291 | /** |
294 | * mnt_want_write - get write access to a mount | 292 | * __mnt_want_write - get write access to a mount without freeze protection |
295 | * @m: the mount on which to take a write | 293 | * @m: the mount on which to take a write |
296 | * | 294 | * |
297 | * This tells the low-level filesystem that a write is | 295 | * This tells the low-level filesystem that a write is about to be performed to |
298 | * about to be performed to it, and makes sure that | 296 | * it, and makes sure that writes are allowed (mnt it read-write) before |
299 | * writes are allowed before returning success. When | 297 | * returning success. This operation does not protect against filesystem being |
300 | * the write operation is finished, mnt_drop_write() | 298 | * frozen. When the write operation is finished, __mnt_drop_write() must be |
301 | * must be called. This is effectively a refcount. | 299 | * called. This is effectively a refcount. |
302 | */ | 300 | */ |
303 | int mnt_want_write(struct vfsmount *m) | 301 | int __mnt_want_write(struct vfsmount *m) |
304 | { | 302 | { |
305 | struct mount *mnt = real_mount(m); | 303 | struct mount *mnt = real_mount(m); |
306 | int ret = 0; | 304 | int ret = 0; |
@@ -326,6 +324,27 @@ int mnt_want_write(struct vfsmount *m) | |||
326 | ret = -EROFS; | 324 | ret = -EROFS; |
327 | } | 325 | } |
328 | preempt_enable(); | 326 | preempt_enable(); |
327 | |||
328 | return ret; | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * mnt_want_write - get write access to a mount | ||
333 | * @m: the mount on which to take a write | ||
334 | * | ||
335 | * This tells the low-level filesystem that a write is about to be performed to | ||
336 | * it, and makes sure that writes are allowed (mount is read-write, filesystem | ||
337 | * is not frozen) before returning success. When the write operation is | ||
338 | * finished, mnt_drop_write() must be called. This is effectively a refcount. | ||
339 | */ | ||
340 | int mnt_want_write(struct vfsmount *m) | ||
341 | { | ||
342 | int ret; | ||
343 | |||
344 | sb_start_write(m->mnt_sb); | ||
345 | ret = __mnt_want_write(m); | ||
346 | if (ret) | ||
347 | sb_end_write(m->mnt_sb); | ||
329 | return ret; | 348 | return ret; |
330 | } | 349 | } |
331 | EXPORT_SYMBOL_GPL(mnt_want_write); | 350 | EXPORT_SYMBOL_GPL(mnt_want_write); |
@@ -355,38 +374,76 @@ int mnt_clone_write(struct vfsmount *mnt) | |||
355 | EXPORT_SYMBOL_GPL(mnt_clone_write); | 374 | EXPORT_SYMBOL_GPL(mnt_clone_write); |
356 | 375 | ||
357 | /** | 376 | /** |
358 | * mnt_want_write_file - get write access to a file's mount | 377 | * __mnt_want_write_file - get write access to a file's mount |
359 | * @file: the file who's mount on which to take a write | 378 | * @file: the file who's mount on which to take a write |
360 | * | 379 | * |
361 | * This is like mnt_want_write, but it takes a file and can | 380 | * This is like __mnt_want_write, but it takes a file and can |
362 | * do some optimisations if the file is open for write already | 381 | * do some optimisations if the file is open for write already |
363 | */ | 382 | */ |
364 | int mnt_want_write_file(struct file *file) | 383 | int __mnt_want_write_file(struct file *file) |
365 | { | 384 | { |
366 | struct inode *inode = file->f_dentry->d_inode; | 385 | struct inode *inode = file->f_dentry->d_inode; |
386 | |||
367 | if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode)) | 387 | if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode)) |
368 | return mnt_want_write(file->f_path.mnt); | 388 | return __mnt_want_write(file->f_path.mnt); |
369 | else | 389 | else |
370 | return mnt_clone_write(file->f_path.mnt); | 390 | return mnt_clone_write(file->f_path.mnt); |
371 | } | 391 | } |
392 | |||
393 | /** | ||
394 | * mnt_want_write_file - get write access to a file's mount | ||
395 | * @file: the file who's mount on which to take a write | ||
396 | * | ||
397 | * This is like mnt_want_write, but it takes a file and can | ||
398 | * do some optimisations if the file is open for write already | ||
399 | */ | ||
400 | int mnt_want_write_file(struct file *file) | ||
401 | { | ||
402 | int ret; | ||
403 | |||
404 | sb_start_write(file->f_path.mnt->mnt_sb); | ||
405 | ret = __mnt_want_write_file(file); | ||
406 | if (ret) | ||
407 | sb_end_write(file->f_path.mnt->mnt_sb); | ||
408 | return ret; | ||
409 | } | ||
372 | EXPORT_SYMBOL_GPL(mnt_want_write_file); | 410 | EXPORT_SYMBOL_GPL(mnt_want_write_file); |
373 | 411 | ||
374 | /** | 412 | /** |
375 | * mnt_drop_write - give up write access to a mount | 413 | * __mnt_drop_write - give up write access to a mount |
376 | * @mnt: the mount on which to give up write access | 414 | * @mnt: the mount on which to give up write access |
377 | * | 415 | * |
378 | * Tells the low-level filesystem that we are done | 416 | * Tells the low-level filesystem that we are done |
379 | * performing writes to it. Must be matched with | 417 | * performing writes to it. Must be matched with |
380 | * mnt_want_write() call above. | 418 | * __mnt_want_write() call above. |
381 | */ | 419 | */ |
382 | void mnt_drop_write(struct vfsmount *mnt) | 420 | void __mnt_drop_write(struct vfsmount *mnt) |
383 | { | 421 | { |
384 | preempt_disable(); | 422 | preempt_disable(); |
385 | mnt_dec_writers(real_mount(mnt)); | 423 | mnt_dec_writers(real_mount(mnt)); |
386 | preempt_enable(); | 424 | preempt_enable(); |
387 | } | 425 | } |
426 | |||
427 | /** | ||
428 | * mnt_drop_write - give up write access to a mount | ||
429 | * @mnt: the mount on which to give up write access | ||
430 | * | ||
431 | * Tells the low-level filesystem that we are done performing writes to it and | ||
432 | * also allows filesystem to be frozen again. Must be matched with | ||
433 | * mnt_want_write() call above. | ||
434 | */ | ||
435 | void mnt_drop_write(struct vfsmount *mnt) | ||
436 | { | ||
437 | __mnt_drop_write(mnt); | ||
438 | sb_end_write(mnt->mnt_sb); | ||
439 | } | ||
388 | EXPORT_SYMBOL_GPL(mnt_drop_write); | 440 | EXPORT_SYMBOL_GPL(mnt_drop_write); |
389 | 441 | ||
442 | void __mnt_drop_write_file(struct file *file) | ||
443 | { | ||
444 | __mnt_drop_write(file->f_path.mnt); | ||
445 | } | ||
446 | |||
390 | void mnt_drop_write_file(struct file *file) | 447 | void mnt_drop_write_file(struct file *file) |
391 | { | 448 | { |
392 | mnt_drop_write(file->f_path.mnt); | 449 | mnt_drop_write(file->f_path.mnt); |
@@ -620,7 +620,7 @@ static inline int __get_file_write_access(struct inode *inode, | |||
620 | /* | 620 | /* |
621 | * Balanced in __fput() | 621 | * Balanced in __fput() |
622 | */ | 622 | */ |
623 | error = mnt_want_write(mnt); | 623 | error = __mnt_want_write(mnt); |
624 | if (error) | 624 | if (error) |
625 | put_write_access(inode); | 625 | put_write_access(inode); |
626 | } | 626 | } |