Blame view

fs/notify/fsnotify.c 16.8 KB
c82ee6d3b   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
90586523e   Eric Paris   fsnotify: unified...
2
3
  /*
   *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
90586523e   Eric Paris   fsnotify: unified...
4
5
6
7
   */
  
  #include <linux/dcache.h>
  #include <linux/fs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
8
  #include <linux/gfp.h>
90586523e   Eric Paris   fsnotify: unified...
9
10
  #include <linux/init.h>
  #include <linux/module.h>
7131485a9   Eric Paris   fsnotify: mount p...
11
  #include <linux/mount.h>
90586523e   Eric Paris   fsnotify: unified...
12
13
14
15
16
17
  #include <linux/srcu.h>
  
  #include <linux/fsnotify_backend.h>
  #include "fsnotify.h"
  
  /*
3be25f49b   Eric Paris   fsnotify: add mar...
18
19
20
21
22
23
24
   * 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...
25
26
27
28
  void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
  {
  	fsnotify_clear_marks_by_mount(mnt);
  }
ebb3b47e3   Jan Kara   fsnotify: Drop in...
29
30
31
32
33
34
35
  /**
   * fsnotify_unmount_inodes - an sb is unmounting.  handle any watched inodes.
   * @sb: superblock being unmounted.
   *
   * Called during unmount with no locks held, so needs to be safe against
   * concurrent modifiers. We temporarily drop sb->s_inode_list_lock and CAN block.
   */
1e6cb7239   Amir Goldstein   fsnotify: add sup...
36
  static void fsnotify_unmount_inodes(struct super_block *sb)
ebb3b47e3   Jan Kara   fsnotify: Drop in...
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  {
  	struct inode *inode, *iput_inode = NULL;
  
  	spin_lock(&sb->s_inode_list_lock);
  	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
  		/*
  		 * We cannot __iget() an inode in state I_FREEING,
  		 * I_WILL_FREE, or I_NEW which is fine because by that point
  		 * the inode cannot have any associated watches.
  		 */
  		spin_lock(&inode->i_lock);
  		if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) {
  			spin_unlock(&inode->i_lock);
  			continue;
  		}
  
  		/*
  		 * If i_count is zero, the inode cannot have any watches and
1751e8a6c   Linus Torvalds   Rename superblock...
55
  		 * doing an __iget/iput with SB_ACTIVE clear would actually
ebb3b47e3   Jan Kara   fsnotify: Drop in...
56
57
  		 * evict all inodes with zero i_count from icache which is
  		 * unnecessarily violent and may in fact be illegal to do.
1edc8eb2e   Eric Sandeen   fs: call fsnotify...
58
59
60
  		 * However, we should have been called /after/ evict_inodes
  		 * removed all zero refcount inodes, in any case.  Test to
  		 * be sure.
ebb3b47e3   Jan Kara   fsnotify: Drop in...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  		 */
  		if (!atomic_read(&inode->i_count)) {
  			spin_unlock(&inode->i_lock);
  			continue;
  		}
  
  		__iget(inode);
  		spin_unlock(&inode->i_lock);
  		spin_unlock(&sb->s_inode_list_lock);
  
  		if (iput_inode)
  			iput(iput_inode);
  
  		/* for each watch, send FS_UNMOUNT and then remove it */
82ace1efb   Amir Goldstein   fsnotify: create ...
75
  		fsnotify_inode(inode, FS_UNMOUNT);
ebb3b47e3   Jan Kara   fsnotify: Drop in...
76
77
78
79
  
  		fsnotify_inode_delete(inode);
  
  		iput_inode = inode;
04646aebd   Eric Sandeen   fs: avoid softloc...
80
  		cond_resched();
ebb3b47e3   Jan Kara   fsnotify: Drop in...
81
82
83
84
85
86
  		spin_lock(&sb->s_inode_list_lock);
  	}
  	spin_unlock(&sb->s_inode_list_lock);
  
  	if (iput_inode)
  		iput(iput_inode);
