aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify/fsnotify.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r--fs/notify/fsnotify.c68
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. */
87void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) 87int __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}
141EXPORT_SYMBOL_GPL(__fsnotify_parent); 121EXPORT_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;
283out:
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