diff options
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r-- | fs/notify/fsnotify.c | 92 |
1 files changed, 70 insertions, 22 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 23b5cfbeed5..a61aaa71082 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -21,6 +21,7 @@ #include <linux/gfp.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/mount.h> #include <linux/srcu.h> #include <linux/fsnotify_backend.h> @@ -134,6 +135,45 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); +static void send_to_group(__u32 mask, + struct fsnotify_group *group, + void *data, int data_is, const char *file_name, + u32 cookie, struct fsnotify_event **event, + struct inode *to_tell) +{ + if (!group->ops->should_send_event(group, to_tell, mask, + data, data_is)) + return; + if (!*event) { + *event = fsnotify_create_event(to_tell, mask, data, + data_is, file_name, + cookie, GFP_KERNEL); + /* + * shit, we OOM'd and now we can't tell, maybe + * someday someone else will want to do something + * here + */ + if (!*event) + return; + } + group->ops->handle_event(group, *event); +} + +static bool needed_by_vfsmount(__u32 test_mask, void *data, int data_is) +{ + struct path *path; + + if (data_is == FSNOTIFY_EVENT_PATH) + path = (struct path *)data; + else if (data_is == FSNOTIFY_EVENT_FILE) + path = &((struct file *)data)->f_path; + else + return false; + + /* hook in this when mnt->mnt_fsnotify_mask is defined */ + /* return (test_mask & path->mnt->mnt_fsnotify_mask); */ + return false; +} /* * This is the main call to fsnotify. The VFS calls into hook specific functions * in linux/fsnotify.h. Those functions then in turn call here. Here will call @@ -148,38 +188,46 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - if (list_empty(&fsnotify_inode_groups)) - return; + /* if no fsnotify listeners, nothing to do */ + if (list_empty(&fsnotify_inode_groups) && + list_empty(&fsnotify_vfsmount_groups)) + return; + + /* if none of the directed listeners or vfsmount listeners care */ + if (!(test_mask & fsnotify_inode_mask) && + !(test_mask & fsnotify_vfsmount_mask)) + return; + + /* if this inode's directed listeners don't care and nothing on the vfsmount + * listeners list cares, nothing to do */ + if (!(test_mask & to_tell->i_fsnotify_mask) && + !needed_by_vfsmount(test_mask, data, data_is)) + return; - if (!(test_mask & fsnotify_inode_mask)) - return; - - if (!(test_mask & to_tell->i_fsnotify_mask)) - return; /* * SRCU!! the groups list is very very much read only and the path is * very hot. The VAST majority of events are not going to need to do * anything other than walk the list so it's crazy to pre-allocate. */ idx = srcu_read_lock(&fsnotify_grp_srcu); - list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { - if (test_mask & group->mask) { - if (!group->ops->should_send_event(group, to_tell, mask, - data, data_is)) - continue; - if (!event) { - event = fsnotify_create_event(to_tell, mask, data, - data_is, file_name, cookie, - GFP_KERNEL); - /* shit, we OOM'd and now we can't tell, maybe - * someday someone else will want to do something - * here */ - if (!event) - break; + + if (test_mask & to_tell->i_fsnotify_mask) { + list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { + if (test_mask & group->mask) { + send_to_group(mask, group, data, data_is, + file_name, cookie, &event, to_tell); } - group->ops->handle_event(group, event); } } + if (needed_by_vfsmount(test_mask, data, data_is)) { + list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { + if (test_mask & group->mask) { + send_to_group(mask, group, data, data_is, + file_name, cookie, &event, to_tell); + } + } + } + srcu_read_unlock(&fsnotify_grp_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned |