[PATCH v8 03/19] fsnotify: add helper to check if file is actually being watched

Josef Bacik
2024-11-15T15:31:27Z

From: Amir Goldstein <amir73il@gmail.com>

So far, we set FMODE_NONOTIFY_ flags at open time if we know that there
are no permission event watchers at all on the filesystem, but lack of
FMODE_NONOTIFY_ flags does not mean that the file is actually watched.

To make the flags more accurate we add a helper that checks if the
file's inode, mount, sb or parent are being watched for a set of events.

This is going to be used for setting FMODE_NONOTIFY_HSM only when the
specific file is actually watched for pre-content events.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/notify/fsnotify.c | 36 +++++++++++++++++++++++++-------
include/linux/fsnotify_backend.h | 7 +++++++
2 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index f976949d2634..33576a848a9f 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -193,16 +193,38 @@ static bool fsnotify_event_needs_parent(struct inode *inode, __u32 mnt_mask,
return mask & marks_mask;
}

-/* Are there any inode/mount/sb objects that are interested in this event? */
-static inline bool fsnotify_object_watched(struct inode *inode, __u32 mnt_mask,
- __u32 mask)
+/* Are there any inode/mount/sb objects that watch for these events? */
+static inline __u32 fsnotify_object_watched(struct inode *inode, __u32 mnt_mask,
+ __u32 events_mask)
{
__u32 marks_mask = READ_ONCE(inode->i_fsnotify_mask) | mnt_mask |
READ_ONCE(inode->i_sb->s_fsnotify_mask);

- return mask & marks_mask & ALL_FSNOTIFY_EVENTS;
+ return events_mask & marks_mask;
}

+/* Are there any inode/mount/sb/parent objects that watch for these events? */
+__u32 fsnotify_file_object_watched(struct file *file, __u32 events_mask)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct dentry *parent;
+ __u32 marks_mask, mnt_mask =
+ READ_ONCE(real_mount(file->f_path.mnt)->mnt_fsnotify_mask);
+
+ marks_mask = fsnotify_object_watched(d_inode(dentry), mnt_mask,
+ events_mask);
+
+ if (likely(!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)))
+ return marks_mask;
+
+ parent = dget_parent(dentry);
+ marks_mask |= fsnotify_inode_watches_children(d_inode(parent));
+ dput(parent);
+
+ return marks_mask & events_mask;
+}
+EXPORT_SYMBOL_GPL(fsnotify_file_object_watched);
+
/*
* Notify this dentry's parent about a child's events with child name info
* if parent is watching or if inode/sb/mount are interested in events with
@@ -221,7 +243,7 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
struct dentry *parent;
bool parent_watched = dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED;
bool parent_needed, parent_interested;
- __u32 p_mask;
+ __u32 p_mask, test_mask = mask & ALL_FSNOTIFY_EVENTS;
struct inode *p_inode = NULL;
struct name_snapshot name;
struct qstr *file_name = NULL;
@@ -229,7 +251,7 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,

/* Optimize the likely case of nobody watching this path */
if (likely(!parent_watched &&
- !fsnotify_object_watched(inode, mnt_mask, mask)))
+ !fsnotify_object_watched(inode, mnt_mask, test_mask)))
return 0;

parent = NULL;
@@ -248,7 +270,7 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
* Include parent/name in notification either if some notification
* groups require parent info or the parent is interested in this event.
*/
- parent_interested = mask & p_mask & ALL_FSNOTIFY_EVENTS;
+ parent_interested = p_mask & test_mask;
if (parent_needed || parent_interested) {
/* When notifying parent, child should be passed as data */
WARN_ON_ONCE(inode != fsnotify_data_inode(data, data_type));
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 3ecf7768e577..99d81c3c11d7 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -855,8 +855,15 @@ static inline void fsnotify_init_event(struct fsnotify_event *event)
INIT_LIST_HEAD(&event->list);
}

+__u32 fsnotify_file_object_watched(struct file *file, __u32 mask);
+
#else

+static inline __u32 fsnotify_file_object_watched(struct file *file, __u32 mask)
+{
+ return 0;
+}
+
static inline int fsnotify(__u32 mask, const void *data, int data_type,
struct inode *dir, const struct qstr *name,
struct inode *inode, u32 cookie)
--
2.43.0