721fb6fbf   Jan Kara   fsnotify: Fix bus...
87
88
89
  	/* Wait for outstanding inode references from connectors */
  	wait_var_event(&sb->s_fsnotify_inode_refs,
  		       !atomic_long_read(&sb->s_fsnotify_inode_refs));
ebb3b47e3   Jan Kara   fsnotify: Drop in...
90
  }
1e6cb7239   Amir Goldstein   fsnotify: add sup...
91
92
93
94
95
  void fsnotify_sb_delete(struct super_block *sb)
  {
  	fsnotify_unmount_inodes(sb);
  	fsnotify_clear_marks_by_sb(sb);
  }
3be25f49b   Eric Paris   fsnotify: add mar...
96
  /*
c28f7e56e   Eric Paris   fsnotify: parent ...
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
   * 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...
113
  	spin_lock(&inode->i_lock);
c28f7e56e   Eric Paris   fsnotify: parent ...
114
115
  	/* 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 */
946e51f2b   Al Viro   move d_rcu from o...
116
  	hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
c28f7e56e   Eric Paris   fsnotify: parent ...
117
118
119
120
121
  		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 ...
122
  		spin_lock(&alias->d_lock);
946e51f2b   Al Viro   move d_rcu from o...
123
  		list_for_each_entry(child, &alias->d_subdirs, d_child) {
c28f7e56e   Eric Paris   fsnotify: parent ...
124
125
  			if (!child->d_inode)
  				continue;
2fd6b7f50   Nick Piggin   fs: dcache scale ...
126
  			spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
c28f7e56e   Eric Paris   fsnotify: parent ...
127
128
129
130
131
132
  			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 ...
133
  		spin_unlock(&alias->d_lock);
c28f7e56e   Eric Paris   fsnotify: parent ...
134
  	}
873feea09   Nick Piggin   fs: dcache per-in...
135
  	spin_unlock(&inode->i_lock);
c28f7e56e   Eric Paris   fsnotify: parent ...
136
  }
9b93f3310   Amir Goldstein   fsnotify: send ev...
137
138
139
140
141
142
143
144
145
  /* Are inode/sb/mount interested in parent and name info with this event? */
  static bool fsnotify_event_needs_parent(struct inode *inode, struct mount *mnt,
  					__u32 mask)
  {
  	__u32 marks_mask = 0;
  
  	/* We only send parent/name to inode/sb/mount for events on non-dir */
  	if (mask & FS_ISDIR)
  		return false;
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
146
147
148
149
150
151
  	/*
  	 * All events that are possible on child can also may be reported with
  	 * parent/name info to inode/sb/mount.  Otherwise, a watching parent
  	 * could result in events reported with unexpected name info to sb/mount.
  	 */
  	BUILD_BUG_ON(FS_EVENTS_POSS_ON_CHILD & ~FS_EVENTS_POSS_TO_PARENT);
9b93f3310   Amir Goldstein   fsnotify: send ev...
152
153
154
155
156
157
158
159
160
  	/* Did either inode/sb/mount subscribe for events with parent/name? */
  	marks_mask |= fsnotify_parent_needed_mask(inode->i_fsnotify_mask);
  	marks_mask |= fsnotify_parent_needed_mask(inode->i_sb->s_fsnotify_mask);
  	if (mnt)
  		marks_mask |= fsnotify_parent_needed_mask(mnt->mnt_fsnotify_mask);
  
  	/* Did they subscribe for this event with parent/name info? */
  	return mask & marks_mask;
  }
c738fbabb   Amir Goldstein   fsnotify: fold fs...
161
162
  /*
   * Notify this dentry's parent about a child's events with child name info
9b93f3310   Amir Goldstein   fsnotify: send ev...
163
164
165
166
167
   * if parent is watching or if inode/sb/mount are interested in events with
   * parent and name info.
   *
   * Notify only the child without name info if parent is not watching and
   * inode/sb/mount are not interested in events with parent and name info.
c738fbabb   Amir Goldstein   fsnotify: fold fs...
168
   */
71d734103   Mel Gorman   fsnotify: Rearran...
169
  int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
c738fbabb   Amir Goldstein   fsnotify: fold fs...
170
  		      int data_type)
c28f7e56e   Eric Paris   fsnotify: parent ...
171
  {
9b93f3310   Amir Goldstein   fsnotify: send ev...
172
173
  	const struct path *path = fsnotify_data_path(data, data_type);
  	struct mount *mnt = path ? real_mount(path->mnt) : NULL;
497b0c5a7   Amir Goldstein   fsnotify: send ev...
174
  	struct inode *inode = d_inode(dentry);
c28f7e56e   Eric Paris   fsnotify: parent ...
175
  	struct dentry *parent;
9b93f3310   Amir Goldstein   fsnotify: send ev...
176
  	bool parent_watched = dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED;
7372e79c9   Amir Goldstein   fanotify: fix log...
177
  	bool parent_needed, parent_interested;
9b93f3310   Amir Goldstein   fsnotify: send ev...
178
  	__u32 p_mask;
40a100d3a   Amir Goldstein   fsnotify: pass di...
179
  	struct inode *p_inode = NULL;
497b0c5a7   Amir Goldstein   fsnotify: send ev...
180
181
  	struct name_snapshot name;
  	struct qstr *file_name = NULL;
52420392c   Eric Paris   fsnotify: call fs...
182
  	int ret = 0;
c28f7e56e   Eric Paris   fsnotify: parent ...
183

9b93f3310   Amir Goldstein   fsnotify: send ev...
184
185
186
187
188
189
190
  	/*
  	 * Do inode/sb/mount care about parent and name info on non-dir?
  	 * Do they care about any event at all?
  	 */
  	if (!inode->i_fsnotify_marks && !inode->i_sb->s_fsnotify_marks &&
  	    (!mnt || !mnt->mnt_fsnotify_marks) && !parent_watched)
  		return 0;
497b0c5a7   Amir Goldstein   fsnotify: send ev...
191
  	parent = NULL;
7372e79c9   Amir Goldstein   fanotify: fix log...
192
193
  	parent_needed = fsnotify_event_needs_parent(inode, mnt, mask);
  	if (!parent_watched && !parent_needed)
497b0c5a7   Amir Goldstein   fsnotify: send ev...
194
  		goto notify;
c28f7e56e   Eric Paris   fsnotify: parent ...
195

9b93f3310   Amir Goldstein   fsnotify: send ev...
196
  	/* Does parent inode care about events on children? */
4d4eb3667   Christoph Hellwig   fsnotify: use dge...
197
  	parent = dget_parent(dentry);
c28f7e56e   Eric Paris   fsnotify: parent ...
198
  	p_inode = parent->d_inode;
9b93f3310   Amir Goldstein   fsnotify: send ev...
199
200
  	p_mask = fsnotify_inode_watches_children(p_inode);
  	if (unlikely(parent_watched && !p_mask))
4d4eb3667   Christoph Hellwig   fsnotify: use dge...
201
  		__fsnotify_update_child_dentry_flags(p_inode);
9b93f3310   Amir Goldstein   fsnotify: send ev...
202
203
204
  
  	/*
  	 * Include parent/name in notification either if some notification
7372e79c9   Amir Goldstein   fanotify: fix log...
205
  	 * groups require parent info or the parent is interested in this event.
9b93f3310   Amir Goldstein   fsnotify: send ev...
206
  	 */
7372e79c9   Amir Goldstein   fanotify: fix log...
207
208
  	parent_interested = mask & p_mask & ALL_FSNOTIFY_EVENTS;
  	if (parent_needed || parent_interested) {
497b0c5a7   Amir Goldstein   fsnotify: send ev...
209
210
  		/* When notifying parent, child should be passed as data */
  		WARN_ON_ONCE(inode != fsnotify_data_inode(data, data_type));
49d31c2f3   Al Viro   dentry name snaps...
211

497b0c5a7   Amir Goldstein   fsnotify: send ev...
212
  		/* Notify both parent and child with child name info */
49d31c2f3   Al Viro   dentry name snaps...
213
  		take_dentry_name_snapshot(&name, dentry);
497b0c5a7   Amir Goldstein   fsnotify: send ev...
214
  		file_name = &name.name;
7372e79c9   Amir Goldstein   fanotify: fix log...
215
  		if (parent_interested)
9b93f3310   Amir Goldstein   fsnotify: send ev...
216
  			mask |= FS_EVENT_ON_CHILD;
c28f7e56e   Eric Paris   fsnotify: parent ...
217
  	}
497b0c5a7   Amir Goldstein   fsnotify: send ev...
218
  notify:
40a100d3a   Amir Goldstein   fsnotify: pass di...
219
  	ret = fsnotify(mask, data, data_type, p_inode, file_name, inode, 0);
52420392c   Eric Paris   fsnotify: call fs...
220

497b0c5a7   Amir Goldstein   fsnotify: send ev...
221
222
223
  	if (file_name)
  		release_dentry_name_snapshot(&name);
  	dput(parent);
c738fbabb   Amir Goldstein   fsnotify: fold fs...
224

497b0c5a7   Amir Goldstein   fsnotify: send ev...
225
  	return ret;
c28f7e56e   Eric Paris   fsnotify: parent ...
226
  }
71d734103   Mel Gorman   fsnotify: Rearran...
227
  EXPORT_SYMBOL_GPL(__fsnotify_parent);
c28f7e56e   Eric Paris   fsnotify: parent ...
228

c9be99c86   Amir Goldstein   fsnotify: general...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  static int fsnotify_handle_inode_event(struct fsnotify_group *group,
  				       struct fsnotify_mark *inode_mark,
  				       u32 mask, const void *data, int data_type,
  				       struct inode *dir, const struct qstr *name,
  				       u32 cookie)
  {
  	const struct path *path = fsnotify_data_path(data, data_type);
  	struct inode *inode = fsnotify_data_inode(data, data_type);
  	const struct fsnotify_ops *ops = group->ops;
  
  	if (WARN_ON_ONCE(!ops->handle_inode_event))
  		return 0;
  
  	if ((inode_mark->mask & FS_EXCL_UNLINK) &&
  	    path && d_unlinked(path->dentry))
  		return 0;
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
245
246
247
  	/* Check interest of this mark in case event was sent with two marks */
  	if (!(mask & inode_mark->mask & ALL_FSNOTIFY_EVENTS))
  		return 0;
c9be99c86   Amir Goldstein   fsnotify: general...
248
249
  	return ops->handle_inode_event(inode_mark, mask, inode, dir, name, cookie);
  }
b9a1b9772   Amir Goldstein   fsnotify: create ...
250
251
252
253
254
255
  static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask,
  				 const void *data, int data_type,
  				 struct inode *dir, const struct qstr *name,
  				 u32 cookie, struct fsnotify_iter_info *iter_info)
  {
  	struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
256
  	struct fsnotify_mark *parent_mark = fsnotify_iter_parent_mark(iter_info);
b9a1b9772   Amir Goldstein   fsnotify: create ...
257
  	int ret;
b9a1b9772   Amir Goldstein   fsnotify: create ...
258
259
260
  	if (WARN_ON_ONCE(fsnotify_iter_sb_mark(iter_info)) ||
  	    WARN_ON_ONCE(fsnotify_iter_vfsmount_mark(iter_info)))
  		return 0;
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  	if (parent_mark) {
  		/*
  		 * parent_mark indicates that the parent inode is watching
  		 * children and interested in this event, which is an event
  		 * possible on child. But is *this mark* watching children and
  		 * interested in this event?
  		 */
  		if (parent_mark->mask & FS_EVENT_ON_CHILD) {
  			ret = fsnotify_handle_inode_event(group, parent_mark, mask,
  							  data, data_type, dir, name, 0);
  			if (ret)
  				return ret;
  		}
  		if (!inode_mark)
  			return 0;
  	}
  
  	if (mask & FS_EVENT_ON_CHILD) {
  		/*
  		 * Some events can be sent on both parent dir and child marks
  		 * (e.g. FS_ATTRIB).  If both parent dir and child are
  		 * watching, report the event once to parent dir with name (if
  		 * interested) and once to child without name (if interested).
  		 * The child watcher is expecting an event without a file name
  		 * and without the FS_EVENT_ON_CHILD flag.
  		 */
  		mask &= ~FS_EVENT_ON_CHILD;
b9a1b9772   Amir Goldstein   fsnotify: create ...
288
289
290
  		dir = NULL;
  		name = NULL;
  	}
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
291
292
  	return fsnotify_handle_inode_event(group, inode_mark, mask, data, data_type,
  					   dir, name, cookie);
b9a1b9772   Amir Goldstein   fsnotify: create ...
293
  }
