diff options
-rw-r--r-- | fs/notify/fsnotify.c | 47 | ||||
-rw-r--r-- | include/linux/fsnotify.h | 19 | ||||
-rw-r--r-- | include/linux/fsnotify_backend.h | 15 | ||||
-rw-r--r-- | include/linux/security.h | 1 | ||||
-rw-r--r-- | security/security.c | 16 |
5 files changed, 68 insertions, 30 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 | ||
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index b8cf161f5a6d..64efda9aae62 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h | |||
@@ -34,6 +34,25 @@ static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u | |||
34 | __fsnotify_parent(path, dentry, mask); | 34 | __fsnotify_parent(path, dentry, mask); |
35 | } | 35 | } |
36 | 36 | ||
37 | /* simple call site for access decisions */ | ||
38 | static inline int fsnotify_perm(struct file *file, int mask) | ||
39 | { | ||
40 | struct path *path = &file->f_path; | ||
41 | struct inode *inode = path->dentry->d_inode; | ||
42 | __u32 fsnotify_mask; | ||
43 | |||
44 | if (file->f_mode & FMODE_NONOTIFY) | ||
45 | return 0; | ||
46 | if (!(mask & (MAY_READ | MAY_OPEN))) | ||
47 | return 0; | ||
48 | if (mask & MAY_READ) | ||
49 | fsnotify_mask = FS_ACCESS_PERM; | ||
50 | if (mask & MAY_OPEN) | ||
51 | fsnotify_mask = FS_OPEN_PERM; | ||
52 | |||
53 | return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); | ||
54 | } | ||
55 | |||
37 | /* | 56 | /* |
38 | * fsnotify_d_move - dentry has been moved | 57 | * fsnotify_d_move - dentry has been moved |
39 | * Called with dcache_lock and dentry->d_lock held. | 58 | * Called with dcache_lock and dentry->d_lock held. |
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index efe9ba321cf2..c34728e7d8cb 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h | |||
@@ -41,6 +41,9 @@ | |||
41 | #define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ | 41 | #define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ |
42 | #define FS_IN_IGNORED 0x00008000 /* last inotify event here */ | 42 | #define FS_IN_IGNORED 0x00008000 /* last inotify event here */ |
43 | 43 | ||
44 | #define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */ | ||
45 | #define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */ | ||
46 | |||
44 | #define FS_IN_ISDIR 0x40000000 /* event occurred against dir */ | 47 | #define FS_IN_ISDIR 0x40000000 /* event occurred against dir */ |
45 | #define FS_IN_ONESHOT 0x80000000 /* only send event once */ | 48 | #define FS_IN_ONESHOT 0x80000000 /* only send event once */ |
46 | 49 | ||
@@ -282,8 +285,8 @@ struct fsnotify_mark { | |||
282 | /* called from the vfs helpers */ | 285 | /* called from the vfs helpers */ |
283 | 286 | ||
284 | /* main fsnotify call to send events */ | 287 | /* main fsnotify call to send events */ |
285 | extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | 288 | extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, |
286 | const unsigned char *name, u32 cookie); | 289 | const unsigned char *name, u32 cookie); |
287 | extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); | 290 | extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); |
288 | extern void __fsnotify_inode_delete(struct inode *inode); | 291 | extern void __fsnotify_inode_delete(struct inode *inode); |
289 | extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); | 292 | extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); |
@@ -413,9 +416,11 @@ extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, | |||
413 | 416 | ||
414 | #else | 417 | #else |
415 | 418 | ||
416 | static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | 419 | static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, |
417 | const unsigned char *name, u32 cookie) | 420 | const unsigned char *name, u32 cookie) |
418 | {} | 421 | { |
422 | return 0; | ||
423 | } | ||
419 | 424 | ||
420 | static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) | 425 | static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) |
421 | {} | 426 | {} |
diff --git a/include/linux/security.h b/include/linux/security.h index 0c8819170463..24fc29540aa3 100644 --- a/include/linux/security.h +++ b/include/linux/security.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #define __LINUX_SECURITY_H | 23 | #define __LINUX_SECURITY_H |
24 | 24 | ||
25 | #include <linux/fs.h> | 25 | #include <linux/fs.h> |
26 | #include <linux/fsnotify.h> | ||
26 | #include <linux/binfmts.h> | 27 | #include <linux/binfmts.h> |
27 | #include <linux/signal.h> | 28 | #include <linux/signal.h> |
28 | #include <linux/resource.h> | 29 | #include <linux/resource.h> |
diff --git a/security/security.c b/security/security.c index 351942a4ca0e..f6ac27cd3452 100644 --- a/security/security.c +++ b/security/security.c | |||
@@ -620,7 +620,13 @@ void security_inode_getsecid(const struct inode *inode, u32 *secid) | |||
620 | 620 | ||
621 | int security_file_permission(struct file *file, int mask) | 621 | int security_file_permission(struct file *file, int mask) |
622 | { | 622 | { |
623 | return security_ops->file_permission(file, mask); | 623 | int ret; |
624 | |||
625 | ret = security_ops->file_permission(file, mask); | ||
626 | if (ret) | ||
627 | return ret; | ||
628 | |||
629 | return fsnotify_perm(file, mask); | ||
624 | } | 630 | } |
625 | 631 | ||
626 | int security_file_alloc(struct file *file) | 632 | int security_file_alloc(struct file *file) |
@@ -684,7 +690,13 @@ int security_file_receive(struct file *file) | |||
684 | 690 | ||
685 | int security_dentry_open(struct file *file, const struct cred *cred) | 691 | int security_dentry_open(struct file *file, const struct cred *cred) |
686 | { | 692 | { |
687 | return security_ops->dentry_open(file, cred); | 693 | int ret; |
694 | |||
695 | ret = security_ops->dentry_open(file, cred); | ||
696 | if (ret) | ||
697 | return ret; | ||
698 | |||
699 | return fsnotify_perm(file, MAY_OPEN); | ||
688 | } | 700 | } |
689 | 701 | ||
690 | int security_task_create(unsigned long clone_flags) | 702 | int security_task_create(unsigned long clone_flags) |