aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/notify/fsnotify.c47
-rw-r--r--include/linux/fsnotify.h19
-rw-r--r--include/linux/fsnotify_backend.h15
-rw-r--r--include/linux/security.h1
-rw-r--r--security/security.c16
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
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
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 */
38static 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 */
285extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, 288extern 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);
287extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); 290extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
288extern void __fsnotify_inode_delete(struct inode *inode); 291extern void __fsnotify_inode_delete(struct inode *inode);
289extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); 292extern 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
416static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, 419static 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
420static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) 425static 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
621int security_file_permission(struct file *file, int mask) 621int 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
626int security_file_alloc(struct file *file) 632int security_file_alloc(struct file *file)
@@ -684,7 +690,13 @@ int security_file_receive(struct file *file)
684 690
685int security_dentry_open(struct file *file, const struct cred *cred) 691int 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
690int security_task_create(unsigned long clone_flags) 702int security_task_create(unsigned long clone_flags)