b54cecf5e   Amir Goldstein   fsnotify: pass di...
294
295
296
  static int send_to_group(__u32 mask, const void *data, int data_type,
  			 struct inode *dir, const struct qstr *file_name,
  			 u32 cookie, struct fsnotify_iter_info *iter_info)
7131485a9   Eric Paris   fsnotify: mount p...
297
  {
faa9560ae   Eric Paris   fanotify: do not ...
298
  	struct fsnotify_group *group = NULL;
007d1e839   Amir Goldstein   fsnotify: general...
299
  	__u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS);
92183a428   Amir Goldstein   fsnotify: fix ign...
300
301
  	__u32 marks_mask = 0;
  	__u32 marks_ignored_mask = 0;
3dca1a749   Amir Goldstein   fsnotify: general...
302
303
  	struct fsnotify_mark *mark;
  	int type;
613a807fe   Eric Paris   fsnotify: walk th...
304

5b0457ad0   Amir Goldstein   fsnotify: remove ...
305
  	if (WARN_ON(!iter_info->report_mask))
faa9560ae   Eric Paris   fanotify: do not ...
306
  		return 0;
ce8f76fb7   Eric Paris   fsnotify: pass bo...
307
308
309
  
  	/* clear ignored on inode modification */
  	if (mask & FS_MODIFY) {
3dca1a749   Amir Goldstein   fsnotify: general...
310
311
312
313
314
315
316
317
  		fsnotify_foreach_obj_type(type) {
  			if (!fsnotify_iter_should_report_type(iter_info, type))
  				continue;
  			mark = iter_info->marks[type];
  			if (mark &&
  			    !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
  				mark->ignored_mask = 0;
  		}
ce8f76fb7   Eric Paris   fsnotify: pass bo...
318
  	}
613a807fe   Eric Paris   fsnotify: walk th...
319

3dca1a749   Amir Goldstein   fsnotify: general...
320
321
322
323
324
325
326
327
328
329
  	fsnotify_foreach_obj_type(type) {
  		if (!fsnotify_iter_should_report_type(iter_info, type))
  			continue;
  		mark = iter_info->marks[type];
  		/* does the object mark tell us to do something? */
  		if (mark) {
  			group = mark->group;
  			marks_mask |= mark->mask;
  			marks_ignored_mask |= mark->ignored_mask;
  		}
ce8f76fb7   Eric Paris   fsnotify: pass bo...
330
  	}
b54cecf5e   Amir Goldstein   fsnotify: pass di...
331
332
333
334
  	pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignored_mask=%x data=%p data_type=%d dir=%p cookie=%d
  ",
  		 __func__, group, mask, marks_mask, marks_ignored_mask,
  		 data, data_type, dir, cookie);
faa9560ae   Eric Paris   fanotify: do not ...
335

92183a428   Amir Goldstein   fsnotify: fix ign...
336
  	if (!(test_mask & marks_mask & ~marks_ignored_mask))
613a807fe   Eric Paris   fsnotify: walk th...
337
  		return 0;
b9a1b9772   Amir Goldstein   fsnotify: create ...
338
339
340
341
342
343
344
  	if (group->ops->handle_event) {
  		return group->ops->handle_event(group, mask, data, data_type, dir,
  						file_name, cookie, iter_info);
  	}
  
  	return fsnotify_handle_event(group, mask, data, data_type, dir,
  				     file_name, cookie, iter_info);
7131485a9   Eric Paris   fsnotify: mount p...
345
  }
