Blame view

fs/notify/fsnotify.c 9.3 KB
90586523e   Eric Paris   fsnotify: unified...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2, or (at your option)
   *  any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program; see the file COPYING.  If not, write to
   *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  #include <linux/dcache.h>
  #include <linux/fs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
21
  #include <linux/gfp.h>
90586523e   Eric Paris   fsnotify: unified...
22
23
  #include <linux/init.h>
  #include <linux/module.h>
7131485a9   Eric Paris   fsnotify: mount p...
24
  #include <linux/mount.h>
90586523e   Eric Paris   fsnotify: unified...
25
26
27
28
  #include <linux/srcu.h>
  
  #include <linux/fsnotify_backend.h>
  #include "fsnotify.h"
c63181e6b   Al Viro   vfs: move fsnotif...
29
  #include "../mount.h"
90586523e   Eric Paris   fsnotify: unified...
30
31
  
  /*
3be25f49b   Eric Paris   fsnotify: add mar...
32
33
34
35
36
37
38
   * Clear all of the marks on an inode when it is being evicted from core
   */
  void __fsnotify_inode_delete(struct inode *inode)
  {
  	fsnotify_clear_marks_by_inode(inode);
  }
  EXPORT_SYMBOL_GPL(__fsnotify_inode_delete);
ca9c726ee   Andreas Gruenbacher   fsnotify: Infrast...
39
40
41
42
  void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
  {
  	fsnotify_clear_marks_by_mount(mnt);
  }
3be25f49b   Eric Paris   fsnotify: add mar...
43
  /*
c28f7e56e   Eric Paris   fsnotify: parent ...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
   * Given an inode, first check if we care what happens to our children.  Inotify
   * and dnotify both tell their parents about events.  If we care about any event
   * on a child we run all of our children and set a dentry flag saying that the
   * parent cares.  Thus when an event happens on a child it can quickly tell if
   * if there is a need to find a parent and send the event to the parent.
   */
  void __fsnotify_update_child_dentry_flags(struct inode *inode)
  {
  	struct dentry *alias;
  	int watched;
  
  	if (!S_ISDIR(inode->i_mode))
  		return;
  
  	/* determine if the children should tell inode about their events */
  	watched = fsnotify_inode_watches_children(inode);
873feea09   Nick Piggin   fs: dcache per-in...
60
  	spin_lock(&inode->i_lock);
c28f7e56e   Eric Paris   fsnotify: parent ...
61
62
63
64
65
66
67
68
  	/* run all of the dentries associated with this inode.  Since this is a
  	 * directory, there damn well better only be one item on this list */
  	list_for_each_entry(alias, &inode->i_dentry, d_alias) {
  		struct dentry *child;
  
  		/* run all of the children of the original inode and fix their
  		 * d_flags to indicate parental interest (their parent is the
  		 * original inode) */
2fd6b7f50   Nick Piggin   fs: dcache scale ...
69
  		spin_lock(&alias->d_lock);
c28f7e56e   Eric Paris   fsnotify: parent ...
70
71
72
  		list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) {
  			if (!child->d_inode)
  				continue;
2fd6b7f50   Nick Piggin   fs: dcache scale ...
73
  			spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
c28f7e56e   Eric Paris   fsnotify: parent ...
74
75
76
77
78
79
  			if (watched)
  				child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
  			else
  				child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED;
  			spin_unlock(&child->d_lock);
  		}
2fd6b7f50   Nick Piggin   fs: dcache scale ...
80
  		spin_unlock(&alias->d_lock);
c28f7e56e   Eric Paris   fsnotify: parent ...
81
  	}
873feea09   Nick Piggin   fs: dcache per-in...
82
  	spin_unlock(&inode->i_lock);
c28f7e56e   Eric Paris   fsnotify: parent ...
83
84
85
  }
  
  /* Notify this dentry's parent about a child's events. */
52420392c   Eric Paris   fsnotify: call fs...
86
  int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
c28f7e56e   Eric Paris   fsnotify: parent ...
87
88
89
  {
  	struct dentry *parent;
  	struct inode *p_inode;
52420392c   Eric Paris   fsnotify: call fs...
90
  	int ret = 0;
c28f7e56e   Eric Paris   fsnotify: parent ...
91

72acc8544   Andreas Gruenbacher   fsnotify: kill FS...
92
  	if (!dentry)
2069601b3   Linus Torvalds   Revert "fsnotify:...
93
  		dentry = path->dentry;
28c60e37f   Eric Paris   fsnotify: send st...
94

c28f7e56e   Eric Paris   fsnotify: parent ...
95
  	if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
52420392c   Eric Paris   fsnotify: call fs...
96
  		return 0;
c28f7e56e   Eric Paris   fsnotify: parent ...
97

4d4eb3667   Christoph Hellwig   fsnotify: use dge...
98
  	parent = dget_parent(dentry);
c28f7e56e   Eric Paris   fsnotify: parent ...
99
  	p_inode = parent->d_inode;
4d4eb3667   Christoph Hellwig   fsnotify: use dge...
100
101
102
  	if (unlikely(!fsnotify_inode_watches_children(p_inode)))
  		__fsnotify_update_child_dentry_flags(p_inode);
  	else if (p_inode->i_fsnotify_mask & mask) {
c28f7e56e   Eric Paris   fsnotify: parent ...
103
104
105
  		/* we are notifying a parent so come up with the new mask which
  		 * specifies these are events which came from a child. */
  		mask |= FS_EVENT_ON_CHILD;
2069601b3   Linus Torvalds   Revert "fsnotify:...
106
  		if (path)
52420392c   Eric Paris   fsnotify: call fs...
107
108
  			ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
  				       dentry->d_name.name, 0);
28c60e37f   Eric Paris   fsnotify: send st...
109
  		else
52420392c   Eric Paris   fsnotify: call fs...
110
111
  			ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
  				       dentry->d_name.name, 0);
c28f7e56e   Eric Paris   fsnotify: parent ...
112
  	}
4d4eb3667   Christoph Hellwig   fsnotify: use dge...
113
  	dput(parent);
52420392c   Eric Paris   fsnotify: call fs...
114
115
  
  	return ret;
c28f7e56e   Eric Paris   fsnotify: parent ...
116
117
  }
  EXPORT_SYMBOL_GPL(__fsnotify_parent);
613a807fe   Eric Paris   fsnotify: walk th...
118
  static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
ce8f76fb7   Eric Paris   fsnotify: pass bo...
119
120
121
  			 struct fsnotify_mark *inode_mark,
  			 struct fsnotify_mark *vfsmount_mark,
  			 __u32 mask, void *data,
613a807fe   Eric Paris   fsnotify: walk th...
122
  			 int data_is, u32 cookie,
3a9b16b40   Eric Paris   fsnotify: send fs...
123
  			 const unsigned char *file_name,
c4ec54b40   Eric Paris   fsnotify: new fsn...
124
  			 struct fsnotify_event **event)
7131485a9   Eric Paris   fsnotify: mount p...
125
  {
faa9560ae   Eric Paris   fanotify: do not ...
126
  	struct fsnotify_group *group = NULL;
84e1ab4d8   Eric Paris   fsnotify: fix ign...
127
128
  	__u32 inode_test_mask = 0;
  	__u32 vfsmount_test_mask = 0;
613a807fe   Eric Paris   fsnotify: walk th...
129

faa9560ae   Eric Paris   fanotify: do not ...
130
131
132
133
  	if (unlikely(!inode_mark && !vfsmount_mark)) {
  		BUG();
  		return 0;
  	}
ce8f76fb7   Eric Paris   fsnotify: pass bo...
134
135
136
137
138
139
140
141
142
143
  
  	/* 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;
  	}
5ba08e2ee   Eric Paris   fsnotify: add pr_...
144

ce8f76fb7   Eric Paris   fsnotify: pass bo...
145
146
  	/* does the inode mark tell us to do something? */
  	if (inode_mark) {
faa9560ae   Eric Paris   fanotify: do not ...
147
  		group = inode_mark->group;
84e1ab4d8   Eric Paris   fsnotify: fix ign...
148
  		inode_test_mask = (mask & ~FS_EVENT_ON_CHILD);
ce8f76fb7   Eric Paris   fsnotify: pass bo...
149
150
151
  		inode_test_mask &= inode_mark->mask;
  		inode_test_mask &= ~inode_mark->ignored_mask;
  	}
613a807fe   Eric Paris   fsnotify: walk th...
152

ce8f76fb7   Eric Paris   fsnotify: pass bo...
153
154
  	/* does the vfsmount_mark tell us to do something? */
  	if (vfsmount_mark) {
84e1ab4d8   Eric Paris   fsnotify: fix ign...
155
  		vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD);
faa9560ae   Eric Paris   fanotify: do not ...
156
  		group = vfsmount_mark->group;
ce8f76fb7   Eric Paris   fsnotify: pass bo...
157
158
159
160
161
  		vfsmount_test_mask &= vfsmount_mark->mask;
  		vfsmount_test_mask &= ~vfsmount_mark->ignored_mask;
  		if (inode_mark)
  			vfsmount_test_mask &= ~inode_mark->ignored_mask;
  	}
84e1ab4d8   Eric Paris   fsnotify: fix ign...
162
163
164
165
166
167
168
  	pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x inode_mark=%p"
  		 " inode_test_mask=%x vfsmount_mark=%p vfsmount_test_mask=%x"
  		 " data=%p data_is=%d cookie=%d event=%p
  ",
  		 __func__, group, to_tell, mnt, mask, inode_mark,
  		 inode_test_mask, vfsmount_mark, vfsmount_test_mask, data,
  		 data_is, cookie, *event);
faa9560ae   Eric Paris   fanotify: do not ...
169

ce8f76fb7   Eric Paris   fsnotify: pass bo...
170
  	if (!inode_test_mask && !vfsmount_test_mask)
613a807fe   Eric Paris   fsnotify: walk th...
171
  		return 0;
1968f5eed   Eric Paris   fanotify: use bot...
172
  	if (group->ops->should_send_event(group, to_tell, inode_mark,
ce8f76fb7   Eric Paris   fsnotify: pass bo...
173
174
  					  vfsmount_mark, mask, data,
  					  data_is) == false)
c4ec54b40   Eric Paris   fsnotify: new fsn...
175
  		return 0;
613a807fe   Eric Paris   fsnotify: walk th...
176

7131485a9   Eric Paris   fsnotify: mount p...
177
178
179
180
  	if (!*event) {
  		*event = fsnotify_create_event(to_tell, mask, data,
  						data_is, file_name,
  						cookie, GFP_KERNEL);
7131485a9   Eric Paris   fsnotify: mount p...
181
  		if (!*event)
c4ec54b40   Eric Paris   fsnotify: new fsn...
182
  			return -ENOMEM;
7131485a9   Eric Paris   fsnotify: mount p...
183
  	}
ce8f76fb7   Eric Paris   fsnotify: pass bo...
184
  	return group->ops->handle_event(group, inode_mark, vfsmount_mark, *event);
7131485a9   Eric Paris   fsnotify: mount p...
185
  }
c28f7e56e   Eric Paris   fsnotify: parent ...
186
  /*
90586523e   Eric Paris   fsnotify: unified...
187
188
189
190
191
   * This is the main call to fsnotify.  The VFS calls into hook specific functions
   * in linux/fsnotify.h.  Those functions then in turn call here.  Here will call
   * out to all of the registered fsnotify_group.  Those groups can then use the
   * notification event in whatever means they feel necessary.
   */
