aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify/fsnotify.c
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-12-17 21:24:34 -0500
committerEric Paris <eparis@redhat.com>2010-07-28 09:59:01 -0400
commitc4ec54b40d33f8016fea970a383cc584dd0e6019 (patch)
tree8e8865170cf340d1e79dc379f56417588715b2c8 /fs/notify/fsnotify.c
parentd14f1729483fad3a8817fbbcbd017678b7d1ad26 (diff)
fsnotify: new fsnotify hooks and events types for access decisions
introduce a new fsnotify hook, fsnotify_perm(), which is called from the security code. This hook is used to allow fsnotify groups to make access control decisions about events on the system. We also must change the generic fsnotify function to return an error code if we intend these hooks to be in any way useful. Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r--fs/notify/fsnotify.c47
1 files changed, 24 insertions, 23 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index c5adf833bf6a..668268627894 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -169,27 +169,22 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is)
169 } 169 }
170} 170}
171 171
172static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, 172static int send_to_group(struct fsnotify_group *group, struct inode *to_tell,
173 struct vfsmount *mnt, __u32 mask, void *data, 173 struct vfsmount *mnt, __u32 mask, void *data,
174 int data_is, u32 cookie, const unsigned char *file_name, 174 int data_is, u32 cookie, const unsigned char *file_name,
175 struct fsnotify_event **event) 175 struct fsnotify_event **event)
176{ 176{
177 if (!group->ops->should_send_event(group, to_tell, mnt, mask, 177 if (!group->ops->should_send_event(group, to_tell, mnt, mask,
178 data, data_is)) 178 data, data_is))
179 return; 179 return 0;
180 if (!*event) { 180 if (!*event) {
181 *event = fsnotify_create_event(to_tell, mask, data, 181 *event = fsnotify_create_event(to_tell, mask, data,
182 data_is, file_name, 182 data_is, file_name,
183 cookie, GFP_KERNEL); 183 cookie, GFP_KERNEL);
184 /*
185 * shit, we OOM'd and now we can't tell, maybe
186 * someday someone else will want to do something
187 * here
188 */
189 if (!*event) 184 if (!*event)
190 return; 185 return -ENOMEM;
191 } 186 }
192 group->ops->handle_event(group, *event); 187 return group->ops->handle_event(group, *event);
193} 188}
194 189
195static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) 190static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt)
@@ -206,20 +201,20 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt)
206 * out to all of the registered fsnotify_group. Those groups can then use the 201 * out to all of the registered fsnotify_group. Those groups can then use the
207 * notification event in whatever means they feel necessary. 202 * notification event in whatever means they feel necessary.
208 */ 203 */
209void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, 204int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
210 const unsigned char *file_name, u32 cookie) 205 const unsigned char *file_name, u32 cookie)
211{ 206{
212 struct fsnotify_group *group; 207 struct fsnotify_group *group;
213 struct fsnotify_event *event = NULL; 208 struct fsnotify_event *event = NULL;
214 struct vfsmount *mnt = NULL; 209 struct vfsmount *mnt = NULL;
215 int idx; 210 int idx, ret = 0;
216 /* global tests shouldn't care about events on child only the specific event */ 211 /* global tests shouldn't care about events on child only the specific event */
217 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); 212 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
218 213
219 /* if no fsnotify listeners, nothing to do */ 214 /* if no fsnotify listeners, nothing to do */
220 if (list_empty(&fsnotify_inode_groups) && 215 if (list_empty(&fsnotify_inode_groups) &&
221 list_empty(&fsnotify_vfsmount_groups)) 216 list_empty(&fsnotify_vfsmount_groups))
222 return; 217 return 0;
223 218
224 if (mask & FS_MODIFY) 219 if (mask & FS_MODIFY)
225 __fsnotify_flush_ignored_mask(to_tell, data, data_is); 220 __fsnotify_flush_ignored_mask(to_tell, data, data_is);
@@ -227,7 +222,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
227 /* if none of the directed listeners or vfsmount listeners care */ 222 /* if none of the directed listeners or vfsmount listeners care */
228 if (!(test_mask & fsnotify_inode_mask) && 223 if (!(test_mask & fsnotify_inode_mask) &&
229 !(test_mask & fsnotify_vfsmount_mask)) 224 !(test_mask & fsnotify_vfsmount_mask))
230 return; 225 return 0;
231 226
232 if (data_is == FSNOTIFY_EVENT_PATH) 227 if (data_is == FSNOTIFY_EVENT_PATH)
233 mnt = ((struct path *)data)->mnt; 228 mnt = ((struct path *)data)->mnt;
@@ -236,7 +231,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
236 * listeners list cares, nothing to do */ 231 * listeners list cares, nothing to do */
237 if (!(test_mask & to_tell->i_fsnotify_mask) && 232 if (!(test_mask & to_tell->i_fsnotify_mask) &&
238 !needed_by_vfsmount(test_mask, mnt)) 233 !needed_by_vfsmount(test_mask, mnt))
239 return; 234 return 0;
240 235
241 /* 236 /*
242 * SRCU!! the groups list is very very much read only and the path is 237 * SRCU!! the groups list is very very much read only and the path is
@@ -248,20 +243,24 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
248 if (test_mask & to_tell->i_fsnotify_mask) { 243 if (test_mask & to_tell->i_fsnotify_mask) {
249 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { 244 list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) {
250 if (test_mask & group->mask) { 245 if (test_mask & group->mask) {
251 send_to_group(group, to_tell, NULL, mask, data, data_is, 246 ret = send_to_group(group, to_tell, NULL, mask, data, data_is,
252 cookie, file_name, &event); 247 cookie, file_name, &event);
248 if (ret)
249 goto out;
253 } 250 }
254 } 251 }
255 } 252 }
256 if (needed_by_vfsmount(test_mask, mnt)) { 253 if (needed_by_vfsmount(test_mask, mnt)) {
257 list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { 254 list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) {
258 if (test_mask & group->mask) { 255 if (test_mask & group->mask) {
259 send_to_group(group, to_tell, mnt, mask, data, data_is, 256 ret = send_to_group(group, to_tell, mnt, mask, data, data_is,
260 cookie, file_name, &event); 257 cookie, file_name, &event);
258 if (ret)
259 goto out;
261 } 260 }
262 } 261 }
263 } 262 }
264 263out:
265 srcu_read_unlock(&fsnotify_grp_srcu, idx); 264 srcu_read_unlock(&fsnotify_grp_srcu, idx);
266 /* 265 /*
267 * fsnotify_create_event() took a reference so the event can't be cleaned 266 * fsnotify_create_event() took a reference so the event can't be cleaned
@@ -269,6 +268,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
269 */ 268 */
270 if (event) 269 if (event)
271 fsnotify_put_event(event); 270 fsnotify_put_event(event);
271
272 return 0;
272} 273}
273EXPORT_SYMBOL_GPL(fsnotify); 274EXPORT_SYMBOL_GPL(fsnotify);
274 275