3427ce715   Miklos Szeredi   fsnotify: clean u...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
  static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector **connp)
  {
  	struct fsnotify_mark_connector *conn;
  	struct hlist_node *node = NULL;
  
  	conn = srcu_dereference(*connp, &fsnotify_mark_srcu);
  	if (conn)
  		node = srcu_dereference(conn->list.first, &fsnotify_mark_srcu);
  
  	return hlist_entry_safe(node, struct fsnotify_mark, obj_list);
  }
  
  static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark)
  {
  	struct hlist_node *node = NULL;
  
  	if (mark)
  		node = srcu_dereference(mark->obj_list.next,
  					&fsnotify_mark_srcu);
  
  	return hlist_entry_safe(node, struct fsnotify_mark, obj_list);
  }
c28f7e56e   Eric Paris   fsnotify: parent ...
368
  /*
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
369
370
371
372
373
374
375
376
   * iter_info is a multi head priority queue of marks.
   * Pick a subset of marks from queue heads, all with the
   * same group and set the report_mask for selected subset.
   * Returns the report_mask of the selected subset.
   */
  static unsigned int fsnotify_iter_select_report_types(
  		struct fsnotify_iter_info *iter_info)
  {
47d9c7cc4   Amir Goldstein   fsnotify: general...
377
378
379
380
381
382
383
384
385
386
387
  	struct fsnotify_group *max_prio_group = NULL;
  	struct fsnotify_mark *mark;
  	int type;
  
  	/* Choose max prio group among groups of all queue heads */
  	fsnotify_foreach_obj_type(type) {
  		mark = iter_info->marks[type];
  		if (mark &&
  		    fsnotify_compare_groups(max_prio_group, mark->group) > 0)
  			max_prio_group = mark->group;
  	}
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
388

47d9c7cc4   Amir Goldstein   fsnotify: general...
389
  	if (!max_prio_group)
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
390
  		return 0;
47d9c7cc4   Amir Goldstein   fsnotify: general...
391
  	/* Set the report mask for marks from same group as max prio group */
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
392
  	iter_info->report_mask = 0;
47d9c7cc4   Amir Goldstein   fsnotify: general...
393
394
395
396
397
398
  	fsnotify_foreach_obj_type(type) {
  		mark = iter_info->marks[type];
  		if (mark &&
  		    fsnotify_compare_groups(max_prio_group, mark->group) == 0)
  			fsnotify_iter_set_report_type(iter_info, type);
  	}
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
399
400
401
402
403
404
405
406
407
408
  
  	return iter_info->report_mask;
  }
  
  /*
   * Pop from iter_info multi head queue, the marks that were iterated in the
   * current iteration step.
   */
  static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
  {
47d9c7cc4   Amir Goldstein   fsnotify: general...
409
  	int type;
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
410

47d9c7cc4   Amir Goldstein   fsnotify: general...
411
412
413
414
415
  	fsnotify_foreach_obj_type(type) {
  		if (fsnotify_iter_should_report_type(iter_info, type))
  			iter_info->marks[type] =
  				fsnotify_next_mark(iter_info->marks[type]);
  	}
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
416
417
418
  }
  
  /*
40a100d3a   Amir Goldstein   fsnotify: pass di...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
   * fsnotify - 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.
   *
   * @mask:	event type and flags
   * @data:	object that event happened on
   * @data_type:	type of object for fanotify_data_XXX() accessors
   * @dir:	optional directory associated with event -
   *		if @file_name is not NULL, this is the directory that
   *		@file_name is relative to
   * @file_name:	optional file name associated with event
   * @inode:	optional inode associated with event -
   *		either @dir or @inode must be non-NULL.
   *		if both are non-NULL event may be reported to both.
   * @cookie:	inotify rename cookie
90586523e   Eric Paris   fsnotify: unified...
437
   */
40a100d3a   Amir Goldstein   fsnotify: pass di...
438
439
  int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
  	     const struct qstr *file_name, struct inode *inode, u32 cookie)