c4ec54b40   Eric Paris   fsnotify: new fsn...
192
193
  int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
  	     const unsigned char *file_name, u32 cookie)
90586523e   Eric Paris   fsnotify: unified...
194
  {
84e1ab4d8   Eric Paris   fsnotify: fix ign...
195
  	struct hlist_node *inode_node = NULL, *vfsmount_node = NULL;
613a807fe   Eric Paris   fsnotify: walk th...
196
197
  	struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL;
  	struct fsnotify_group *inode_group, *vfsmount_group;
90586523e   Eric Paris   fsnotify: unified...
198
  	struct fsnotify_event *event = NULL;
c63181e6b   Al Viro   vfs: move fsnotif...
199
  	struct mount *mnt;
c4ec54b40   Eric Paris   fsnotify: new fsn...
200
  	int idx, ret = 0;
e42e27736   Eric Paris   inotify/dnotify: ...
201
202
  	/* global tests shouldn't care about events on child only the specific event */
  	__u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
90586523e   Eric Paris   fsnotify: unified...
203

2069601b3   Linus Torvalds   Revert "fsnotify:...
204
  	if (data_is == FSNOTIFY_EVENT_PATH)
c63181e6b   Al Viro   vfs: move fsnotif...
205
  		mnt = real_mount(((struct path *)data)->mnt);
613a807fe   Eric Paris   fsnotify: walk th...
206
207
208
209
210
211
212
213
214
215
216
217
  	else
  		mnt = NULL;
  
  	/*
  	 * if this is a modify event we may need to clear the ignored masks
  	 * otherwise return if neither the inode nor the vfsmount care about
  	 * this type of event.
  	 */
  	if (!(mask & FS_MODIFY) &&
  	    !(test_mask & to_tell->i_fsnotify_mask) &&
  	    !(mnt && test_mask & mnt->mnt_fsnotify_mask))
  		return 0;
3a9fb89f4   Eric Paris   fsnotify: include...
218

75c1be487   Eric Paris   fsnotify: srcu to...
219
  	idx = srcu_read_lock(&fsnotify_mark_srcu);
7131485a9   Eric Paris   fsnotify: mount p...
220

613a807fe   Eric Paris   fsnotify: walk th...
221
222
  	if ((mask & FS_MODIFY) ||
  	    (test_mask & to_tell->i_fsnotify_mask))
ce8f76fb7   Eric Paris   fsnotify: pass bo...
223
224
  		inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first,
  					      &fsnotify_mark_srcu);
613a807fe   Eric Paris   fsnotify: walk th...
225

84e1ab4d8   Eric Paris   fsnotify: fix ign...
226
227
228
229
230
231
  	if (mnt && ((mask & FS_MODIFY) ||
  		    (test_mask & mnt->mnt_fsnotify_mask))) {
  		vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first,
  						 &fsnotify_mark_srcu);
  		inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first,
  					      &fsnotify_mark_srcu);
90586523e   Eric Paris   fsnotify: unified...
232
  	}
75c1be487   Eric Paris   fsnotify: srcu to...
233

613a807fe   Eric Paris   fsnotify: walk th...
234
  	while (inode_node || vfsmount_node) {
f72adfd54   Eric Paris   fsnotify: fix lis...
235
  		inode_group = vfsmount_group = NULL;
5f3f259fa   Eric Paris   fsnotify: reset u...
236

613a807fe   Eric Paris   fsnotify: walk th...
237
238
239
240
  		if (inode_node) {
  			inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
  						 struct fsnotify_mark, i.i_list);
  			inode_group = inode_mark->group;
f72adfd54   Eric Paris   fsnotify: fix lis...
241
  		}
613a807fe   Eric Paris   fsnotify: walk th...
242
243
244
245
246
  
  		if (vfsmount_node) {
  			vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu),
  							struct fsnotify_mark, m.m_list);
  			vfsmount_group = vfsmount_mark->group;
f72adfd54   Eric Paris   fsnotify: fix lis...
247
  		}
