diff --git a/fs/notify/inotify/Kconfig b/fs/notify/inotify/Kconfig index b981fc0..1ccd92e 100644 --- a/fs/notify/inotify/Kconfig +++ b/fs/notify/inotify/Kconfig @@ -15,3 +15,12 @@ config INOTIFY_USER For more information, see 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 60f954a..3daff50 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -24,6 +24,7 @@ #include #include /* struct inode */ +#include #include #include #include /* module_init */ @@ -87,6 +88,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; @@ -341,7 +429,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; @@ -355,6 +443,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, struct inotify_inode_mark *i_mark) { diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 9473e79..4af5048 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "overlayfs.h" MODULE_AUTHOR("Miklos Szeredi "); @@ -671,13 +672,39 @@ static struct file_system_type ovl_fs_type = { }; MODULE_ALIAS_FS("overlayfs"); +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) { + inotify_unregister_stackfs(&ovl_inotify); unregister_filesystem(&ovl_fs_type); } diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 23aede0..2c4d7a1 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -8,6 +8,8 @@ #include #include +#include +#include extern struct ctl_table inotify_table[]; /* for sysctl */ @@ -19,4 +21,30 @@ 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 /* _LINUX_INOTIFY_H */