xref: /openbmc/linux/fs/notify/fsnotify.c (revision 873feea0)
190586523SEric Paris /*
290586523SEric Paris  *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
390586523SEric Paris  *
490586523SEric Paris  *  This program is free software; you can redistribute it and/or modify
590586523SEric Paris  *  it under the terms of the GNU General Public License as published by
690586523SEric Paris  *  the Free Software Foundation; either version 2, or (at your option)
790586523SEric Paris  *  any later version.
890586523SEric Paris  *
990586523SEric Paris  *  This program is distributed in the hope that it will be useful,
1090586523SEric Paris  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1190586523SEric Paris  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1290586523SEric Paris  *  GNU General Public License for more details.
1390586523SEric Paris  *
1490586523SEric Paris  *  You should have received a copy of the GNU General Public License
1590586523SEric Paris  *  along with this program; see the file COPYING.  If not, write to
1690586523SEric Paris  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
1790586523SEric Paris  */
1890586523SEric Paris 
1990586523SEric Paris #include <linux/dcache.h>
2090586523SEric Paris #include <linux/fs.h>
215a0e3ad6STejun Heo #include <linux/gfp.h>
2290586523SEric Paris #include <linux/init.h>
2390586523SEric Paris #include <linux/module.h>
247131485aSEric Paris #include <linux/mount.h>
2590586523SEric Paris #include <linux/srcu.h>
2690586523SEric Paris 
2790586523SEric Paris #include <linux/fsnotify_backend.h>
2890586523SEric Paris #include "fsnotify.h"
2990586523SEric Paris 
3090586523SEric Paris /*
313be25f49SEric Paris  * Clear all of the marks on an inode when it is being evicted from core
323be25f49SEric Paris  */
333be25f49SEric Paris void __fsnotify_inode_delete(struct inode *inode)
343be25f49SEric Paris {
353be25f49SEric Paris 	fsnotify_clear_marks_by_inode(inode);
363be25f49SEric Paris }
373be25f49SEric Paris EXPORT_SYMBOL_GPL(__fsnotify_inode_delete);
383be25f49SEric Paris 
39ca9c726eSAndreas Gruenbacher void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
40ca9c726eSAndreas Gruenbacher {
41ca9c726eSAndreas Gruenbacher 	fsnotify_clear_marks_by_mount(mnt);
42ca9c726eSAndreas Gruenbacher }
43ca9c726eSAndreas Gruenbacher 
443be25f49SEric Paris /*
45c28f7e56SEric Paris  * Given an inode, first check if we care what happens to our children.  Inotify
46c28f7e56SEric Paris  * and dnotify both tell their parents about events.  If we care about any event
47c28f7e56SEric Paris  * on a child we run all of our children and set a dentry flag saying that the
48c28f7e56SEric Paris  * parent cares.  Thus when an event happens on a child it can quickly tell if
49c28f7e56SEric Paris  * if there is a need to find a parent and send the event to the parent.
50c28f7e56SEric Paris  */
51c28f7e56SEric Paris void __fsnotify_update_child_dentry_flags(struct inode *inode)
52c28f7e56SEric Paris {
53c28f7e56SEric Paris 	struct dentry *alias;
54c28f7e56SEric Paris 	int watched;
55c28f7e56SEric Paris 
56c28f7e56SEric Paris 	if (!S_ISDIR(inode->i_mode))
57c28f7e56SEric Paris 		return;
58c28f7e56SEric Paris 
59c28f7e56SEric Paris 	/* determine if the children should tell inode about their events */
60c28f7e56SEric Paris 	watched = fsnotify_inode_watches_children(inode);
61c28f7e56SEric Paris 
62873feea0SNick Piggin 	spin_lock(&inode->i_lock);
63c28f7e56SEric Paris 	/* run all of the dentries associated with this inode.  Since this is a
64c28f7e56SEric Paris 	 * directory, there damn well better only be one item on this list */
65c28f7e56SEric Paris 	list_for_each_entry(alias, &inode->i_dentry, d_alias) {
66c28f7e56SEric Paris 		struct dentry *child;
67c28f7e56SEric Paris 
68c28f7e56SEric Paris 		/* run all of the children of the original inode and fix their
69c28f7e56SEric Paris 		 * d_flags to indicate parental interest (their parent is the
70c28f7e56SEric Paris 		 * original inode) */
712fd6b7f5SNick Piggin 		spin_lock(&alias->d_lock);
72c28f7e56SEric Paris 		list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) {
73c28f7e56SEric Paris 			if (!child->d_inode)
74c28f7e56SEric Paris 				continue;
75c28f7e56SEric Paris 
762fd6b7f5SNick Piggin 			spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
77c28f7e56SEric Paris 			if (watched)
78c28f7e56SEric Paris 				child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
79c28f7e56SEric Paris 			else
80c28f7e56SEric Paris 				child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED;
81c28f7e56SEric Paris 			spin_unlock(&child->d_lock);
82c28f7e56SEric Paris 		}
832fd6b7f5SNick Piggin 		spin_unlock(&alias->d_lock);
84c28f7e56SEric Paris 	}
85873feea0SNick Piggin 	spin_unlock(&inode->i_lock);
86c28f7e56SEric Paris }
87c28f7e56SEric Paris 
88c28f7e56SEric Paris /* Notify this dentry's parent about a child's events. */
8952420392SEric Paris int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
90c28f7e56SEric Paris {
91c28f7e56SEric Paris 	struct dentry *parent;
92c28f7e56SEric Paris 	struct inode *p_inode;
9352420392SEric Paris 	int ret = 0;
94c28f7e56SEric Paris 
9572acc854SAndreas Gruenbacher 	if (!dentry)
962069601bSLinus Torvalds 		dentry = path->dentry;
9728c60e37SEric Paris 
98c28f7e56SEric Paris 	if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
9952420392SEric Paris 		return 0;
100c28f7e56SEric Paris 
1014d4eb366SChristoph Hellwig 	parent = dget_parent(dentry);
102c28f7e56SEric Paris 	p_inode = parent->d_inode;
103c28f7e56SEric Paris 
1044d4eb366SChristoph Hellwig 	if (unlikely(!fsnotify_inode_watches_children(p_inode)))
1054d4eb366SChristoph Hellwig 		__fsnotify_update_child_dentry_flags(p_inode);
1064d4eb366SChristoph Hellwig 	else if (p_inode->i_fsnotify_mask & mask) {
107c28f7e56SEric Paris 		/* we are notifying a parent so come up with the new mask which
108c28f7e56SEric Paris 		 * specifies these are events which came from a child. */
109c28f7e56SEric Paris 		mask |= FS_EVENT_ON_CHILD;
110c28f7e56SEric Paris 
1112069601bSLinus Torvalds 		if (path)
11252420392SEric Paris 			ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
11328c60e37SEric Paris 				       dentry->d_name.name, 0);
11428c60e37SEric Paris 		else
11552420392SEric Paris 			ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
11647882c6fSEric Paris 				       dentry->d_name.name, 0);
117c28f7e56SEric Paris 	}
118c28f7e56SEric Paris 
119c28f7e56SEric Paris 	dput(parent);
12052420392SEric Paris 
12152420392SEric Paris 	return ret;
122c28f7e56SEric Paris }
123c28f7e56SEric Paris EXPORT_SYMBOL_GPL(__fsnotify_parent);
124c28f7e56SEric Paris 
125613a807fSEric Paris static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
126ce8f76fbSEric Paris 			 struct fsnotify_mark *inode_mark,
127ce8f76fbSEric Paris 			 struct fsnotify_mark *vfsmount_mark,
128613a807fSEric Paris 			 __u32 mask, void *data,
129613a807fSEric Paris 			 int data_is, u32 cookie,
1303a9b16b4SEric Paris 			 const unsigned char *file_name,
1313a9fb89fSEric Paris 			 struct fsnotify_event **event)
1327131485aSEric Paris {
133faa9560aSEric Paris 	struct fsnotify_group *group = NULL;
13484e1ab4dSEric Paris 	__u32 inode_test_mask = 0;
13584e1ab4dSEric Paris 	__u32 vfsmount_test_mask = 0;
136613a807fSEric Paris 
137faa9560aSEric Paris 	if (unlikely(!inode_mark && !vfsmount_mark)) {
138faa9560aSEric Paris 		BUG();
139faa9560aSEric Paris 		return 0;
140faa9560aSEric Paris 	}
1415ba08e2eSEric Paris 
142ce8f76fbSEric Paris 	/* clear ignored on inode modification */
143ce8f76fbSEric Paris 	if (mask & FS_MODIFY) {
144ce8f76fbSEric Paris 		if (inode_mark &&
145ce8f76fbSEric Paris 		    !(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
146ce8f76fbSEric Paris 			inode_mark->ignored_mask = 0;
147ce8f76fbSEric Paris 		if (vfsmount_mark &&
148ce8f76fbSEric Paris 		    !(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
149ce8f76fbSEric Paris 			vfsmount_mark->ignored_mask = 0;
150ce8f76fbSEric Paris 	}
151613a807fSEric Paris 
152ce8f76fbSEric Paris 	/* does the inode mark tell us to do something? */
153ce8f76fbSEric Paris 	if (inode_mark) {
154faa9560aSEric Paris 		group = inode_mark->group;
15584e1ab4dSEric Paris 		inode_test_mask = (mask & ~FS_EVENT_ON_CHILD);
156ce8f76fbSEric Paris 		inode_test_mask &= inode_mark->mask;
157ce8f76fbSEric Paris 		inode_test_mask &= ~inode_mark->ignored_mask;
158ce8f76fbSEric Paris 	}
159ce8f76fbSEric Paris 
160ce8f76fbSEric Paris 	/* does the vfsmount_mark tell us to do something? */
161ce8f76fbSEric Paris 	if (vfsmount_mark) {
16284e1ab4dSEric Paris 		vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD);
163faa9560aSEric Paris 		group = vfsmount_mark->group;
164ce8f76fbSEric Paris 		vfsmount_test_mask &= vfsmount_mark->mask;
165ce8f76fbSEric Paris 		vfsmount_test_mask &= ~vfsmount_mark->ignored_mask;
166ce8f76fbSEric Paris 		if (inode_mark)
167ce8f76fbSEric Paris 			vfsmount_test_mask &= ~inode_mark->ignored_mask;
168ce8f76fbSEric Paris 	}
169ce8f76fbSEric Paris 
17084e1ab4dSEric Paris 	pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x inode_mark=%p"
17184e1ab4dSEric Paris 		 " inode_test_mask=%x vfsmount_mark=%p vfsmount_test_mask=%x"
17284e1ab4dSEric Paris 		 " data=%p data_is=%d cookie=%d event=%p\n",
17384e1ab4dSEric Paris 		 __func__, group, to_tell, mnt, mask, inode_mark,
17484e1ab4dSEric Paris 		 inode_test_mask, vfsmount_mark, vfsmount_test_mask, data,
17584e1ab4dSEric Paris 		 data_is, cookie, *event);
176faa9560aSEric Paris 
177ce8f76fbSEric Paris 	if (!inode_test_mask && !vfsmount_test_mask)
178613a807fSEric Paris 		return 0;
179613a807fSEric Paris 
1801968f5eeSEric Paris 	if (group->ops->should_send_event(group, to_tell, inode_mark,
181ce8f76fbSEric Paris 					  vfsmount_mark, mask, data,
182ce8f76fbSEric Paris 					  data_is) == false)
183c4ec54b4SEric Paris 		return 0;
184613a807fSEric Paris 
1857131485aSEric Paris 	if (!*event) {
1867131485aSEric Paris 		*event = fsnotify_create_event(to_tell, mask, data,
1877131485aSEric Paris 						data_is, file_name,
1887131485aSEric Paris 						cookie, GFP_KERNEL);
1897131485aSEric Paris 		if (!*event)
190c4ec54b4SEric Paris 			return -ENOMEM;
1917131485aSEric Paris 	}
192ce8f76fbSEric Paris 	return group->ops->handle_event(group, inode_mark, vfsmount_mark, *event);
1937131485aSEric Paris }
1947131485aSEric Paris 
195c28f7e56SEric Paris /*
19690586523SEric Paris  * This is the main call to fsnotify.  The VFS calls into hook specific functions
19790586523SEric Paris  * in linux/fsnotify.h.  Those functions then in turn call here.  Here will call
19890586523SEric Paris  * out to all of the registered fsnotify_group.  Those groups can then use the
19990586523SEric Paris  * notification event in whatever means they feel necessary.
20090586523SEric Paris  */
201c4ec54b4SEric Paris int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
20259b0df21SEric Paris 	     const unsigned char *file_name, u32 cookie)
20390586523SEric Paris {
20484e1ab4dSEric Paris 	struct hlist_node *inode_node = NULL, *vfsmount_node = NULL;
205613a807fSEric Paris 	struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL;
206613a807fSEric Paris 	struct fsnotify_group *inode_group, *vfsmount_group;
20790586523SEric Paris 	struct fsnotify_event *event = NULL;
208613a807fSEric Paris 	struct vfsmount *mnt;
209c4ec54b4SEric Paris 	int idx, ret = 0;
210e42e2773SEric Paris 	/* global tests shouldn't care about events on child only the specific event */
211e42e2773SEric Paris 	__u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
21290586523SEric Paris 
2132069601bSLinus Torvalds 	if (data_is == FSNOTIFY_EVENT_PATH)
2142069601bSLinus Torvalds 		mnt = ((struct path *)data)->mnt;
215613a807fSEric Paris 	else
216613a807fSEric Paris 		mnt = NULL;
217613a807fSEric Paris 
218613a807fSEric Paris 	/*
219613a807fSEric Paris 	 * if this is a modify event we may need to clear the ignored masks
220613a807fSEric Paris 	 * otherwise return if neither the inode nor the vfsmount care about
221613a807fSEric Paris 	 * this type of event.
222613a807fSEric Paris 	 */
223613a807fSEric Paris 	if (!(mask & FS_MODIFY) &&
224613a807fSEric Paris 	    !(test_mask & to_tell->i_fsnotify_mask) &&
225613a807fSEric Paris 	    !(mnt && test_mask & mnt->mnt_fsnotify_mask))
226613a807fSEric Paris 		return 0;
2273a9fb89fSEric Paris 
22875c1be48SEric Paris 	idx = srcu_read_lock(&fsnotify_mark_srcu);
2297131485aSEric Paris 
230613a807fSEric Paris 	if ((mask & FS_MODIFY) ||
231613a807fSEric Paris 	    (test_mask & to_tell->i_fsnotify_mask))
232ce8f76fbSEric Paris 		inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first,
233ce8f76fbSEric Paris 					      &fsnotify_mark_srcu);
23475c1be48SEric Paris 
23584e1ab4dSEric Paris 	if (mnt && ((mask & FS_MODIFY) ||
23684e1ab4dSEric Paris 		    (test_mask & mnt->mnt_fsnotify_mask))) {
237ce8f76fbSEric Paris 		vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first,
238ce8f76fbSEric Paris 						 &fsnotify_mark_srcu);
23984e1ab4dSEric Paris 		inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first,
24084e1ab4dSEric Paris 					      &fsnotify_mark_srcu);
2417131485aSEric Paris 	}
24275c1be48SEric Paris 
243613a807fSEric Paris 	while (inode_node || vfsmount_node) {
244f72adfd5SEric Paris 		inode_group = vfsmount_group = NULL;
2455f3f259fSEric Paris 
246613a807fSEric Paris 		if (inode_node) {
247613a807fSEric Paris 			inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
248613a807fSEric Paris 						 struct fsnotify_mark, i.i_list);
249613a807fSEric Paris 			inode_group = inode_mark->group;
250f72adfd5SEric Paris 		}
25175c1be48SEric Paris 
252613a807fSEric Paris 		if (vfsmount_node) {
253613a807fSEric Paris 			vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu),
254613a807fSEric Paris 							struct fsnotify_mark, m.m_list);
255613a807fSEric Paris 			vfsmount_group = vfsmount_mark->group;
256f72adfd5SEric Paris 		}
25775c1be48SEric Paris 
258f72adfd5SEric Paris 		if (inode_group > vfsmount_group) {
259613a807fSEric Paris 			/* handle inode */
260ff8bcbd0SEric Paris 			ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
261613a807fSEric Paris 					    data_is, cookie, file_name, &event);
26292b4678eSEric Paris 			/* we didn't use the vfsmount_mark */
26392b4678eSEric Paris 			vfsmount_group = NULL;
264f72adfd5SEric Paris 		} else if (vfsmount_group > inode_group) {
265ff8bcbd0SEric Paris 			ret = send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
266613a807fSEric Paris 					    data_is, cookie, file_name, &event);
26792b4678eSEric Paris 			inode_group = NULL;
268613a807fSEric Paris 		} else {
269ff8bcbd0SEric Paris 			ret = send_to_group(to_tell, mnt, inode_mark, vfsmount_mark,
270ce8f76fbSEric Paris 					    mask, data, data_is, cookie, file_name,
271ce8f76fbSEric Paris 					    &event);
272613a807fSEric Paris 		}
27384a5b68eSEric Paris 
274ff8bcbd0SEric Paris 		if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
275ff8bcbd0SEric Paris 			goto out;
276ff8bcbd0SEric Paris 
27792b4678eSEric Paris 		if (inode_group)
278ce8f76fbSEric Paris 			inode_node = srcu_dereference(inode_node->next,
279ce8f76fbSEric Paris 						      &fsnotify_mark_srcu);
28092b4678eSEric Paris 		if (vfsmount_group)
281ce8f76fbSEric Paris 			vfsmount_node = srcu_dereference(vfsmount_node->next,
282ce8f76fbSEric Paris 							 &fsnotify_mark_srcu);
2837131485aSEric Paris 	}
284ff8bcbd0SEric Paris 	ret = 0;
285ff8bcbd0SEric Paris out:
28675c1be48SEric Paris 	srcu_read_unlock(&fsnotify_mark_srcu, idx);
28790586523SEric Paris 	/*
28890586523SEric Paris 	 * fsnotify_create_event() took a reference so the event can't be cleaned
28990586523SEric Paris 	 * up while we are still trying to add it to lists, drop that one.
29090586523SEric Paris 	 */
29190586523SEric Paris 	if (event)
29290586523SEric Paris 		fsnotify_put_event(event);
293c4ec54b4SEric Paris 
29498b5c10dSJean-Christophe Dubois 	return ret;
29590586523SEric Paris }
29690586523SEric Paris EXPORT_SYMBOL_GPL(fsnotify);
29790586523SEric Paris 
29890586523SEric Paris static __init int fsnotify_init(void)
29990586523SEric Paris {
30075c1be48SEric Paris 	int ret;
30175c1be48SEric Paris 
30220dee624SEric Paris 	BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23);
30320dee624SEric Paris 
30475c1be48SEric Paris 	ret = init_srcu_struct(&fsnotify_mark_srcu);
30575c1be48SEric Paris 	if (ret)
30675c1be48SEric Paris 		panic("initializing fsnotify_mark_srcu");
30775c1be48SEric Paris 
30875c1be48SEric Paris 	return 0;
30990586523SEric Paris }
31075c1be48SEric Paris core_initcall(fsnotify_init);
311