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;
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,
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);
}
/*
* 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;
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);
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.
* 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))
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