From: Amir Goldstein <amir73il@xxxxxxxxx> 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@xxxxxxxxx> --- 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