blob: 1b61d0a942de56b0ce1d98de9e821589385219f0 [file] [log] [blame]
Eric Paris0d48b7f2009-12-17 21:24:27 -05001/*
2 * Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#include <linux/fs.h>
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/mount.h>
24#include <linux/mutex.h>
25#include <linux/slab.h>
26#include <linux/spinlock.h>
27#include <linux/writeback.h> /* for inode_lock */
28
29#include <asm/atomic.h>
30
31#include <linux/fsnotify_backend.h>
32#include "fsnotify.h"
33
34void fsnotify_clear_marks_by_mount(struct vfsmount *mnt)
35{
36 struct fsnotify_mark *mark, *lmark;
37 struct hlist_node *pos, *n;
38 LIST_HEAD(free_list);
39
40 spin_lock(&mnt->mnt_root->d_lock);
41 hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) {
42 list_add(&mark->m.free_m_list, &free_list);
43 hlist_del_init(&mark->m.m_list);
44 fsnotify_get_mark(mark);
45 }
46 spin_unlock(&mnt->mnt_root->d_lock);
47
48 list_for_each_entry_safe(mark, lmark, &free_list, m.free_m_list) {
49 fsnotify_destroy_mark(mark);
50 fsnotify_put_mark(mark);
51 }
52}
53
54/*
55 * Recalculate the mask of events relevant to a given vfsmount locked.
56 */
57static void fsnotify_recalc_vfsmount_mask_locked(struct vfsmount *mnt)
58{
59 struct fsnotify_mark *mark;
60 struct hlist_node *pos;
61 __u32 new_mask = 0;
62
63 assert_spin_locked(&mnt->mnt_root->d_lock);
64
65 hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list)
66 new_mask |= mark->mask;
67 mnt->mnt_fsnotify_mask = new_mask;
68}
69
70/*
71 * Recalculate the mnt->mnt_fsnotify_mask, or the mask of all FS_* event types
72 * any notifier is interested in hearing for this mount point
73 */
74void fsnotify_recalc_vfsmount_mask(struct vfsmount *mnt)
75{
76 spin_lock(&mnt->mnt_root->d_lock);
77 fsnotify_recalc_vfsmount_mask_locked(mnt);
78 spin_unlock(&mnt->mnt_root->d_lock);
79}
80
81void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark)
82{
83 struct vfsmount *mnt = mark->m.mnt;
84
85 assert_spin_locked(&mark->lock);
86 assert_spin_locked(&mark->group->mark_lock);
87
88 spin_lock(&mnt->mnt_root->d_lock);
89
90 hlist_del_init(&mark->m.m_list);
91 mark->m.mnt = NULL;
92
93 fsnotify_recalc_vfsmount_mask_locked(mnt);
94
95 spin_unlock(&mnt->mnt_root->d_lock);
96}
97
98static struct fsnotify_mark *fsnotify_find_vfsmount_mark_locked(struct fsnotify_group *group,
99 struct vfsmount *mnt)
100{
101 struct fsnotify_mark *mark;
102 struct hlist_node *pos;
103
104 assert_spin_locked(&mnt->mnt_root->d_lock);
105
106 hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list) {
107 if (mark->group == group) {
108 fsnotify_get_mark(mark);
109 return mark;
110 }
111 }
112 return NULL;
113}
114
115/*
116 * given a group and vfsmount, find the mark associated with that combination.
117 * if found take a reference to that mark and return it, else return NULL
118 */
119struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group,
120 struct vfsmount *mnt)
121{
122 struct fsnotify_mark *mark;
123
124 spin_lock(&mnt->mnt_root->d_lock);
125 mark = fsnotify_find_vfsmount_mark_locked(group, mnt);
126 spin_unlock(&mnt->mnt_root->d_lock);
127
128 return mark;
129}
130
131/*
132 * Attach an initialized mark to a given group and vfsmount.
133 * These marks may be used for the fsnotify backend to determine which
134 * event types should be delivered to which groups.
135 */
136int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
137 struct fsnotify_group *group, struct vfsmount *mnt,
138 int allow_dups)
139{
140 struct fsnotify_mark *lmark = NULL;
141 int ret = 0;
142
143 mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT;
144
145 /*
146 * LOCKING ORDER!!!!
147 * mark->lock
148 * group->mark_lock
149 * mnt->mnt_root->d_lock
150 */
151 assert_spin_locked(&mark->lock);
152 assert_spin_locked(&group->mark_lock);
153
154 spin_lock(&mnt->mnt_root->d_lock);
155
156 if (!allow_dups)
157 lmark = fsnotify_find_vfsmount_mark_locked(group, mnt);
158 if (!lmark) {
159 mark->m.mnt = mnt;
160
161 hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks);
162
163 fsnotify_recalc_vfsmount_mask_locked(mnt);
164 } else {
165 ret = -EEXIST;
166 }
167
168 spin_unlock(&mnt->mnt_root->d_lock);
169
170 return ret;
171}