blob: c35c1175c4cfbd1384a941069a77fdcb55dbcaaa [file] [log] [blame]
Eric Parisff0b16a2009-12-17 21:24:25 -05001#include <linux/fdtable.h>
2#include <linux/fsnotify_backend.h>
3#include <linux/init.h>
4#include <linux/kernel.h> /* UINT_MAX */
5#include <linux/types.h>
6
7#include "fanotify.h"
8
Eric Paris767cd462009-12-17 21:24:25 -05009static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new)
10{
11 pr_debug("%s: old=%p new=%p\n", __func__, old, new);
12
13 if ((old->mask == new->mask) &&
14 (old->to_tell == new->to_tell) &&
15 (old->data_type == new->data_type)) {
16 switch (old->data_type) {
17 case (FSNOTIFY_EVENT_PATH):
18 if ((old->path.mnt == new->path.mnt) &&
19 (old->path.dentry == new->path.dentry))
20 return true;
21 case (FSNOTIFY_EVENT_NONE):
22 return true;
23 default:
24 BUG();
25 };
26 }
27 return false;
28}
29
30static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
31{
32 struct fsnotify_event_holder *holder;
33 struct fsnotify_event *test_event;
34
35 pr_debug("%s: list=%p event=%p\n", __func__, list, event);
36
37 /* and the list better be locked by something too! */
38
39 list_for_each_entry_reverse(holder, list, event_list) {
40 test_event = holder->event;
41 if (should_merge(test_event, event))
42 return -EEXIST;
43 }
44
45 return 0;
46}
47
Eric Parisff0b16a2009-12-17 21:24:25 -050048static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
49{
50 int ret;
51
52
53 BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
54 BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
55 BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE);
56 BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE);
57 BUILD_BUG_ON(FAN_OPEN != FS_OPEN);
58 BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD);
59 BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
60
61 pr_debug("%s: group=%p event=%p\n", __func__, group, event);
62
Eric Paris767cd462009-12-17 21:24:25 -050063 ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge);
64 /* -EEXIST means this event was merged with another, not that it was an error */
65 if (ret == -EEXIST)
66 ret = 0;
Eric Parisff0b16a2009-12-17 21:24:25 -050067 return ret;
68}
69
70static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *inode,
71 struct vfsmount *mnt, __u32 mask, void *data,
72 int data_type)
73{
74 struct fsnotify_mark *fsn_mark;
75 bool send;
76
77 pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n",
78 __func__, group, inode, mask, data, data_type);
79
80 /* sorry, fanotify only gives a damn about files and dirs */
81 if (!S_ISREG(inode->i_mode) &&
82 !S_ISDIR(inode->i_mode))
83 return false;
84
85 /* if we don't have enough info to send an event to userspace say no */
86 if (data_type != FSNOTIFY_EVENT_PATH)
87 return false;
88
89 fsn_mark = fsnotify_find_mark(group, inode);
90 if (!fsn_mark)
91 return false;
92
93 /* if the event is for a child and this inode doesn't care about
94 * events on the child, don't send it! */
95 if ((mask & FS_EVENT_ON_CHILD) &&
96 !(fsn_mark->mask & FS_EVENT_ON_CHILD)) {
97 send = false;
98 } else {
99 /*
100 * We care about children, but do we care about this particular
101 * type of event?
102 */
103 mask = (mask & ~FS_EVENT_ON_CHILD);
104 send = (fsn_mark->mask & mask);
105 }
106
107 /* find took a reference */
108 fsnotify_put_mark(fsn_mark);
109
110 return send;
111}
112
113const struct fsnotify_ops fanotify_fsnotify_ops = {
114 .handle_event = fanotify_handle_event,
115 .should_send_event = fanotify_should_send_event,
116 .free_group_priv = NULL,
117 .free_event_priv = NULL,
118 .freeing_mark = NULL,
119};