X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Finotify.c;h=690e72595e6e6048addba645cf179cb418208f50;hb=537388bb6563bfde7c0307b95ecc1f7a1ae39b02;hp=8477c4fbecb45ff7a181c05ac835e8238c086dcd;hpb=a9dc971d3fdb857a2bcd6d53238125a2cd31d5f4;p=linux-2.6 diff --git a/fs/inotify.c b/fs/inotify.c index 8477c4fbec..690e72595e 100644 --- a/fs/inotify.c +++ b/fs/inotify.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -167,20 +168,14 @@ static void set_dentry_child_flags(struct inode *inode, int watched) struct dentry *child; list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { - if (!child->d_inode) { - WARN_ON(child->d_flags & DCACHE_INOTIFY_PARENT_WATCHED); + if (!child->d_inode) continue; - } + spin_lock(&child->d_lock); - if (watched) { - WARN_ON(child->d_flags & - DCACHE_INOTIFY_PARENT_WATCHED); + if (watched) child->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; - } else { - WARN_ON(!(child->d_flags & - DCACHE_INOTIFY_PARENT_WATCHED)); - child->d_flags&=~DCACHE_INOTIFY_PARENT_WATCHED; - } + else + child->d_flags &=~DCACHE_INOTIFY_PARENT_WATCHED; spin_unlock(&child->d_lock); } } @@ -207,7 +202,7 @@ static struct inotify_watch *inode_find_handle(struct inode *inode, } /* - * remove_watch_no_event - remove_watch() without the IN_IGNORED event. + * remove_watch_no_event - remove watch without the IN_IGNORED event. * * Callers must hold both inode->inotify_mutex and ih->mutex. */ @@ -223,17 +218,22 @@ static void remove_watch_no_event(struct inotify_watch *watch, idr_remove(&ih->idr, watch->wd); } -/* - * remove_watch - Remove a watch from both the handle and the inode. Sends - * the IN_IGNORED event signifying that the inode is no longer watched. +/** + * inotify_remove_watch_locked - Remove a watch from both the handle and the + * inode. Sends the IN_IGNORED event signifying that the inode is no longer + * watched. May be invoked from a caller's event handler. + * @ih: inotify handle associated with watch + * @watch: watch to remove * * Callers must hold both inode->inotify_mutex and ih->mutex. */ -static void remove_watch(struct inotify_watch *watch, struct inotify_handle *ih) +void inotify_remove_watch_locked(struct inotify_handle *ih, + struct inotify_watch *watch) { remove_watch_no_event(watch, ih); ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL); } +EXPORT_SYMBOL_GPL(inotify_remove_watch_locked); /* Kernel API for producing events */ @@ -247,7 +247,6 @@ void inotify_d_instantiate(struct dentry *entry, struct inode *inode) if (!inode) return; - WARN_ON(entry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED); spin_lock(&entry->d_lock); parent = entry->d_parent; if (parent->d_inode && inotify_inode_watched(parent->d_inode)) @@ -378,7 +377,7 @@ void inotify_unmount_inodes(struct list_head *list) need_iput_tmp = need_iput; need_iput = NULL; - /* In case the remove_watch() drops a reference. */ + /* In case inotify_remove_watch_locked() drops a reference. */ if (inode != need_iput_tmp) __iget(inode); else @@ -411,7 +410,7 @@ void inotify_unmount_inodes(struct list_head *list) mutex_lock(&ih->mutex); ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0, NULL, NULL); - remove_watch(watch, ih); + inotify_remove_watch_locked(ih, watch); mutex_unlock(&ih->mutex); } mutex_unlock(&inode->inotify_mutex); @@ -434,7 +433,7 @@ void inotify_inode_is_dead(struct inode *inode) list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) { struct inotify_handle *ih = watch->ih; mutex_lock(&ih->mutex); - remove_watch(watch, ih); + inotify_remove_watch_locked(ih, watch); mutex_unlock(&ih->mutex); } mutex_unlock(&inode->inotify_mutex); @@ -503,7 +502,7 @@ void inotify_destroy(struct inotify_handle *ih) mutex_unlock(&ih->mutex); break; } - watch = list_entry(watches->next, struct inotify_watch, h_list); + watch = list_first_entry(watches, struct inotify_watch, h_list); get_inotify_watch(watch); mutex_unlock(&ih->mutex); @@ -621,6 +620,7 @@ s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch, struct inode *inode, u32 mask) { int ret = 0; + int newly_watched; /* don't allow invalid bits: we don't want flags set */ mask &= IN_ALL_EVENTS | IN_ONESHOT; @@ -647,12 +647,18 @@ s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch, */ watch->inode = igrab(inode); - if (!inotify_inode_watched(inode)) - set_dentry_child_flags(inode, 1); - /* Add the watch to the handle's and the inode's list */ + newly_watched = !inotify_inode_watched(inode); list_add(&watch->h_list, &ih->watches); list_add(&watch->i_list, &inode->inotify_watches); + /* + * Set child flags _after_ adding the watch, so there is no race + * windows where newly instantiated children could miss their parent's + * watched flag. + */ + if (newly_watched) + set_dentry_child_flags(inode, 1); + out: mutex_unlock(&ih->mutex); mutex_unlock(&inode->inotify_mutex); @@ -660,6 +666,49 @@ out: } EXPORT_SYMBOL_GPL(inotify_add_watch); +/** + * inotify_clone_watch - put the watch next to existing one + * @old: already installed watch + * @new: new watch + * + * Caller must hold the inotify_mutex of inode we are dealing with; + * it is expected to remove the old watch before unlocking the inode. + */ +s32 inotify_clone_watch(struct inotify_watch *old, struct inotify_watch *new) +{ + struct inotify_handle *ih = old->ih; + int ret = 0; + + new->mask = old->mask; + new->ih = ih; + + mutex_lock(&ih->mutex); + + /* Initialize a new watch */ + ret = inotify_handle_get_wd(ih, new); + if (unlikely(ret)) + goto out; + ret = new->wd; + + get_inotify_handle(ih); + + new->inode = igrab(old->inode); + + list_add(&new->h_list, &ih->watches); + list_add(&new->i_list, &old->inode->inotify_watches); +out: + mutex_unlock(&ih->mutex); + return ret; +} + +void inotify_evict_watch(struct inotify_watch *watch) +{ + get_inotify_watch(watch); + mutex_lock(&watch->ih->mutex); + inotify_remove_watch_locked(watch->ih, watch); + mutex_unlock(&watch->ih->mutex); +} + /** * inotify_rm_wd - remove a watch from an inotify instance * @ih: inotify handle @@ -687,7 +736,7 @@ int inotify_rm_wd(struct inotify_handle *ih, u32 wd) /* make sure that we did not race */ if (likely(idr_find(&ih->idr, wd) == watch)) - remove_watch(watch, ih); + inotify_remove_watch_locked(ih, watch); mutex_unlock(&ih->mutex); mutex_unlock(&inode->inotify_mutex);