Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vfsmount_mark.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 Red Hat, Inc., Eric Paris <[email protected]>
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/spinlock.h>
26 
27 #include <linux/atomic.h>
28 
29 #include <linux/fsnotify_backend.h>
30 #include "fsnotify.h"
31 #include "../mount.h"
32 
34 {
35  struct fsnotify_mark *mark, *lmark;
36  struct hlist_node *pos, *n;
37  struct mount *m = real_mount(mnt);
39 
40  spin_lock(&mnt->mnt_root->d_lock);
41  hlist_for_each_entry_safe(mark, pos, n, &m->mnt_fsnotify_marks, m.m_list) {
42  list_add(&mark->m.free_m_list, &free_list);
43  hlist_del_init_rcu(&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) {
50  fsnotify_put_mark(mark);
51  }
52 }
53 
54 void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group)
55 {
56  fsnotify_clear_marks_by_group_flags(group, FSNOTIFY_MARK_FLAG_VFSMOUNT);
57 }
58 
59 /*
60  * Recalculate the mask of events relevant to a given vfsmount locked.
61  */
62 static void fsnotify_recalc_vfsmount_mask_locked(struct vfsmount *mnt)
63 {
64  struct mount *m = real_mount(mnt);
65  struct fsnotify_mark *mark;
66  struct hlist_node *pos;
67  __u32 new_mask = 0;
68 
69  assert_spin_locked(&mnt->mnt_root->d_lock);
70 
71  hlist_for_each_entry(mark, pos, &m->mnt_fsnotify_marks, m.m_list)
72  new_mask |= mark->mask;
73  m->mnt_fsnotify_mask = new_mask;
74 }
75 
76 /*
77  * Recalculate the mnt->mnt_fsnotify_mask, or the mask of all FS_* event types
78  * any notifier is interested in hearing for this mount point
79  */
81 {
82  spin_lock(&mnt->mnt_root->d_lock);
83  fsnotify_recalc_vfsmount_mask_locked(mnt);
84  spin_unlock(&mnt->mnt_root->d_lock);
85 }
86 
87 void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark)
88 {
89  struct vfsmount *mnt = mark->m.mnt;
90 
91  assert_spin_locked(&mark->lock);
92  assert_spin_locked(&mark->group->mark_lock);
93 
94  spin_lock(&mnt->mnt_root->d_lock);
95 
96  hlist_del_init_rcu(&mark->m.m_list);
97  mark->m.mnt = NULL;
98 
99  fsnotify_recalc_vfsmount_mask_locked(mnt);
100 
101  spin_unlock(&mnt->mnt_root->d_lock);
102 }
103 
104 static struct fsnotify_mark *fsnotify_find_vfsmount_mark_locked(struct fsnotify_group *group,
105  struct vfsmount *mnt)
106 {
107  struct mount *m = real_mount(mnt);
108  struct fsnotify_mark *mark;
109  struct hlist_node *pos;
110 
111  assert_spin_locked(&mnt->mnt_root->d_lock);
112 
113  hlist_for_each_entry(mark, pos, &m->mnt_fsnotify_marks, m.m_list) {
114  if (mark->group == group) {
115  fsnotify_get_mark(mark);
116  return mark;
117  }
118  }
119  return NULL;
120 }
121 
122 /*
123  * given a group and vfsmount, find the mark associated with that combination.
124  * if found take a reference to that mark and return it, else return NULL
125  */
126 struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group,
127  struct vfsmount *mnt)
128 {
129  struct fsnotify_mark *mark;
130 
131  spin_lock(&mnt->mnt_root->d_lock);
132  mark = fsnotify_find_vfsmount_mark_locked(group, mnt);
133  spin_unlock(&mnt->mnt_root->d_lock);
134 
135  return mark;
136 }
137 
138 /*
139  * Attach an initialized mark to a given group and vfsmount.
140  * These marks may be used for the fsnotify backend to determine which
141  * event types should be delivered to which groups.
142  */
143 int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
144  struct fsnotify_group *group, struct vfsmount *mnt,
145  int allow_dups)
146 {
147  struct mount *m = real_mount(mnt);
148  struct fsnotify_mark *lmark;
149  struct hlist_node *node, *last = NULL;
150  int ret = 0;
151 
152  mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT;
153 
154  assert_spin_locked(&mark->lock);
155  assert_spin_locked(&group->mark_lock);
156 
157  spin_lock(&mnt->mnt_root->d_lock);
158 
159  mark->m.mnt = mnt;
160 
161  /* is mark the first mark? */
162  if (hlist_empty(&m->mnt_fsnotify_marks)) {
163  hlist_add_head_rcu(&mark->m.m_list, &m->mnt_fsnotify_marks);
164  goto out;
165  }
166 
167  /* should mark be in the middle of the current list? */
168  hlist_for_each_entry(lmark, node, &m->mnt_fsnotify_marks, m.m_list) {
169  last = node;
170 
171  if ((lmark->group == group) && !allow_dups) {
172  ret = -EEXIST;
173  goto out;
174  }
175 
176  if (mark->group->priority < lmark->group->priority)
177  continue;
178 
179  if ((mark->group->priority == lmark->group->priority) &&
180  (mark->group < lmark->group))
181  continue;
182 
183  hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list);
184  goto out;
185  }
186 
187  BUG_ON(last == NULL);
188  /* mark should be the last entry. last is the current last entry */
189  hlist_add_after_rcu(last, &mark->m.m_list);
190 out:
191  fsnotify_recalc_vfsmount_mask_locked(mnt);
192  spin_unlock(&mnt->mnt_root->d_lock);
193 
194  return ret;
195 }