| From: Amir Goldstein <amir73il@gmail.com> |
| Date: Wed, 4 Apr 2018 23:42:18 +0300 |
| Subject: fanotify: fix logic of events on child |
| |
| commit 54a307ba8d3cd00a3902337ffaae28f436eeb1a4 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: 1968f5eed54c ("fanotify: use both marks when possible") |
| Signed-off-by: Amir Goldstein <amir73il@gmail.com> |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/notify/fanotify/fanotify.c | 34 +++++++++++++++------------------- |
| 1 file changed, 15 insertions(+), 19 deletions(-) |
| |
| --- a/fs/notify/fanotify/fanotify.c |
| +++ b/fs/notify/fanotify/fanotify.c |
| @@ -92,7 +92,7 @@ static bool fanotify_should_send_event(s |
| 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" |
| @@ -108,24 +108,20 @@ static bool fanotify_should_send_event(s |
| !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) && |