diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e92b2c87ae94..bda588b831ad 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -83,7 +83,8 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) * events. */ static int dnotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { struct dnotify_mark *dn_mark; @@ -93,11 +94,13 @@ static int dnotify_handle_event(struct fsnotify_group *group, struct fown_struct *fown; __u32 test_mask = event->mask & ~FS_EVENT_ON_CHILD; + BUG_ON(vfsmount_mark); + to_tell = event->to_tell; - dn_mark = container_of(mark, struct dnotify_mark, fsn_mark); + dn_mark = container_of(inode_mark, struct dnotify_mark, fsn_mark); - spin_lock(&mark->lock); + spin_lock(&inode_mark->lock); prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_mask & test_mask) == 0) { @@ -111,11 +114,11 @@ static int dnotify_handle_event(struct fsnotify_group *group, else { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(mark); + dnotify_recalc_inode_mask(inode_mark); } } - spin_unlock(&mark->lock); + spin_unlock(&inode_mark->lock); return 0; } @@ -126,8 +129,9 @@ static int dnotify_handle_event(struct fsnotify_group *group, */ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, - struct fsnotify_mark *mark, __u32 mask, - void *data, int data_type) + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, + __u32 mask, void *data, int data_type) { /* not a dir, dnotify doesn't care */ if (!S_ISDIR(inode->i_mode)) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index fbd7f35c6134..ef4fa4a45c94 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -115,7 +115,8 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, #endif static int fanotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *fanotify_mark, struct fsnotify_event *event) { int ret = 0; @@ -196,8 +197,11 @@ static bool should_send_inode_event(struct fsnotify_group *group, return true; } -static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, struct fsnotify_mark *mark, +static bool fanotify_should_send_event(struct fsnotify_group *group, + struct inode *to_tell, + struct vfsmount *mnt, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", @@ -213,9 +217,10 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; if (mnt) - return should_send_vfsmount_event(group, mnt, to_tell, mark, mask); + return should_send_vfsmount_event(group, mnt, to_tell, + vfsmount_mark, mask); else - return should_send_inode_event(group, to_tell, mark, mask); + return should_send_inode_event(group, to_tell, inode_mark, mask); } const struct fsnotify_ops fanotify_fsnotify_ops = { diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index cdaa51cb698c..090b64c3b4f9 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -141,28 +141,51 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) EXPORT_SYMBOL_GPL(__fsnotify_parent); static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, - struct fsnotify_mark *mark, - __u32 mask, void *data, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, + __u32 mask, void *data, int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { - struct fsnotify_group *group = mark->group; - __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); + struct fsnotify_group *group = inode_mark->group; + __u32 inode_test_mask = (mask & ~FS_EVENT_ON_CHILD); + __u32 vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD); pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, - mnt, mark, mask, data, data_is, cookie, *event); + mnt, inode_mark, mask, data, data_is, cookie, *event); - if ((mask & FS_MODIFY) && - !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; + /* clear ignored on inode modification */ + if (mask & FS_MODIFY) { + if (inode_mark && + !(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + inode_mark->ignored_mask = 0; + if (vfsmount_mark && + !(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + vfsmount_mark->ignored_mask = 0; + } - if (!(test_mask & mark->mask & ~mark->ignored_mask)) + /* does the inode mark tell us to do something? */ + if (inode_mark) { + inode_test_mask &= inode_mark->mask; + inode_test_mask &= ~inode_mark->ignored_mask; + } + + /* does the vfsmount_mark tell us to do something? */ + if (vfsmount_mark) { + vfsmount_test_mask &= vfsmount_mark->mask; + vfsmount_test_mask &= ~vfsmount_mark->ignored_mask; + if (inode_mark) + vfsmount_test_mask &= ~inode_mark->ignored_mask; + } + + if (!inode_test_mask && !vfsmount_test_mask) return 0; - if (group->ops->should_send_event(group, to_tell, mnt, mark, mask, - data, data_is) == false) + if (group->ops->should_send_event(group, to_tell, mnt, inode_mark, + vfsmount_mark, mask, data, + data_is) == false) return 0; if (!*event) { @@ -172,7 +195,7 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, if (!*event) return -ENOMEM; } - return group->ops->handle_event(group, mark, *event); + return group->ops->handle_event(group, inode_mark, vfsmount_mark, *event); } /* @@ -213,14 +236,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if ((mask & FS_MODIFY) || (test_mask & to_tell->i_fsnotify_mask)) - inode_node = to_tell->i_fsnotify_marks.first; + inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first, + &fsnotify_mark_srcu); else inode_node = NULL; if (mnt) { if ((mask & FS_MODIFY) || (test_mask & mnt->mnt_fsnotify_mask)) - vfsmount_node = mnt->mnt_fsnotify_marks.first; + vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first, + &fsnotify_mark_srcu); else vfsmount_node = NULL; } else { @@ -245,26 +270,27 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if (inode_group < vfsmount_group) { /* handle inode */ - send_to_group(to_tell, NULL, inode_mark, mask, data, + send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, data_is, cookie, file_name, &event); used_inode = true; } else if (vfsmount_group < inode_group) { - send_to_group(to_tell, mnt, vfsmount_mark, mask, data, + send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, data_is, cookie, file_name, &event); used_vfsmount = true; } else { - send_to_group(to_tell, mnt, vfsmount_mark, mask, data, - data_is, cookie, file_name, &event); + send_to_group(to_tell, mnt, inode_mark, vfsmount_mark, + mask, data, data_is, cookie, file_name, + &event); used_vfsmount = true; - send_to_group(to_tell, NULL, inode_mark, mask, data, - data_is, cookie, file_name, &event); used_inode = true; } if (used_inode) - inode_node = inode_node->next; + inode_node = srcu_dereference(inode_node->next, + &fsnotify_mark_srcu); if (used_vfsmount) - vfsmount_node = vfsmount_node->next; + vfsmount_node = srcu_dereference(vfsmount_node->next, + &fsnotify_mark_srcu); } srcu_read_unlock(&fsnotify_mark_srcu, idx); diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 7cf518b25daa..e53f49731b6e 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -90,7 +90,8 @@ static struct fsnotify_event *inotify_merge(struct list_head *list, } static int inotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { struct inotify_inode_mark *i_mark; @@ -100,12 +101,14 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *added_event; int wd, ret = 0; + BUG_ON(vfsmount_mark); + pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, event, event->to_tell, event->mask); to_tell = event->to_tell; - i_mark = container_of(mark, struct inotify_inode_mark, + i_mark = container_of(inode_mark, struct inotify_inode_mark, fsn_mark); wd = i_mark->wd; @@ -127,8 +130,8 @@ static int inotify_handle_event(struct fsnotify_group *group, ret = PTR_ERR(added_event); } - if (mark->mask & IN_ONESHOT) - fsnotify_destroy_mark(mark); + if (inode_mark->mask & IN_ONESHOT) + fsnotify_destroy_mark(inode_mark); return ret; } @@ -140,6 +143,7 @@ static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, struct fsnotify_mark *mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { if ((mark->mask & FS_EXCL_UNLINK) && diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 2e7cc8c2a151..d38f922977f9 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -92,9 +92,12 @@ struct fsnotify_event_private_data; */ struct fsnotify_ops { bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *mark, + struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type); - int (*handle_event)(struct fsnotify_group *group, struct fsnotify_mark *mark, + int (*handle_event)(struct fsnotify_group *group, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event); void (*free_group_priv)(struct fsnotify_group *group); void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 2abb99f3459d..781ab7f4e35c 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -904,7 +904,8 @@ static void evict_chunk(struct audit_chunk *chunk) } static int audit_tree_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmonut_mark, struct fsnotify_event *event) { BUG(); @@ -920,7 +921,8 @@ static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify } static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *mark, + struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { return false; diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 1b87e757845d..a273cf340527 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -503,7 +503,8 @@ void audit_remove_watch_rule(struct audit_krule *krule) } static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *mark, + struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { return true; @@ -511,7 +512,8 @@ static bool audit_watch_should_send_event(struct fsnotify_group *group, struct i /* Update watch data in audit rules based on fsnotify events. */ static int audit_watch_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { struct inode *inode; @@ -519,7 +521,7 @@ static int audit_watch_handle_event(struct fsnotify_group *group, const char *dname = event->file_name; struct audit_parent *parent; - parent = container_of(mark, struct audit_parent, mark); + parent = container_of(inode_mark, struct audit_parent, mark); BUG_ON(group != audit_watch_group);