Skip to content

Commit

Permalink
fanotify: fix logic of events on child
Browse files Browse the repository at this point in the history
commit 54a307b upstream.

When event on child inodes are sent to the parent inode mark and
parent inode mark was not marked with FAN_EVENT_ON_CHILD, the event
will not be delivered to the listener process. However, if the same
process also has a mount mark, the event to the parent inode will be
delivered regadless of the mount mark mask.

This behavior is incorrect in the case where the mount mark mask does
not contain the specific event type. For example, the process adds
a mark on a directory with mask FAN_MODIFY (without FAN_EVENT_ON_CHILD)
and a mount mark with mask FAN_CLOSE_NOWRITE (without FAN_ONDIR).

A modify event on a file inside that directory (and inside that mount)
should not create a FAN_MODIFY event, because neither of the marks
requested to get that event on the file.

Fixes: 1968f5e ("fanotify: use both marks when possible")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
[natechancellor: Fix small conflict due to lack of 3cd5eca]
Signed-off-by: Nathan Chancellor <natechancellor@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Amir Goldstein authored and Greg Kroah-Hartman committed Apr 24, 2018
1 parent daf60c6 commit 04b35f0
Showing 1 changed file with 15 additions and 19 deletions.
34 changes: 15 additions & 19 deletions fs/notify/fanotify/fanotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
u32 event_mask,
void *data, int data_type)
{
__u32 marks_mask, marks_ignored_mask;
__u32 marks_mask = 0, marks_ignored_mask = 0;
struct path *path = data;

pr_debug("%s: inode_mark=%p vfsmnt_mark=%p mask=%x data=%p"
Expand All @@ -108,24 +108,20 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
!S_ISDIR(path->dentry->d_inode->i_mode))
return false;

if (inode_mark && vfsmnt_mark) {
marks_mask = (vfsmnt_mark->mask | inode_mark->mask);
marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_mask);
} else if (inode_mark) {
/*
* if the event is for a child and this inode doesn't care about
* events on the child, don't send it!
*/
if ((event_mask & FS_EVENT_ON_CHILD) &&
!(inode_mark->mask & FS_EVENT_ON_CHILD))
return false;
marks_mask = inode_mark->mask;
marks_ignored_mask = inode_mark->ignored_mask;
} else if (vfsmnt_mark) {
marks_mask = vfsmnt_mark->mask;
marks_ignored_mask = vfsmnt_mark->ignored_mask;
} else {
BUG();
/*
* if the event is for a child and this inode doesn't care about
* events on the child, don't send it!
*/
if (inode_mark &&
(!(event_mask & FS_EVENT_ON_CHILD) ||
(inode_mark->mask & FS_EVENT_ON_CHILD))) {
marks_mask |= inode_mark->mask;
marks_ignored_mask |= inode_mark->ignored_mask;
}

if (vfsmnt_mark) {
marks_mask |= vfsmnt_mark->mask;
marks_ignored_mask |= vfsmnt_mark->ignored_mask;
}

if (S_ISDIR(path->dentry->d_inode->i_mode) &&
Expand Down

0 comments on commit 04b35f0

Please sign in to comment.