diff options
-rw-r--r-- | fs/kernfs/file.c | 10 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 8 | ||||
-rw-r--r-- | fs/notify/fsnotify.c | 60 |
3 files changed, 45 insertions, 33 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index e23b3f62483c..5b1468bc509e 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -883,6 +883,7 @@ repeat: list_for_each_entry(info, &kernfs_root(kn)->supers, node) { struct kernfs_node *parent; + struct inode *p_inode = NULL; struct inode *inode; struct qstr name; @@ -899,8 +900,6 @@ repeat: name = (struct qstr)QSTR_INIT(kn->name, strlen(kn->name)); parent = kernfs_get_parent(kn); if (parent) { - struct inode *p_inode; - p_inode = ilookup(info->sb, kernfs_ino(parent)); if (p_inode) { fsnotify(p_inode, FS_MODIFY | FS_EVENT_ON_CHILD, @@ -911,8 +910,11 @@ repeat: kernfs_put(parent); } - fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE, - NULL, 0); + if (!p_inode) { + fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE, + NULL, 0); + } + iput(inode); } diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index c107974d6830..3baf93e998c1 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -261,12 +261,12 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group, continue; /* - * If the event is for a child and this mark doesn't care about - * events on a child, don't send it! + * If the event is for a child and this mark is on a parent not + * watching children, don't send it! */ if (event_mask & FS_EVENT_ON_CHILD && - (type != FSNOTIFY_OBJ_TYPE_INODE || - !(mark->mask & FS_EVENT_ON_CHILD))) + type == FSNOTIFY_OBJ_TYPE_INODE && + !(mark->mask & FS_EVENT_ON_CHILD)) continue; marks_mask |= mark->mask; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index f12a554be3f0..0b0e01e04349 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -145,17 +145,21 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) /* * Notify this dentry's parent about a child's events with child name info * if parent is watching. - * Notify also the child without name info if child inode is watching. + * Notify only the child without name info if parent is not watching. */ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, int data_type) { + struct inode *inode = d_inode(dentry); struct dentry *parent; struct inode *p_inode; + struct name_snapshot name; + struct qstr *file_name = NULL; int ret = 0; + parent = NULL; if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) - goto notify_child; + goto notify; parent = dget_parent(dentry); p_inode = parent->d_inode; @@ -163,25 +167,24 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, if (unlikely(!fsnotify_inode_watches_children(p_inode))) { __fsnotify_update_child_dentry_flags(p_inode); } else if (p_inode->i_fsnotify_mask & mask & ALL_FSNOTIFY_EVENTS) { - struct name_snapshot name; + /* When notifying parent, child should be passed as data */ + WARN_ON_ONCE(inode != fsnotify_data_inode(data, data_type)); - /* - * We are notifying a parent, so set a flag in mask to inform - * backend that event has information about a child entry. - */ + /* Notify both parent and child with child name info */ + inode = p_inode; take_dentry_name_snapshot(&name, dentry); - ret = fsnotify(p_inode, mask | FS_EVENT_ON_CHILD, data, - data_type, &name.name, 0); - release_dentry_name_snapshot(&name); + file_name = &name.name; + mask |= FS_EVENT_ON_CHILD; } - dput(parent); +notify: + ret = fsnotify(inode, mask, data, data_type, file_name, 0); - if (ret) - return ret; + if (file_name) + release_dentry_name_snapshot(&name); + dput(parent); -notify_child: - return fsnotify(d_inode(dentry), mask, data, data_type, NULL, 0); + return ret; } EXPORT_SYMBOL_GPL(__fsnotify_parent); @@ -322,12 +325,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_type, struct super_block *sb = to_tell->i_sb; struct inode *dir = file_name ? to_tell : NULL; struct mount *mnt = NULL; + struct inode *child = NULL; int ret = 0; __u32 test_mask, marks_mask; if (path) mnt = real_mount(path->mnt); + if (mask & FS_EVENT_ON_CHILD) + child = fsnotify_data_inode(data, data_type); + /* * Optimization: srcu_read_lock() has a memory barrier which can * be expensive. It protects walking the *_fsnotify_marks lists. @@ -336,21 +343,20 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_type, * need SRCU to keep them "alive". */ if (!to_tell->i_fsnotify_marks && !sb->s_fsnotify_marks && - (!mnt || !mnt->mnt_fsnotify_marks)) + (!mnt || !mnt->mnt_fsnotify_marks) && + (!child || !child->i_fsnotify_marks)) return 0; - /* An event "on child" is not intended for a mount/sb mark */ - marks_mask = to_tell->i_fsnotify_mask; - if (!(mask & FS_EVENT_ON_CHILD)) { - marks_mask |= sb->s_fsnotify_mask; - if (mnt) - marks_mask |= mnt->mnt_fsnotify_mask; - } + marks_mask = to_tell->i_fsnotify_mask | sb->s_fsnotify_mask; + if (mnt) + marks_mask |= mnt->mnt_fsnotify_mask; + if (child) + marks_mask |= child->i_fsnotify_mask; + /* * if this is a modify event we may need to clear the ignored masks - * otherwise return if neither the inode nor the vfsmount/sb care about - * this type of event. + * otherwise return if none of the marks care about this type of event. */ test_mask = (mask & ALL_FSNOTIFY_EVENTS); if (!(mask & FS_MODIFY) && !(test_mask & marks_mask)) @@ -366,6 +372,10 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_type, iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] = fsnotify_first_mark(&mnt->mnt_fsnotify_marks); } + if (child) { + iter_info.marks[FSNOTIFY_OBJ_TYPE_CHILD] = + fsnotify_first_mark(&child->i_fsnotify_marks); + } /* * We need to merge inode/vfsmount/sb mark lists so that e.g. inode mark |