Fix issues with OverlayFS not interacting correctly with the notify system

diff --git a/fs/notify/inotify/Kconfig b/fs/notify/inotify/Kconfig
index b981fc0..137d3fa 100644
--- a/fs/notify/inotify/Kconfig
+++ b/fs/notify/inotify/Kconfig
@@ -15,3 +15,13 @@ config INOTIFY_USER
 	  For more information, see <file:Documentation/filesystems/inotify.txt>
 
 	  If unsure, say Y.
+
+
+config INOTIFY_STACKFS
+	bool "Inotify support for stackable filesystem"
+	select INOTIFY_USER
+	default y
+	---help---
+	  Say Y here to enable inotify support for stackable filesystem.
+
+	  If unsure, say N.
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index f255d37..e555ac5 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -24,6 +24,7 @@
 
 #include <linux/file.h>
 #include <linux/fs.h> /* struct inode */
+#include <linux/mount.h>
 #include <linux/fsnotify_backend.h>
 #include <linux/idr.h>
 #include <linux/init.h> /* module_init */
@@ -86,6 +87,93 @@ ctl_table inotify_table[] = {
 };
 #endif /* CONFIG_SYSCTL */
 
+#ifdef CONFIG_INOTIFY_STACKFS
+
+static DEFINE_RWLOCK(inotify_fs_lock);
+static LIST_HEAD(inotify_fs_list);
+
+static inline struct file_system_type* peek_fs_type(struct path *path)
+{
+        return path->mnt->mnt_sb->s_type;
+}
+
+static struct inotify_stackfs* inotify_get_stackfs(struct path *path)
+{
+        struct file_system_type *fs;
+        struct inotify_stackfs *fse, *ret = NULL;
+
+        fs = peek_fs_type(path);
+
+        read_lock(&inotify_fs_lock);
+        list_for_each_entry(fse, &inotify_fs_list, list) {
+                if (fse->fs_type == fs) {
+                        ret = fse;
+                        break;
+                }
+        }
+        read_unlock(&inotify_fs_lock);
+
+        return ret;
+}
+
+static inline void inotify_put_stackfs(struct inotify_stackfs *fs)
+{
+}
+
+int inotify_register_stackfs(struct inotify_stackfs *fs)
+{
+        int ret = 0;
+        struct inotify_stackfs *fse;
+
+        BUG_ON(IS_ERR_OR_NULL(fs->fs_type));
+        BUG_ON(IS_ERR_OR_NULL(fs->func));
+
+        INIT_LIST_HEAD(&fs->list);
+
+        write_lock(&inotify_fs_lock);
+        list_for_each_entry(fse, &inotify_fs_list, list) {
+                if (fse->fs_type == fs->fs_type) {
+                        write_unlock(&inotify_fs_lock);
+                        ret = -EBUSY;
+                        goto out;
+                }
+        }
+        list_add_tail(&fs->list, &inotify_fs_list);
+        write_unlock(&inotify_fs_lock);
+
+out:
+        return ret;
+}
+EXPORT_SYMBOL_GPL(inotify_register_stackfs);
+
+void inotify_unregister_stackfs(struct inotify_stackfs *fs)
+{
+        struct inotify_stackfs *fse, *n;
+
+        write_lock(&inotify_fs_lock);
+        list_for_each_entry_safe(fse, n, &inotify_fs_list, list) {
+                if (fse == fs) {
+                        list_del(&fse->list);
+                        break;
+                }
+        }
+        write_unlock(&inotify_fs_lock);
+}
+EXPORT_SYMBOL_GPL(inotify_unregister_stackfs);
+
+#else
+
+static inline struct inotify_stackfs* inotify_get_stackfs(struct path *path)
+{
+        return NULL;
+}
+
+static inline void inotify_put_stackfs(struct inotify_stackfs *fs)
+{
+}
+
+#endif /* CONFIG_INOTIFY_STACKFS */
+
 static inline __u32 inotify_arg_to_mask(u32 arg)
 {
 	__u32 mask;
@@ -348,7 +436,7 @@ static const struct file_operations inotify_fops = {
 /*
  * find_inode - resolve a user-given path to a specific inode
  */
-static int inotify_find_inode(const char __user *dirname, struct path *path, unsigned flags)
+static inline int __inotify_find_inode(const char __user *dirname, struct path *path, unsigned flags)
 {
 	int error;
 
@@ -362,6 +450,27 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
 	return error;
 }
 
+static int inotify_find_inode(const char __user *dirname, struct path *path, unsigned flags)
+{
+	int ret;
+	struct path tpath;
+	struct inotify_stackfs *fse;
+
+	ret = __inotify_find_inode(dirname, &tpath, flags);
+	if (ret)
+		return ret;
+	fse = inotify_get_stackfs(&tpath);
+	if (fse == NULL) {
+		*path = tpath;
+		return 0;
+	}
+	ret = fse->func(path, &tpath);
+	inotify_put_stackfs(fse);
+	path_put(&tpath);
+
+	return ret;
+}
+
 static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock,
 			      int *last_wd,
 			      struct inotify_inode_mark *i_mark)
diff --git a/overlayfs/super.c b/fs/overlayfs/super.c
index 508cf19..cb39ec9 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -16,6 +16,7 @@
 #include <linux/parser.h>
 #include <linux/module.h>
 #include <linux/seq_file.h>
+#include <linux/inotify.h>
 #include "overlayfs.h"
 
 MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
@@ -642,14 +643,40 @@ static struct file_system_type ovl_fs_type = {
 	.kill_sb	= kill_anon_super,
 };
 
+static int ovl_inotify_path(struct path *dst, struct path *src)
+{
+	ovl_path_real(src->dentry, dst);
+
+	path_get(dst);
+
+	return 0;
+}
+
+static struct inotify_stackfs ovl_inotify = {
+	.fs_type	= &ovl_fs_type,
+	.func		= ovl_inotify_path,
+};
+
 static int __init ovl_init(void)
 {
-	return register_filesystem(&ovl_fs_type);
+	int ret;
+
+	ret = register_filesystem(&ovl_fs_type);
+	if (ret)
+		return ret;
+	ret = inotify_register_stackfs(&ovl_inotify);
+	if (ret) {
+		pr_err("overlayfs: hook inotify error\n");
+		unregister_filesystem(&ovl_fs_type);
+	}
+
+	return ret;
 }
 
 static void __exit ovl_exit(void)
 {
-	unregister_filesystem(&ovl_fs_type);
+	inotify_unregister_stackfs(&ovl_inotify);
+ 	unregister_filesystem(&ovl_fs_type);
 }
 
 module_init(ovl_init);
diff --git a/include/linux/inotify.h b/include/linux/inotify.h
index d33041e..9d7e36f 100644
--- a/include/linux/inotify.h
+++ b/include/linux/inotify.h
@@ -10,6 +10,8 @@
 /* For O_CLOEXEC and O_NONBLOCK */
 #include <linux/fcntl.h>
 #include <linux/types.h>
+#include <linux/list.h>
+#include <linux/fs.h>
 
 /*
  * struct inotify_event - structure read from the inotify device for each event
@@ -82,6 +84,32 @@ extern struct ctl_table inotify_table[]; /* for sysctl */
 			  IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_MASK_ADD | \
 			  IN_ISDIR | IN_ONESHOT)
 
+typedef int (*inotify_path_proc)(struct path *dst, struct path *src);
+
+struct inotify_stackfs {
+	struct list_head	list;		/* entry in inotify_fs_list */
+	struct file_system_type	*fs_type;	/* registed file_system_type */
+	inotify_path_proc	func;		/* registed callback function */
+};
+
+#ifdef CONFIG_INOTIFY_STACKFS
+
+extern int inotify_register_stackfs(struct inotify_stackfs *fs);
+extern void inotify_unregister_stackfs(struct inotify_stackfs *fs);
+
+#else
+
+static inline int inotify_register_stackfs(struct inotify_stackfs *fs)
+{
+	return 0;
+}
+
+static inline void inotify_unregister_stackfs(struct inotify_stackfs *fs)
+{
+}
+
+#endif	/* CONFIG_INOTIFY_STACKFS */
+
 #endif
 
 #endif	/* _LINUX_INOTIFY_H */
