summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/kernfs/file.c10
-rw-r--r--fs/notify/fanotify/fanotify.c8
-rw-r--r--fs/notify/fsnotify.c60
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