]> bbs.cooldavid.org Git - net-next-2.6.git/blob - fs/notify/fanotify/fanotify.c
fanotify: use merge argument to determine actual event added to queue
[net-next-2.6.git] / fs / notify / fanotify / fanotify.c
1 #include <linux/fanotify.h>
2 #include <linux/fdtable.h>
3 #include <linux/fsnotify_backend.h>
4 #include <linux/init.h>
5 #include <linux/kernel.h> /* UINT_MAX */
6 #include <linux/mount.h>
7 #include <linux/types.h>
8
9 static 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->to_tell == new->to_tell &&
14             old->data_type == new->data_type &&
15             old->tgid == new->tgid) {
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
30 /* Note, if we return an event in *arg that a reference is being held... */
31 static int fanotify_merge(struct list_head *list,
32                           struct fsnotify_event *event,
33                           void **arg)
34 {
35         struct fsnotify_event_holder *test_holder;
36         struct fsnotify_event *test_event;
37         struct fsnotify_event *new_event;
38         struct fsnotify_event **return_event = (struct fsnotify_event **)arg;
39         int ret = 0;
40
41         pr_debug("%s: list=%p event=%p\n", __func__, list, event);
42
43         *return_event = NULL;
44
45         /* and the list better be locked by something too! */
46
47         list_for_each_entry_reverse(test_holder, list, event_list) {
48                 test_event = test_holder->event;
49                 if (should_merge(test_event, event)) {
50                         fsnotify_get_event(test_event);
51                         *return_event = test_event;
52
53                         ret = -EEXIST;
54                         /* if they are exactly the same we are done */
55                         if (test_event->mask == event->mask)
56                                 goto out;
57
58                         /*
59                          * if the refcnt == 1 this is the only queue
60                          * for this event and so we can update the mask
61                          * in place.
62                          */
63                         if (atomic_read(&test_event->refcnt) == 1) {
64                                 test_event->mask |= event->mask;
65                                 goto out;
66                         }
67
68                         /* can't allocate memory, merge was no possible */
69                         new_event = fsnotify_clone_event(test_event);
70                         if (unlikely(!new_event)) {
71                                 ret = 0;
72                                 goto out;
73                         }
74
75                         /* we didn't return the test_event, so drop that ref */
76                         fsnotify_put_event(test_event);
77                         /* the reference we return on new_event is from clone */
78                         *return_event = new_event;
79
80                         /* build new event and replace it on the list */
81                         new_event->mask = (test_event->mask | event->mask);
82                         fsnotify_replace_event(test_holder, new_event);
83
84                         break;
85                 }
86         }
87 out:
88         return ret;
89 }
90
91 static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
92 {
93         int ret;
94         struct fsnotify_event *used_event;
95
96         BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
97         BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
98         BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE);
99         BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE);
100         BUILD_BUG_ON(FAN_OPEN != FS_OPEN);
101         BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD);
102         BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
103
104         pr_debug("%s: group=%p event=%p\n", __func__, group, event);
105
106         ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, (void **)&used_event);
107         /* -EEXIST means this event was merged with another, not that it was an error */
108         if (ret == -EEXIST)
109                 ret = 0;
110         if (used_event)
111                 fsnotify_put_event(used_event);
112         return ret;
113 }
114
115 static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsmount *mnt,
116                                        struct inode *inode, __u32 mask)
117 {
118         struct fsnotify_mark *mnt_mark;
119         struct fsnotify_mark *inode_mark;
120
121         pr_debug("%s: group=%p vfsmount=%p mask=%x\n",
122                  __func__, group, mnt, mask);
123
124         mnt_mark = fsnotify_find_vfsmount_mark(group, mnt);
125         if (!mnt_mark)
126                 return false;
127
128         mask &= mnt_mark->mask;
129         mask &= ~mnt_mark->ignored_mask;
130
131         if (mask) {
132                 inode_mark = fsnotify_find_inode_mark(group, inode);
133                 if (inode_mark) {
134                         mask &= ~inode_mark->ignored_mask;
135                         fsnotify_put_mark(inode_mark);
136                 }
137         }
138
139         /* find took a reference */
140         fsnotify_put_mark(mnt_mark);
141
142         return mask;
143 }
144
145 static bool should_send_inode_event(struct fsnotify_group *group, struct inode *inode,
146                                     __u32 mask)
147 {
148         struct fsnotify_mark *fsn_mark;
149
150         pr_debug("%s: group=%p inode=%p mask=%x\n",
151                  __func__, group, inode, mask);
152
153         fsn_mark = fsnotify_find_inode_mark(group, inode);
154         if (!fsn_mark)
155                 return false;
156
157         /* if the event is for a child and this inode doesn't care about
158          * events on the child, don't send it! */
159         if ((mask & FS_EVENT_ON_CHILD) &&
160             !(fsn_mark->mask & FS_EVENT_ON_CHILD)) {
161                 mask = 0;
162         } else {
163                 /*
164                  * We care about children, but do we care about this particular
165                  * type of event?
166                  */
167                 mask &= ~FS_EVENT_ON_CHILD;
168                 mask &= fsn_mark->mask;
169                 mask &= ~fsn_mark->ignored_mask;
170         }
171
172         /* find took a reference */
173         fsnotify_put_mark(fsn_mark);
174
175         return mask;
176 }
177
178 static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell,
179                                        struct vfsmount *mnt, __u32 mask, void *data,
180                                        int data_type)
181 {
182         pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n",
183                  __func__, group, to_tell, mnt, mask, data, data_type);
184
185         /* sorry, fanotify only gives a damn about files and dirs */
186         if (!S_ISREG(to_tell->i_mode) &&
187             !S_ISDIR(to_tell->i_mode))
188                 return false;
189
190         /* if we don't have enough info to send an event to userspace say no */
191         if (data_type != FSNOTIFY_EVENT_PATH)
192                 return false;
193
194         if (mnt)
195                 return should_send_vfsmount_event(group, mnt, to_tell, mask);
196         else
197                 return should_send_inode_event(group, to_tell, mask);
198 }
199
200 const struct fsnotify_ops fanotify_fsnotify_ops = {
201         .handle_event = fanotify_handle_event,
202         .should_send_event = fanotify_should_send_event,
203         .free_group_priv = NULL,
204         .free_event_priv = NULL,
205         .freeing_mark = NULL,
206 };