diff options
author | Eric Paris <eparis@redhat.com> | 2009-12-17 21:24:34 -0500 |
---|---|---|
committer | Eric Paris <eparis@redhat.com> | 2010-07-28 09:59:01 -0400 |
commit | c4ec54b40d33f8016fea970a383cc584dd0e6019 (patch) | |
tree | 8e8865170cf340d1e79dc379f56417588715b2c8 /fs/notify/fsnotify.c | |
parent | d14f1729483fad3a8817fbbcbd017678b7d1ad26 (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.c | 47 |
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 | ||
172 | static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, | 172 | static 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 | ||
195 | static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) | 190 | static 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 | */ |
209 | void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | 204 | int 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 | 263 | out: | |
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 | } |
273 | EXPORT_SYMBOL_GPL(fsnotify); | 274 | EXPORT_SYMBOL_GPL(fsnotify); |
274 | 275 | ||