--- /dev/null
+From 54a307ba8d3cd00a3902337ffaae28f436eeb1a4 Mon Sep 17 00:00:00 2001
+From: Amir Goldstein <amir73il@gmail.com>
+Date: Wed, 4 Apr 2018 23:42:18 +0300
+Subject: fanotify: fix logic of events on child
+
+From: Amir Goldstein <amir73il@gmail.com>
+
+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")
+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 3cd5eca8d7a2f]
+Signed-off-by: Nathan Chancellor <natechancellor@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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
+ !d_can_lookup(path->dentry))
+ 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 (d_is_dir(path->dentry) &&