diff options
author | Nick Piggin <npiggin@suse.de> | 2008-02-06 04:37:28 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-06 13:41:06 -0500 |
commit | d599e36a9ea85432587f4550acc113cd7549d12a (patch) | |
tree | 2baec196271ccafac682b4bdbef3690cca686221 /fs/inotify.c | |
parent | eea63e0e8a60d00485b47fb6e75d9aa2566b989b (diff) |
inotify: fix race
There is a race between setting an inode's children's "parent watched" flag
when placing the first watch on a parent, and instantiating new children of
that parent: a child could miss having its flags set by
set_dentry_child_flags, but then inotify_d_instantiate might still see
!inotify_inode_watched.
The solution is to set_dentry_child_flags after adding the watch. Locking is
taken care of, because both set_dentry_child_flags and inotify_d_instantiate
hold dcache_lock and child->d_locks.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: Robert Love <rlove@google.com>
Cc: John McCutchan <ttb@tentacle.dhs.org>
Cc: Jan Kara <jack@ucw.cz>
Cc: Yan Zheng <yanzheng@21cn.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/inotify.c')
-rw-r--r-- | fs/inotify.c | 13 |
1 files changed, 10 insertions, 3 deletions
diff --git a/fs/inotify.c b/fs/inotify.c index 2c5b92152876..b2b109bf29d6 100644 --- a/fs/inotify.c +++ b/fs/inotify.c | |||
@@ -627,6 +627,7 @@ s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch, | |||
627 | struct inode *inode, u32 mask) | 627 | struct inode *inode, u32 mask) |
628 | { | 628 | { |
629 | int ret = 0; | 629 | int ret = 0; |
630 | int newly_watched; | ||
630 | 631 | ||
631 | /* don't allow invalid bits: we don't want flags set */ | 632 | /* don't allow invalid bits: we don't want flags set */ |
632 | mask &= IN_ALL_EVENTS | IN_ONESHOT; | 633 | mask &= IN_ALL_EVENTS | IN_ONESHOT; |
@@ -653,12 +654,18 @@ s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch, | |||
653 | */ | 654 | */ |
654 | watch->inode = igrab(inode); | 655 | watch->inode = igrab(inode); |
655 | 656 | ||
656 | if (!inotify_inode_watched(inode)) | ||
657 | set_dentry_child_flags(inode, 1); | ||
658 | |||
659 | /* Add the watch to the handle's and the inode's list */ | 657 | /* Add the watch to the handle's and the inode's list */ |
658 | newly_watched = !inotify_inode_watched(inode); | ||
660 | list_add(&watch->h_list, &ih->watches); | 659 | list_add(&watch->h_list, &ih->watches); |
661 | list_add(&watch->i_list, &inode->inotify_watches); | 660 | list_add(&watch->i_list, &inode->inotify_watches); |
661 | /* | ||
662 | * Set child flags _after_ adding the watch, so there is no race | ||
663 | * windows where newly instantiated children could miss their parent's | ||
664 | * watched flag. | ||
665 | */ | ||
666 | if (newly_watched) | ||
667 | set_dentry_child_flags(inode, 1); | ||
668 | |||
662 | out: | 669 | out: |
663 | mutex_unlock(&ih->mutex); | 670 | mutex_unlock(&ih->mutex); |
664 | mutex_unlock(&inode->inotify_mutex); | 671 | mutex_unlock(&inode->inotify_mutex); |