90586523e   Eric Paris   fsnotify: unified...
440
  {
b54cecf5e   Amir Goldstein   fsnotify: pass di...
441
  	const struct path *path = fsnotify_data_path(data, data_type);
3427ce715   Miklos Szeredi   fsnotify: clean u...
442
  	struct fsnotify_iter_info iter_info = {};
40a100d3a   Amir Goldstein   fsnotify: pass di...
443
  	struct super_block *sb;
60f7ed8c7   Amir Goldstein   fsnotify: send pa...
444
  	struct mount *mnt = NULL;
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
445
  	struct inode *parent = NULL;
9385a84d7   Jan Kara   fsnotify: Pass fs...
446
  	int ret = 0;
71d734103   Mel Gorman   fsnotify: Rearran...
447
  	__u32 test_mask, marks_mask;
90586523e   Eric Paris   fsnotify: unified...
448

71d734103   Mel Gorman   fsnotify: Rearran...
449
  	if (path)
aa93bdc55   Amir Goldstein   fsnotify: use hel...
450
  		mnt = real_mount(path->mnt);
613a807fe   Eric Paris   fsnotify: walk th...
451

40a100d3a   Amir Goldstein   fsnotify: pass di...
452
453
454
455
456
  	if (!inode) {
  		/* Dirent event - report on TYPE_INODE to dir */
  		inode = dir;
  	} else if (mask & FS_EVENT_ON_CHILD) {
  		/*
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
457
458
  		 * Event on child - report on TYPE_PARENT to dir if it is
  		 * watching children and on TYPE_INODE to child.
40a100d3a   Amir Goldstein   fsnotify: pass di...
459
  		 */
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
460
  		parent = dir;
40a100d3a   Amir Goldstein   fsnotify: pass di...
461
462
  	}
  	sb = inode->i_sb;
497b0c5a7   Amir Goldstein   fsnotify: send ev...
463

613a807fe   Eric Paris   fsnotify: walk th...
464
  	/*
7c49b8616   Dave Hansen   fs/notify: optimi...
465
466
467
468
469
470
  	 * Optimization: srcu_read_lock() has a memory barrier which can
  	 * be expensive.  It protects walking the *_fsnotify_marks lists.
  	 * However, if we do not walk the lists, we do not have to do
  	 * SRCU because we have no references to any objects and do not
  	 * need SRCU to keep them "alive".
  	 */
9b93f3310   Amir Goldstein   fsnotify: send ev...
471
  	if (!sb->s_fsnotify_marks &&
497b0c5a7   Amir Goldstein   fsnotify: send ev...
472
  	    (!mnt || !mnt->mnt_fsnotify_marks) &&
9b93f3310   Amir Goldstein   fsnotify: send ev...
473
  	    (!inode || !inode->i_fsnotify_marks) &&
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
474
  	    (!parent || !parent->i_fsnotify_marks))
7c49b8616   Dave Hansen   fs/notify: optimi...
475
  		return 0;
71d734103   Mel Gorman   fsnotify: Rearran...
476

9b93f3310   Amir Goldstein   fsnotify: send ev...
477
  	marks_mask = sb->s_fsnotify_mask;
497b0c5a7   Amir Goldstein   fsnotify: send ev...
478
479
  	if (mnt)
  		marks_mask |= mnt->mnt_fsnotify_mask;
9b93f3310   Amir Goldstein   fsnotify: send ev...
480
481
  	if (inode)
  		marks_mask |= inode->i_fsnotify_mask;
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
482
483
  	if (parent)
  		marks_mask |= parent->i_fsnotify_mask;
497b0c5a7   Amir Goldstein   fsnotify: send ev...
484

71d734103   Mel Gorman   fsnotify: Rearran...
485

7c49b8616   Dave Hansen   fs/notify: optimi...
486
  	/*
613a807fe   Eric Paris   fsnotify: walk th...
487
  	 * if this is a modify event we may need to clear the ignored masks
497b0c5a7   Amir Goldstein   fsnotify: send ev...
488
  	 * otherwise return if none of the marks care about this type of event.
613a807fe   Eric Paris   fsnotify: walk th...
489
  	 */
71d734103   Mel Gorman   fsnotify: Rearran...
490
491
  	test_mask = (mask & ALL_FSNOTIFY_EVENTS);
  	if (!(mask & FS_MODIFY) && !(test_mask & marks_mask))
613a807fe   Eric Paris   fsnotify: walk th...
492
  		return 0;
3a9fb89f4   Eric Paris   fsnotify: include...
493

9385a84d7   Jan Kara   fsnotify: Pass fs...
494
  	iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
7131485a9   Eric Paris   fsnotify: mount p...
495

45a9fb372   Amir Goldstein   fsnotify: send al...
496
497
  	iter_info.marks[FSNOTIFY_OBJ_TYPE_SB] =
  		fsnotify_first_mark(&sb->s_fsnotify_marks);
9bdda4e9c   Amir Goldstein   fsnotify: fix ign...
498
  	if (mnt) {
47d9c7cc4   Amir Goldstein   fsnotify: general...
499
  		iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] =
3427ce715   Miklos Szeredi   fsnotify: clean u...
500
  			fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
90586523e   Eric Paris   fsnotify: unified...
501
  	}
