diff options
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r-- | fs/notify/fsnotify.c | 68 |
1 files changed, 26 insertions, 42 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 36802420d69a..20dc218707ca 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c | |||
@@ -84,59 +84,39 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) | |||
84 | } | 84 | } |
85 | 85 | ||
86 | /* Notify this dentry's parent about a child's events. */ | 86 | /* Notify this dentry's parent about a child's events. */ |
87 | void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) | 87 | int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) |
88 | { | 88 | { |
89 | struct dentry *parent; | 89 | struct dentry *parent; |
90 | struct inode *p_inode; | 90 | struct inode *p_inode; |
91 | bool send = false; | 91 | int ret = 0; |
92 | bool should_update_children = false; | ||
93 | 92 | ||
94 | if (!dentry) | 93 | if (!dentry) |
95 | dentry = path->dentry; | 94 | dentry = path->dentry; |
96 | 95 | ||
97 | if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) | 96 | if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) |
98 | return; | 97 | return 0; |
99 | 98 | ||
100 | spin_lock(&dentry->d_lock); | 99 | parent = dget_parent(dentry); |
101 | parent = dentry->d_parent; | ||
102 | p_inode = parent->d_inode; | 100 | p_inode = parent->d_inode; |
103 | 101 | ||
104 | if (fsnotify_inode_watches_children(p_inode)) { | 102 | if (unlikely(!fsnotify_inode_watches_children(p_inode))) |
105 | if (p_inode->i_fsnotify_mask & mask) { | 103 | __fsnotify_update_child_dentry_flags(p_inode); |
106 | dget(parent); | 104 | else if (p_inode->i_fsnotify_mask & mask) { |
107 | send = true; | ||
108 | } | ||
109 | } else { | ||
110 | /* | ||
111 | * The parent doesn't care about events on it's children but | ||
112 | * at least one child thought it did. We need to run all the | ||
113 | * children and update their d_flags to let them know p_inode | ||
114 | * doesn't care about them any more. | ||
115 | */ | ||
116 | dget(parent); | ||
117 | should_update_children = true; | ||
118 | } | ||
119 | |||
120 | spin_unlock(&dentry->d_lock); | ||
121 | |||
122 | if (send) { | ||
123 | /* we are notifying a parent so come up with the new mask which | 105 | /* we are notifying a parent so come up with the new mask which |
124 | * specifies these are events which came from a child. */ | 106 | * specifies these are events which came from a child. */ |
125 | mask |= FS_EVENT_ON_CHILD; | 107 | mask |= FS_EVENT_ON_CHILD; |
126 | 108 | ||
127 | if (path) | 109 | if (path) |
128 | fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, | 110 | ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, |
129 | dentry->d_name.name, 0); | 111 | dentry->d_name.name, 0); |
130 | else | 112 | else |
131 | fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, | 113 | ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, |
132 | dentry->d_name.name, 0); | 114 | dentry->d_name.name, 0); |
133 | dput(parent); | ||
134 | } | 115 | } |
135 | 116 | ||
136 | if (unlikely(should_update_children)) { | 117 | dput(parent); |
137 | __fsnotify_update_child_dentry_flags(p_inode); | 118 | |
138 | dput(parent); | 119 | return ret; |
139 | } | ||
140 | } | 120 | } |
141 | EXPORT_SYMBOL_GPL(__fsnotify_parent); | 121 | EXPORT_SYMBOL_GPL(__fsnotify_parent); |
142 | 122 | ||
@@ -275,20 +255,23 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | |||
275 | 255 | ||
276 | if (inode_group > vfsmount_group) { | 256 | if (inode_group > vfsmount_group) { |
277 | /* handle inode */ | 257 | /* handle inode */ |
278 | send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, | 258 | ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, |
279 | data_is, cookie, file_name, &event); | 259 | data_is, cookie, file_name, &event); |
280 | /* we didn't use the vfsmount_mark */ | 260 | /* we didn't use the vfsmount_mark */ |
281 | vfsmount_group = NULL; | 261 | vfsmount_group = NULL; |
282 | } else if (vfsmount_group > inode_group) { | 262 | } else if (vfsmount_group > inode_group) { |
283 | send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, | 263 | ret = send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, |
284 | data_is, cookie, file_name, &event); | 264 | data_is, cookie, file_name, &event); |
285 | inode_group = NULL; | 265 | inode_group = NULL; |
286 | } else { | 266 | } else { |
287 | send_to_group(to_tell, mnt, inode_mark, vfsmount_mark, | 267 | ret = send_to_group(to_tell, mnt, inode_mark, vfsmount_mark, |
288 | mask, data, data_is, cookie, file_name, | 268 | mask, data, data_is, cookie, file_name, |
289 | &event); | 269 | &event); |
290 | } | 270 | } |
291 | 271 | ||
272 | if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS)) | ||
273 | goto out; | ||
274 | |||
292 | if (inode_group) | 275 | if (inode_group) |
293 | inode_node = srcu_dereference(inode_node->next, | 276 | inode_node = srcu_dereference(inode_node->next, |
294 | &fsnotify_mark_srcu); | 277 | &fsnotify_mark_srcu); |
@@ -296,7 +279,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | |||
296 | vfsmount_node = srcu_dereference(vfsmount_node->next, | 279 | vfsmount_node = srcu_dereference(vfsmount_node->next, |
297 | &fsnotify_mark_srcu); | 280 | &fsnotify_mark_srcu); |
298 | } | 281 | } |
299 | 282 | ret = 0; | |
283 | out: | ||
300 | srcu_read_unlock(&fsnotify_mark_srcu, idx); | 284 | srcu_read_unlock(&fsnotify_mark_srcu, idx); |
301 | /* | 285 | /* |
302 | * fsnotify_create_event() took a reference so the event can't be cleaned | 286 | * fsnotify_create_event() took a reference so the event can't be cleaned |