When adding a root watch with FS_EVENT_ON_DESCENDANT flags (FS_EVENT_ON_CHILD|FS_EVENT_ON_SB), deliver non directory events to root inode as if they are reported to the parent inode, including the file name. This is only relevant to events open/modify/attrib/close, which can be reported to both parent and self. Filename events (create/detete/move) always include file name and self events (move_self/delete_self) never include file name. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/notify/fanotify/fanotify.c | 16 +++++++++++++--- fs/notify/fsnotify.c | 13 +++++++++---- include/linux/fsnotify_backend.h | 13 +++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index ca9894c..4b74e56 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -186,11 +186,20 @@ struct fanotify_event_info *fanotify_alloc_event(struct fsnotify_group *group, int alloc_len = sizeof(*ffe); int name_len = 0; - if ((mask & FAN_FILENAME_EVENTS) && file_name && + /* + * We need to report the file name either for filename events + * (create,delete,move) or for events that happen on non + * directory inodes when reporting file ids to root sb inode + * and only if user has requested to get filename info. + */ + if (file_name && + ((mask & FAN_FILENAME_EVENTS) || + !d_is_dir(path->dentry)) && (group->fanotify_data.flags & FAN_EVENT_INFO_NAME)) { name_len = strlen(file_name); alloc_len += name_len + 1; } + ffe = kmalloc(alloc_len, GFP_KERNEL); if (!ffe) return NULL; @@ -204,8 +213,9 @@ struct fanotify_event_info *fanotify_alloc_event(struct fsnotify_group *group, if ((mask & FAN_EVENT_ON_SB) && (group->fanotify_data.flags & FAN_EVENT_INFO_FH)) { /* - * Encode only parent (dentry) for filename events - * and both parent and child for other events. + * Encode only parent inode for filename events + * and events on directories. Encode both parent + * and child inodes for other events. * ffe->fid is big enough to encode xfs type 0x82: * 64bit parent+child inodes and 32bit generations */ diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 9f0c988..b99e51d 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -95,11 +95,16 @@ int __fsnotify_parent(const struct path *path, struct dentry *dentry, __u32 mask if (!dentry) dentry = path->dentry; - if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) + if (dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED) { + parent = dget_parent(dentry); + } else if (unlikely(fsnotify_sb_root_watches_descendants(dentry)) && + !(mask & FS_ISDIR)) { + /* Parent is not watching, but root inode is watching */ + parent = dget(dentry->d_sb->s_root); + } else { return 0; - - parent = dget_parent(dentry); - p_inode = parent->d_inode; + } + p_inode = d_inode(parent); if (unlikely(!fsnotify_inode_watches_children(p_inode))) __fsnotify_update_child_dentry_flags(p_inode); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 3be81d9..e23c549 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -298,6 +298,19 @@ static inline int fsnotify_inode_watches_children(struct inode *inode) return inode->i_fsnotify_mask & FS_EVENTS_POSS_ON_CHILD; } +static inline int fsnotify_sb_root_watches_descendants(struct dentry *dentry) +{ + struct inode *root = dentry->d_sb->s_root->d_inode; + + /* All FS_EVENT_ON_DESCENDANTS flags are set if root inode may care */ + if ((root->i_fsnotify_mask & FS_EVENT_ON_DESCENDANT) != + FS_EVENT_ON_DESCENDANT) + return 0; + /* root inode might care about distant child events, does it care about + * the specific set of events that can happen on a child? */ + return root->i_fsnotify_mask & FS_EVENTS_POSS_ON_CHILD; +} + /* * Update the dentry with a flag indicating the interest of its parent to receive * filesystem events when those events happens to this dentry->d_inode. -- 2.7.4