9b93f3310   Amir Goldstein   fsnotify: send ev...
502
503
504
505
  	if (inode) {
  		iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
  			fsnotify_first_mark(&inode->i_fsnotify_marks);
  	}
41bf5eed8   Amir Goldstein   fsnotify: fix eve...
506
507
508
  	if (parent) {
  		iter_info.marks[FSNOTIFY_OBJ_TYPE_PARENT] =
  			fsnotify_first_mark(&parent->i_fsnotify_marks);
497b0c5a7   Amir Goldstein   fsnotify: send ev...
509
  	}
75c1be487   Eric Paris   fsnotify: srcu to...
510

8edc6e168   Jan Kara   fanotify: fix not...
511
  	/*
60f7ed8c7   Amir Goldstein   fsnotify: send pa...
512
513
  	 * We need to merge inode/vfsmount/sb mark lists so that e.g. inode mark
  	 * ignore masks are properly reflected for mount/sb mark notifications.
8edc6e168   Jan Kara   fanotify: fix not...
514
515
  	 * That's why this traversal is so complicated...
  	 */
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
516
  	while (fsnotify_iter_select_report_types(&iter_info)) {
b54cecf5e   Amir Goldstein   fsnotify: pass di...
517
518
  		ret = send_to_group(mask, data, data_type, dir, file_name,
  				    cookie, &iter_info);
613a807fe   Eric Paris   fsnotify: walk th...
519

ff8bcbd03   Eric Paris   fsnotify: correct...
520
521
  		if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
  			goto out;
d9a6f30bb   Amir Goldstein   fsnotify: introdu...
522
  		fsnotify_iter_next(&iter_info);
7131485a9   Eric Paris   fsnotify: mount p...
523
  	}
ff8bcbd03   Eric Paris   fsnotify: correct...
524
525
  	ret = 0;
  out:
9385a84d7   Jan Kara   fsnotify: Pass fs...
526
  	srcu_read_unlock(&fsnotify_mark_srcu, iter_info.srcu_idx);
c4ec54b40   Eric Paris   fsnotify: new fsn...
527

98b5c10d3   Jean-Christophe Dubois   fanotify: do not ...
528
  	return ret;
90586523e   Eric Paris   fsnotify: unified...
529
530
531
532
533
  }
  EXPORT_SYMBOL_GPL(fsnotify);
  
  static __init int fsnotify_init(void)
  {
75c1be487   Eric Paris   fsnotify: srcu to...
534
  	int ret;
08b95c338   Amir Goldstein   fanotify: remove ...
535
  	BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 25);
20dee624c   Eric Paris   fsnotify: check t...
536

75c1be487   Eric Paris   fsnotify: srcu to...
537
538
539
  	ret = init_srcu_struct(&fsnotify_mark_srcu);
  	if (ret)
  		panic("initializing fsnotify_mark_srcu");
9dd813c15   Jan Kara   fsnotify: Move ma...
540
541
  	fsnotify_mark_connector_cachep = KMEM_CACHE(fsnotify_mark_connector,
  						    SLAB_PANIC);
75c1be487   Eric Paris   fsnotify: srcu to...
542
  	return 0;
90586523e   Eric Paris   fsnotify: unified...
543
  }
75c1be487   Eric Paris   fsnotify: srcu to...
544
  core_initcall(fsnotify_init);