diff mbox

[RFC,4/6] fanotify: report file name to root inode watch with FS_EVENT_ON_CHILD

Message ID 1489411223-12081-5-git-send-email-amir73il@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Amir Goldstein March 13, 2017, 1:20 p.m. UTC
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(-)
diff mbox

Patch

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.