Commit 7372e79c authored by Amir Goldstein's avatar Amir Goldstein Committed by Jan Kara

fanotify: fix logic of reporting name info with watched parent

The victim inode's parent and name info is required when an event
needs to be delivered to a group interested in filename info OR
when the inode's parent is interested in an event on its children.

Let us call the first condition 'parent_needed' and the second
condition 'parent_interested'.

In fsnotify_parent(), the condition where the inode's parent is
interested in some events on its children, but not necessarily
interested the specific event is called 'parent_watched'.

fsnotify_parent() tests the condition (!parent_watched && !parent_needed)
for sending the event without parent and name info, which is correct.

It then wrongly assumes that parent_watched implies !parent_needed
and tests the condition (parent_watched && !parent_interested)
for sending the event without parent and name info, which is wrong,
because parent may still be needed by some group.

For example, after initializing a group with FAN_REPORT_DFID_NAME and
adding a FAN_MARK_MOUNT with FAN_OPEN mask, open events on non-directory
children of "testdir" are delivered with file name info.

After adding another mark to the same group on the parent "testdir"
with FAN_CLOSE|FAN_EVENT_ON_CHILD mask, open events on non-directory
children of "testdir" are no longer delivered with file name info.

Fix the logic and use auxiliary variables to clarify the conditions.

Fixes: 9b93f331 ("fsnotify: send event with parent/name info to sb/mount/non-dir marks")
Cc: stable@vger.kernel.org#v5.9
Link: https://lore.kernel.org/r/20201108105906.8493-1-amir73il@gmail.comSigned-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent b7cbaf59
...@@ -178,6 +178,7 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, ...@@ -178,6 +178,7 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
struct dentry *parent; struct dentry *parent;
bool parent_watched = dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED; bool parent_watched = dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED;
bool parent_needed, parent_interested;
__u32 p_mask; __u32 p_mask;
struct inode *p_inode = NULL; struct inode *p_inode = NULL;
struct name_snapshot name; struct name_snapshot name;
...@@ -193,7 +194,8 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, ...@@ -193,7 +194,8 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
return 0; return 0;
parent = NULL; parent = NULL;
if (!parent_watched && !fsnotify_event_needs_parent(inode, mnt, mask)) parent_needed = fsnotify_event_needs_parent(inode, mnt, mask);
if (!parent_watched && !parent_needed)
goto notify; goto notify;
/* Does parent inode care about events on children? */ /* Does parent inode care about events on children? */
...@@ -205,17 +207,17 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, ...@@ -205,17 +207,17 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
/* /*
* Include parent/name in notification either if some notification * Include parent/name in notification either if some notification
* groups require parent info (!parent_watched case) or the parent is * groups require parent info or the parent is interested in this event.
* interested in this event.
*/ */
if (!parent_watched || (mask & p_mask & ALL_FSNOTIFY_EVENTS)) { parent_interested = mask & p_mask & ALL_FSNOTIFY_EVENTS;
if (parent_needed || parent_interested) {
/* When notifying parent, child should be passed as data */ /* When notifying parent, child should be passed as data */
WARN_ON_ONCE(inode != fsnotify_data_inode(data, data_type)); WARN_ON_ONCE(inode != fsnotify_data_inode(data, data_type));
/* Notify both parent and child with child name info */ /* Notify both parent and child with child name info */
take_dentry_name_snapshot(&name, dentry); take_dentry_name_snapshot(&name, dentry);
file_name = &name.name; file_name = &name.name;
if (parent_watched) if (parent_interested)
mask |= FS_EVENT_ON_CHILD; mask |= FS_EVENT_ON_CHILD;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment