diff options
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r-- | fs/notify/fsnotify.c | 201 |
1 files changed, 166 insertions, 35 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index fcc2f064af83..4d2a82c1ceb1 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/gfp.h> | 21 | #include <linux/gfp.h> |
22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/mount.h> | ||
24 | #include <linux/srcu.h> | 25 | #include <linux/srcu.h> |
25 | 26 | ||
26 | #include <linux/fsnotify_backend.h> | 27 | #include <linux/fsnotify_backend.h> |
@@ -35,6 +36,11 @@ void __fsnotify_inode_delete(struct inode *inode) | |||
35 | } | 36 | } |
36 | EXPORT_SYMBOL_GPL(__fsnotify_inode_delete); | 37 | EXPORT_SYMBOL_GPL(__fsnotify_inode_delete); |
37 | 38 | ||
39 | void __fsnotify_vfsmount_delete(struct vfsmount *mnt) | ||
40 | { | ||
41 | fsnotify_clear_marks_by_mount(mnt); | ||
42 | } | ||
43 | |||
38 | /* | 44 | /* |
39 | * Given an inode, first check if we care what happens to our children. Inotify | 45 | * Given an inode, first check if we care what happens to our children. Inotify |
40 | * and dnotify both tell their parents about events. If we care about any event | 46 | * and dnotify both tell their parents about events. If we care about any event |
@@ -78,13 +84,16 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) | |||
78 | } | 84 | } |
79 | 85 | ||
80 | /* Notify this dentry's parent about a child's events. */ | 86 | /* Notify this dentry's parent about a child's events. */ |
81 | void __fsnotify_parent(struct dentry *dentry, __u32 mask) | 87 | void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) |
82 | { | 88 | { |
83 | struct dentry *parent; | 89 | struct dentry *parent; |
84 | struct inode *p_inode; | 90 | struct inode *p_inode; |
85 | bool send = false; | 91 | bool send = false; |
86 | bool should_update_children = false; | 92 | bool should_update_children = false; |
87 | 93 | ||
94 | if (!dentry) | ||
95 | dentry = file->f_path.dentry; | ||
96 | |||
88 | if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) | 97 | if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) |
89 | return; | 98 | return; |
90 | 99 | ||
@@ -115,8 +124,12 @@ void __fsnotify_parent(struct dentry *dentry, __u32 mask) | |||
115 | * specifies these are events which came from a child. */ | 124 | * specifies these are events which came from a child. */ |
116 | mask |= FS_EVENT_ON_CHILD; | 125 | mask |= FS_EVENT_ON_CHILD; |
117 | 126 | ||
118 | fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, | 127 | if (file) |
119 | dentry->d_name.name, 0); | 128 | fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE, |
129 | dentry->d_name.name, 0); | ||
130 | else | ||
131 | fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, | ||
132 | dentry->d_name.name, 0); | ||
120 | dput(parent); | 133 | dput(parent); |
121 | } | 134 | } |
122 | 135 | ||
@@ -127,63 +140,181 @@ void __fsnotify_parent(struct dentry *dentry, __u32 mask) | |||
127 | } | 140 | } |
128 | EXPORT_SYMBOL_GPL(__fsnotify_parent); | 141 | EXPORT_SYMBOL_GPL(__fsnotify_parent); |
129 | 142 | ||
143 | static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, | ||
144 | struct fsnotify_mark *inode_mark, | ||
145 | struct fsnotify_mark *vfsmount_mark, | ||
146 | __u32 mask, void *data, | ||
147 | int data_is, u32 cookie, | ||
148 | const unsigned char *file_name, | ||
149 | struct fsnotify_event **event) | ||
150 | { | ||
151 | struct fsnotify_group *group = inode_mark->group; | ||
152 | __u32 inode_test_mask = (mask & ~FS_EVENT_ON_CHILD); | ||
153 | __u32 vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD); | ||
154 | |||
155 | pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" | ||
156 | " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, | ||
157 | mnt, inode_mark, mask, data, data_is, cookie, *event); | ||
158 | |||
159 | /* clear ignored on inode modification */ | ||
160 | if (mask & FS_MODIFY) { | ||
161 | if (inode_mark && | ||
162 | !(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) | ||
163 | inode_mark->ignored_mask = 0; | ||
164 | if (vfsmount_mark && | ||
165 | !(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) | ||
166 | vfsmount_mark->ignored_mask = 0; | ||
167 | } | ||
168 | |||
169 | /* does the inode mark tell us to do something? */ | ||
170 | if (inode_mark) { | ||
171 | inode_test_mask &= inode_mark->mask; | ||
172 | inode_test_mask &= ~inode_mark->ignored_mask; | ||
173 | } | ||
174 | |||
175 | /* does the vfsmount_mark tell us to do something? */ | ||
176 | if (vfsmount_mark) { | ||
177 | vfsmount_test_mask &= vfsmount_mark->mask; | ||
178 | vfsmount_test_mask &= ~vfsmount_mark->ignored_mask; | ||
179 | if (inode_mark) | ||
180 | vfsmount_test_mask &= ~inode_mark->ignored_mask; | ||
181 | } | ||
182 | |||
183 | if (!inode_test_mask && !vfsmount_test_mask) | ||
184 | return 0; | ||
185 | |||
186 | if (group->ops->should_send_event(group, to_tell, inode_mark, | ||
187 | vfsmount_mark, mask, data, | ||
188 | data_is) == false) | ||
189 | return 0; | ||
190 | |||
191 | if (!*event) { | ||
192 | *event = fsnotify_create_event(to_tell, mask, data, | ||
193 | data_is, file_name, | ||
194 | cookie, GFP_KERNEL); | ||
195 | if (!*event) | ||
196 | return -ENOMEM; | ||
197 | } | ||
198 | return group->ops->handle_event(group, inode_mark, vfsmount_mark, *event); | ||
199 | } | ||
200 | |||
130 | /* | 201 | /* |
131 | * This is the main call to fsnotify. The VFS calls into hook specific functions | 202 | * This is the main call to fsnotify. The VFS calls into hook specific functions |
132 | * in linux/fsnotify.h. Those functions then in turn call here. Here will call | 203 | * in linux/fsnotify.h. Those functions then in turn call here. Here will call |
133 | * out to all of the registered fsnotify_group. Those groups can then use the | 204 | * out to all of the registered fsnotify_group. Those groups can then use the |
134 | * notification event in whatever means they feel necessary. | 205 | * notification event in whatever means they feel necessary. |
135 | */ | 206 | */ |
136 | void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const char *file_name, u32 cookie) | 207 | int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, |
208 | const unsigned char *file_name, u32 cookie) | ||
137 | { | 209 | { |
138 | struct fsnotify_group *group; | 210 | struct hlist_node *inode_node, *vfsmount_node; |
211 | struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL; | ||
212 | struct fsnotify_group *inode_group, *vfsmount_group; | ||
139 | struct fsnotify_event *event = NULL; | 213 | struct fsnotify_event *event = NULL; |
140 | int idx; | 214 | struct vfsmount *mnt; |
215 | int idx, ret = 0; | ||
216 | bool used_inode = false, used_vfsmount = false; | ||
141 | /* global tests shouldn't care about events on child only the specific event */ | 217 | /* global tests shouldn't care about events on child only the specific event */ |
142 | __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); | 218 | __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); |
143 | 219 | ||
144 | if (list_empty(&fsnotify_groups)) | 220 | if (data_is == FSNOTIFY_EVENT_FILE) |
145 | return; | 221 | mnt = ((struct file *)data)->f_path.mnt; |
222 | else | ||
223 | mnt = NULL; | ||
146 | 224 | ||
147 | if (!(test_mask & fsnotify_mask)) | ||
148 | return; | ||
149 | |||
150 | if (!(test_mask & to_tell->i_fsnotify_mask)) | ||
151 | return; | ||
152 | /* | 225 | /* |
153 | * SRCU!! the groups list is very very much read only and the path is | 226 | * if this is a modify event we may need to clear the ignored masks |
154 | * very hot. The VAST majority of events are not going to need to do | 227 | * otherwise return if neither the inode nor the vfsmount care about |
155 | * anything other than walk the list so it's crazy to pre-allocate. | 228 | * this type of event. |
156 | */ | 229 | */ |
157 | idx = srcu_read_lock(&fsnotify_grp_srcu); | 230 | if (!(mask & FS_MODIFY) && |
158 | list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { | 231 | !(test_mask & to_tell->i_fsnotify_mask) && |
159 | if (test_mask & group->mask) { | 232 | !(mnt && test_mask & mnt->mnt_fsnotify_mask)) |
160 | if (!group->ops->should_send_event(group, to_tell, mask)) | 233 | return 0; |
161 | continue; | 234 | |
162 | if (!event) { | 235 | idx = srcu_read_lock(&fsnotify_mark_srcu); |
163 | event = fsnotify_create_event(to_tell, mask, data, | 236 | |
164 | data_is, file_name, cookie, | 237 | if ((mask & FS_MODIFY) || |
165 | GFP_KERNEL); | 238 | (test_mask & to_tell->i_fsnotify_mask)) |
166 | /* shit, we OOM'd and now we can't tell, maybe | 239 | inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first, |
167 | * someday someone else will want to do something | 240 | &fsnotify_mark_srcu); |
168 | * here */ | 241 | else |
169 | if (!event) | 242 | inode_node = NULL; |
170 | break; | 243 | |
171 | } | 244 | if (mnt) { |
172 | group->ops->handle_event(group, event); | 245 | if ((mask & FS_MODIFY) || |
246 | (test_mask & mnt->mnt_fsnotify_mask)) | ||
247 | vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first, | ||
248 | &fsnotify_mark_srcu); | ||
249 | else | ||
250 | vfsmount_node = NULL; | ||
251 | } else { | ||
252 | mnt = NULL; | ||
253 | vfsmount_node = NULL; | ||
254 | } | ||
255 | |||
256 | while (inode_node || vfsmount_node) { | ||
257 | if (inode_node) { | ||
258 | inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu), | ||
259 | struct fsnotify_mark, i.i_list); | ||
260 | inode_group = inode_mark->group; | ||
261 | } else | ||
262 | inode_group = (void *)-1; | ||
263 | |||
264 | if (vfsmount_node) { | ||
265 | vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu), | ||
266 | struct fsnotify_mark, m.m_list); | ||
267 | vfsmount_group = vfsmount_mark->group; | ||
268 | } else | ||
269 | vfsmount_group = (void *)-1; | ||
270 | |||
271 | if (inode_group < vfsmount_group) { | ||
272 | /* handle inode */ | ||
273 | send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, | ||
274 | data_is, cookie, file_name, &event); | ||
275 | used_inode = true; | ||
276 | } else if (vfsmount_group < inode_group) { | ||
277 | send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, | ||
278 | data_is, cookie, file_name, &event); | ||
279 | used_vfsmount = true; | ||
280 | } else { | ||
281 | send_to_group(to_tell, mnt, inode_mark, vfsmount_mark, | ||
282 | mask, data, data_is, cookie, file_name, | ||
283 | &event); | ||
284 | used_vfsmount = true; | ||
285 | used_inode = true; | ||
173 | } | 286 | } |
287 | |||
288 | if (used_inode) | ||
289 | inode_node = srcu_dereference(inode_node->next, | ||
290 | &fsnotify_mark_srcu); | ||
291 | if (used_vfsmount) | ||
292 | vfsmount_node = srcu_dereference(vfsmount_node->next, | ||
293 | &fsnotify_mark_srcu); | ||
174 | } | 294 | } |
175 | srcu_read_unlock(&fsnotify_grp_srcu, idx); | 295 | |
296 | srcu_read_unlock(&fsnotify_mark_srcu, idx); | ||
176 | /* | 297 | /* |
177 | * fsnotify_create_event() took a reference so the event can't be cleaned | 298 | * fsnotify_create_event() took a reference so the event can't be cleaned |
178 | * up while we are still trying to add it to lists, drop that one. | 299 | * up while we are still trying to add it to lists, drop that one. |
179 | */ | 300 | */ |
180 | if (event) | 301 | if (event) |
181 | fsnotify_put_event(event); | 302 | fsnotify_put_event(event); |
303 | |||
304 | return ret; | ||
182 | } | 305 | } |
183 | EXPORT_SYMBOL_GPL(fsnotify); | 306 | EXPORT_SYMBOL_GPL(fsnotify); |
184 | 307 | ||
185 | static __init int fsnotify_init(void) | 308 | static __init int fsnotify_init(void) |
186 | { | 309 | { |
187 | return init_srcu_struct(&fsnotify_grp_srcu); | 310 | int ret; |
311 | |||
312 | BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23); | ||
313 | |||
314 | ret = init_srcu_struct(&fsnotify_mark_srcu); | ||
315 | if (ret) | ||
316 | panic("initializing fsnotify_mark_srcu"); | ||
317 | |||
318 | return 0; | ||
188 | } | 319 | } |
189 | subsys_initcall(fsnotify_init); | 320 | core_initcall(fsnotify_init); |