613a807fe   Eric Paris   fsnotify: walk th...
248

f72adfd54   Eric Paris   fsnotify: fix lis...
249
  		if (inode_group > vfsmount_group) {
613a807fe   Eric Paris   fsnotify: walk th...
250
  			/* handle inode */
ff8bcbd03   Eric Paris   fsnotify: correct...
251
252
  			ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
  					    data_is, cookie, file_name, &event);
92b4678ef   Eric Paris   fsnotify: drop tw...
253
254
  			/* we didn't use the vfsmount_mark */
  			vfsmount_group = NULL;
f72adfd54   Eric Paris   fsnotify: fix lis...
255
  		} else if (vfsmount_group > inode_group) {
c63181e6b   Al Viro   vfs: move fsnotif...
256
  			ret = send_to_group(to_tell, &mnt->mnt, NULL, vfsmount_mark, mask, data,
ff8bcbd03   Eric Paris   fsnotify: correct...
257
  					    data_is, cookie, file_name, &event);
92b4678ef   Eric Paris   fsnotify: drop tw...
258
  			inode_group = NULL;
613a807fe   Eric Paris   fsnotify: walk th...
259
  		} else {
c63181e6b   Al Viro   vfs: move fsnotif...
260
  			ret = send_to_group(to_tell, &mnt->mnt, inode_mark, vfsmount_mark,
ff8bcbd03   Eric Paris   fsnotify: correct...
261
262
  					    mask, data, data_is, cookie, file_name,
  					    &event);
7131485a9   Eric Paris   fsnotify: mount p...
263
  		}
613a807fe   Eric Paris   fsnotify: walk th...
264

ff8bcbd03   Eric Paris   fsnotify: correct...
265
266
  		if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
  			goto out;
92b4678ef   Eric Paris   fsnotify: drop tw...
267
  		if (inode_group)
ce8f76fb7   Eric Paris   fsnotify: pass bo...
268
269
  			inode_node = srcu_dereference(inode_node->next,
  						      &fsnotify_mark_srcu);
92b4678ef   Eric Paris   fsnotify: drop tw...
270
  		if (vfsmount_group)
ce8f76fb7   Eric Paris   fsnotify: pass bo...
271
272
  			vfsmount_node = srcu_dereference(vfsmount_node->next,
  							 &fsnotify_mark_srcu);
7131485a9   Eric Paris   fsnotify: mount p...
273
  	}
ff8bcbd03   Eric Paris   fsnotify: correct...
274
275
  	ret = 0;
  out:
75c1be487   Eric Paris   fsnotify: srcu to...
276
  	srcu_read_unlock(&fsnotify_mark_srcu, idx);
90586523e   Eric Paris   fsnotify: unified...
277
278
279
280
281
282
  	/*
  	 * fsnotify_create_event() took a reference so the event can't be cleaned
  	 * up while we are still trying to add it to lists, drop that one.
  	 */
  	if (event)
  		fsnotify_put_event(event);
c4ec54b40   Eric Paris   fsnotify: new fsn...
283

98b5c10d3   Jean-Christophe Dubois   fanotify: do not ...
284
  	return ret;
90586523e   Eric Paris   fsnotify: unified...
285
286
287
288
289
  }
  EXPORT_SYMBOL_GPL(fsnotify);
  
  static __init int fsnotify_init(void)
  {
75c1be487   Eric Paris   fsnotify: srcu to...
290
  	int ret;
20dee624c   Eric Paris   fsnotify: check t...
291
  	BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23);
75c1be487   Eric Paris   fsnotify: srcu to...
292
293
294
295
296
  	ret = init_srcu_struct(&fsnotify_mark_srcu);
  	if (ret)
  		panic("initializing fsnotify_mark_srcu");
  
  	return 0;
90586523e   Eric Paris   fsnotify: unified...
297
  }
75c1be487   Eric Paris   fsnotify: srcu to...
298
  core_initcall(fsnotify_init);