aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/Makefile2
-rw-r--r--fs/notify/fsnotify.h6
-rw-r--r--fs/notify/mark.c20
-rw-r--r--fs/notify/vfsmount_mark.c171
4 files changed, 189 insertions, 10 deletions
diff --git a/fs/notify/Makefile b/fs/notify/Makefile
index 8f7f3b024a2e..ae5f33a6d868 100644
--- a/fs/notify/Makefile
+++ b/fs/notify/Makefile
@@ -1,5 +1,5 @@
1obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o \ 1obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o \
2 mark.o 2 mark.o vfsmount_mark.o
3 3
4obj-y += dnotify/ 4obj-y += dnotify/
5obj-y += inotify/ 5obj-y += inotify/
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
index 7c7a904b802d..38f3fb5cef28 100644
--- a/fs/notify/fsnotify.h
+++ b/fs/notify/fsnotify.h
@@ -24,6 +24,10 @@ extern void fsnotify_flush_notify(struct fsnotify_group *group);
24extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark, 24extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
25 struct fsnotify_group *group, struct inode *inode, 25 struct fsnotify_group *group, struct inode *inode,
26 int allow_dups); 26 int allow_dups);
27/* add a mark to a vfsmount */
28extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
29 struct fsnotify_group *group, struct vfsmount *mnt,
30 int allow_dups);
27 31
28/* add a group to the inode group list */ 32/* add a group to the inode group list */
29extern void fsnotify_add_inode_group(struct fsnotify_group *group); 33extern void fsnotify_add_inode_group(struct fsnotify_group *group);
@@ -32,6 +36,8 @@ extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group);
32/* final kfree of a group */ 36/* final kfree of a group */
33extern void fsnotify_final_destroy_group(struct fsnotify_group *group); 37extern void fsnotify_final_destroy_group(struct fsnotify_group *group);
34 38
39/* vfsmount specific destruction of a mark */
40extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark);
35/* inode specific destruction of a mark */ 41/* inode specific destruction of a mark */
36extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark); 42extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark);
37/* run the list of all marks associated with inode and flag them to be freed */ 43/* run the list of all marks associated with inode and flag them to be freed */
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index 57bb1d74a2b6..d296ec9ffb2a 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -115,15 +115,11 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
115void fsnotify_destroy_mark(struct fsnotify_mark *mark) 115void fsnotify_destroy_mark(struct fsnotify_mark *mark)
116{ 116{
117 struct fsnotify_group *group; 117 struct fsnotify_group *group;
118 struct inode *inode; 118 struct inode *inode = NULL;
119 119
120 spin_lock(&mark->lock); 120 spin_lock(&mark->lock);
121 121
122 group = mark->group; 122 group = mark->group;
123 inode = mark->i.inode;
124
125 BUG_ON(group && !inode);
126 BUG_ON(!group && inode);
127 123
128 /* if !group something else already marked this to die */ 124 /* if !group something else already marked this to die */
129 if (!group) { 125 if (!group) {
@@ -136,8 +132,11 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
136 132
137 spin_lock(&group->mark_lock); 133 spin_lock(&group->mark_lock);
138 134
139 if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) 135 if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) {
140 fsnotify_destroy_inode_mark(mark); 136 fsnotify_destroy_inode_mark(mark);
137 inode = mark->i.inode;
138 } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT)
139 fsnotify_destroy_vfsmount_mark(mark);
141 else 140 else
142 BUG(); 141 BUG();
143 142
@@ -169,8 +168,8 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
169 * is just a lazy update (and could be a perf win...) 168 * is just a lazy update (and could be a perf win...)
170 */ 169 */
171 170
172 171 if (inode)
173 iput(inode); 172 iput(inode);
174 173
175 /* 174 /*
176 * it's possible that this group tried to destroy itself, but this 175 * it's possible that this group tried to destroy itself, but this
@@ -192,7 +191,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
192{ 191{
193 int ret = 0; 192 int ret = 0;
194 193
195 BUG_ON(mnt);
196 BUG_ON(inode && mnt); 194 BUG_ON(inode && mnt);
197 BUG_ON(!inode && !mnt); 195 BUG_ON(!inode && !mnt);
198 196
@@ -223,6 +221,10 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
223 ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups); 221 ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups);
224 if (ret) 222 if (ret)
225 goto err; 223 goto err;
224 } else if (mnt) {
225 ret = fsnotify_add_vfsmount_mark(mark, group, mnt, allow_dups);
226 if (ret)
227 goto err;
226 } else { 228 } else {
227 BUG(); 229 BUG();
228 } 230 }
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c
new file mode 100644
index 000000000000..1b61d0a942de
--- /dev/null
+++ b/fs/notify/vfsmount_mark.c
@@ -0,0 +1,171 @@
1/*
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}