@@ -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
*/
@@ -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);
@@ -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.
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@gmail.com> --- fs/notify/fanotify/fanotify.c | 16 +++++++++++++--- fs/notify/fsnotify.c | 13 +++++++++---- include/linux/fsnotify_backend.h | 13 +++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-)