1 From 8edc6e1688fc8f02c8c1f53a2ec4928cb1055f4d Mon Sep 17 00:00:00 2001
2 From: Jan Kara <jack@suse.cz>
3 Date: Thu, 13 Nov 2014 15:19:33 -0800
4 Subject: [PATCH] fanotify: fix notification of groups with inode & mount marks
6 fsnotify() needs to merge inode and mount marks lists when notifying
7 groups about events so that ignore masks from inode marks are reflected
8 in mount mark notifications and groups are notified in proper order
9 (according to priorities).
11 Currently the sorting of the lists done by fsnotify_add_inode_mark() /
12 fsnotify_add_vfsmount_mark() and fsnotify() differed which resulted
13 ignore masks not being used in some cases.
15 Fix the problem by always using the same comparison function when
16 sorting / merging the mark lists.
18 Thanks to Heinrich Schuchardt for improvements of my patch.
20 Link: https://bugzilla.kernel.org/show_bug.cgi?id=87721
21 Signed-off-by: Jan Kara <jack@suse.cz>
22 Reported-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
23 Tested-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
24 Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
25 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
27 fs/notify/fsnotify.c | 36 +++++++++++++++++++++---------------
28 fs/notify/fsnotify.h | 4 ++++
29 fs/notify/inode_mark.c | 8 +++-----
30 fs/notify/mark.c | 36 ++++++++++++++++++++++++++++++++++++
31 fs/notify/vfsmount_mark.c | 8 +++-----
32 5 files changed, 67 insertions(+), 25 deletions(-)
34 diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
35 index 9d3e9c5..89326ac 100644
36 --- a/fs/notify/fsnotify.c
37 +++ b/fs/notify/fsnotify.c
38 @@ -229,8 +229,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
43 + * We need to merge inode & vfsmount mark lists so that inode mark
44 + * ignore masks are properly reflected for mount mark notifications.
45 + * That's why this traversal is so complicated...
47 while (inode_node || vfsmount_node) {
48 - inode_group = vfsmount_group = NULL;
51 + vfsmount_group = NULL;
52 + vfsmount_mark = NULL;
55 inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
56 @@ -244,21 +252,19 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
57 vfsmount_group = vfsmount_mark->group;
60 - if (inode_group > vfsmount_group) {
62 - ret = send_to_group(to_tell, inode_mark, NULL, mask,
63 - data, data_is, cookie, file_name);
64 - /* we didn't use the vfsmount_mark */
65 - vfsmount_group = NULL;
66 - } else if (vfsmount_group > inode_group) {
67 - ret = send_to_group(to_tell, NULL, vfsmount_mark, mask,
68 - data, data_is, cookie, file_name);
71 - ret = send_to_group(to_tell, inode_mark, vfsmount_mark,
72 - mask, data, data_is, cookie,
74 + if (inode_group && vfsmount_group) {
75 + int cmp = fsnotify_compare_groups(inode_group,
80 + } else if (cmp < 0) {
81 + vfsmount_group = NULL;
82 + vfsmount_mark = NULL;
85 + ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask,
86 + data, data_is, cookie, file_name);
88 if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
90 diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
91 index 9c0898c..3b68b0a 100644
92 --- a/fs/notify/fsnotify.h
93 +++ b/fs/notify/fsnotify.h
94 @@ -12,6 +12,10 @@ extern void fsnotify_flush_notify(struct fsnotify_group *group);
95 /* protects reads of inode and vfsmount marks list */
96 extern struct srcu_struct fsnotify_mark_srcu;
98 +/* compare two groups for sorting of marks lists */
99 +extern int fsnotify_compare_groups(struct fsnotify_group *a,
100 + struct fsnotify_group *b);
102 extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark,
104 /* add a mark to an inode */
105 diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
106 index e849714..dfbf544 100644
107 --- a/fs/notify/inode_mark.c
108 +++ b/fs/notify/inode_mark.c
109 @@ -194,6 +194,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
111 struct fsnotify_mark *lmark, *last = NULL;
115 mark->flags |= FSNOTIFY_MARK_FLAG_INODE;
117 @@ -219,11 +220,8 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
121 - if (mark->group->priority < lmark->group->priority)
124 - if ((mark->group->priority == lmark->group->priority) &&
125 - (mark->group < lmark->group))
126 + cmp = fsnotify_compare_groups(lmark->group, mark->group);
130 hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list);
131 diff --git a/fs/notify/mark.c b/fs/notify/mark.c
132 index d90deaa..34c38fa 100644
133 --- a/fs/notify/mark.c
134 +++ b/fs/notify/mark.c
135 @@ -210,6 +210,42 @@ void fsnotify_set_mark_ignored_mask_locked(struct fsnotify_mark *mark, __u32 mas
139 + * Sorting function for lists of fsnotify marks.
141 + * Fanotify supports different notification classes (reflected as priority of
142 + * notification group). Events shall be passed to notification groups in
143 + * decreasing priority order. To achieve this marks in notification lists for
144 + * inodes and vfsmounts are sorted so that priorities of corresponding groups
147 + * Furthermore correct handling of the ignore mask requires processing inode
148 + * and vfsmount marks of each group together. Using the group address as
149 + * further sort criterion provides a unique sorting order and thus we can
150 + * merge inode and vfsmount lists of marks in linear time and find groups
151 + * present in both lists.
153 + * A return value of 1 signifies that b has priority over a.
154 + * A return value of 0 signifies that the two marks have to be handled together.
155 + * A return value of -1 signifies that a has priority over b.
157 +int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
165 + if (a->priority < b->priority)
167 + if (a->priority > b->priority)
175 * Attach an initialized mark to a given group and fs object.
176 * These marks may be used for the fsnotify backend to determine which
177 * event types should be delivered to which group.
178 diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c
179 index ac851e8..faefa72 100644
180 --- a/fs/notify/vfsmount_mark.c
181 +++ b/fs/notify/vfsmount_mark.c
182 @@ -153,6 +153,7 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
183 struct mount *m = real_mount(mnt);
184 struct fsnotify_mark *lmark, *last = NULL;
188 mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT;
190 @@ -178,11 +179,8 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
194 - if (mark->group->priority < lmark->group->priority)
197 - if ((mark->group->priority == lmark->group->priority) &&
198 - (mark->group < lmark->group))
199 + cmp = fsnotify_compare_groups(lmark->group, mark->group);
203 hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list);