diff options
-rw-r--r-- | fs/notify/inotify/inotify_fsnotify.c | 56 | ||||
-rw-r--r-- | fs/notify/inotify/inotify_user.c | 2 | ||||
-rw-r--r-- | fs/notify/notification.c | 73 | ||||
-rw-r--r-- | include/linux/fsnotify_backend.h | 6 |
4 files changed, 79 insertions, 58 deletions
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 1f33234cc308..0a0f5d0f0d0a 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c | |||
@@ -32,6 +32,60 @@ | |||
32 | 32 | ||
33 | #include "inotify.h" | 33 | #include "inotify.h" |
34 | 34 | ||
35 | /* | ||
36 | * Check if 2 events contain the same information. We do not compare private data | ||
37 | * but at this moment that isn't a problem for any know fsnotify listeners. | ||
38 | */ | ||
39 | static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) | ||
40 | { | ||
41 | if ((old->mask == new->mask) && | ||
42 | (old->to_tell == new->to_tell) && | ||
43 | (old->data_type == new->data_type) && | ||
44 | (old->name_len == new->name_len)) { | ||
45 | switch (old->data_type) { | ||
46 | case (FSNOTIFY_EVENT_INODE): | ||
47 | /* remember, after old was put on the wait_q we aren't | ||
48 | * allowed to look at the inode any more, only thing | ||
49 | * left to check was if the file_name is the same */ | ||
50 | if (!old->name_len || | ||
51 | !strcmp(old->file_name, new->file_name)) | ||
52 | return true; | ||
53 | break; | ||
54 | case (FSNOTIFY_EVENT_PATH): | ||
55 | if ((old->path.mnt == new->path.mnt) && | ||
56 | (old->path.dentry == new->path.dentry)) | ||
57 | return true; | ||
58 | break; | ||
59 | case (FSNOTIFY_EVENT_NONE): | ||
60 | if (old->mask & FS_Q_OVERFLOW) | ||
61 | return true; | ||
62 | else if (old->mask & FS_IN_IGNORED) | ||
63 | return false; | ||
64 | return true; | ||
65 | }; | ||
66 | } | ||
67 | return false; | ||
68 | } | ||
69 | |||
70 | static int inotify_merge(struct list_head *list, struct fsnotify_event *event) | ||
71 | { | ||
72 | struct fsnotify_event_holder *last_holder; | ||
73 | struct fsnotify_event *last_event; | ||
74 | int ret = 0; | ||
75 | |||
76 | /* and the list better be locked by something too */ | ||
77 | spin_lock(&event->lock); | ||
78 | |||
79 | last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); | ||
80 | last_event = last_holder->event; | ||
81 | if (event_compare(last_event, event)) | ||
82 | ret = -EEXIST; | ||
83 | |||
84 | spin_unlock(&event->lock); | ||
85 | |||
86 | return ret; | ||
87 | } | ||
88 | |||
35 | static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) | 89 | static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) |
36 | { | 90 | { |
37 | struct fsnotify_mark_entry *entry; | 91 | struct fsnotify_mark_entry *entry; |
@@ -62,7 +116,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev | |||
62 | fsn_event_priv->group = group; | 116 | fsn_event_priv->group = group; |
63 | event_priv->wd = wd; | 117 | event_priv->wd = wd; |
64 | 118 | ||
65 | ret = fsnotify_add_notify_event(group, event, fsn_event_priv); | 119 | ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); |
66 | if (ret) { | 120 | if (ret) { |
67 | inotify_free_event_priv(fsn_event_priv); | 121 | inotify_free_event_priv(fsn_event_priv); |
68 | /* EEXIST says we tail matched, EOVERFLOW isn't something | 122 | /* EEXIST says we tail matched, EOVERFLOW isn't something |
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f2b542479e91..cbe16df326f8 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
@@ -531,7 +531,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, | |||
531 | fsn_event_priv->group = group; | 531 | fsn_event_priv->group = group; |
532 | event_priv->wd = ientry->wd; | 532 | event_priv->wd = ientry->wd; |
533 | 533 | ||
534 | ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv); | 534 | ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); |
535 | if (ret) | 535 | if (ret) |
536 | inotify_free_event_priv(fsn_event_priv); | 536 | inotify_free_event_priv(fsn_event_priv); |
537 | 537 | ||
diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b34ce7ad0409..6dc96b35e4a7 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c | |||
@@ -104,7 +104,8 @@ struct fsnotify_event_holder *fsnotify_alloc_event_holder(void) | |||
104 | 104 | ||
105 | void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) | 105 | void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) |
106 | { | 106 | { |
107 | kmem_cache_free(fsnotify_event_holder_cachep, holder); | 107 | if (holder) |
108 | kmem_cache_free(fsnotify_event_holder_cachep, holder); | ||
108 | } | 109 | } |
109 | 110 | ||
110 | /* | 111 | /* |
@@ -129,53 +130,17 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot | |||
129 | } | 130 | } |
130 | 131 | ||
131 | /* | 132 | /* |
132 | * Check if 2 events contain the same information. We do not compare private data | ||
133 | * but at this moment that isn't a problem for any know fsnotify listeners. | ||
134 | */ | ||
135 | static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) | ||
136 | { | ||
137 | if ((old->mask == new->mask) && | ||
138 | (old->to_tell == new->to_tell) && | ||
139 | (old->data_type == new->data_type) && | ||
140 | (old->name_len == new->name_len)) { | ||
141 | switch (old->data_type) { | ||
142 | case (FSNOTIFY_EVENT_INODE): | ||
143 | /* remember, after old was put on the wait_q we aren't | ||
144 | * allowed to look at the inode any more, only thing | ||
145 | * left to check was if the file_name is the same */ | ||
146 | if (!old->name_len || | ||
147 | !strcmp(old->file_name, new->file_name)) | ||
148 | return true; | ||
149 | break; | ||
150 | case (FSNOTIFY_EVENT_PATH): | ||
151 | if ((old->path.mnt == new->path.mnt) && | ||
152 | (old->path.dentry == new->path.dentry)) | ||
153 | return true; | ||
154 | break; | ||
155 | case (FSNOTIFY_EVENT_NONE): | ||
156 | if (old->mask & FS_Q_OVERFLOW) | ||
157 | return true; | ||
158 | else if (old->mask & FS_IN_IGNORED) | ||
159 | return false; | ||
160 | return false; | ||
161 | }; | ||
162 | } | ||
163 | return false; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * Add an event to the group notification queue. The group can later pull this | 133 | * Add an event to the group notification queue. The group can later pull this |
168 | * event off the queue to deal with. If the event is successfully added to the | 134 | * event off the queue to deal with. If the event is successfully added to the |
169 | * group's notification queue, a reference is taken on event. | 135 | * group's notification queue, a reference is taken on event. |
170 | */ | 136 | */ |
171 | int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, | 137 | int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, |
172 | struct fsnotify_event_private_data *priv) | 138 | struct fsnotify_event_private_data *priv, |
139 | int (*merge)(struct list_head *, struct fsnotify_event *)) | ||
173 | { | 140 | { |
174 | struct fsnotify_event_holder *holder = NULL; | 141 | struct fsnotify_event_holder *holder = NULL; |
175 | struct list_head *list = &group->notification_list; | 142 | struct list_head *list = &group->notification_list; |
176 | struct fsnotify_event_holder *last_holder; | 143 | int rc = 0; |
177 | struct fsnotify_event *last_event; | ||
178 | int ret = 0; | ||
179 | 144 | ||
180 | /* | 145 | /* |
181 | * There is one fsnotify_event_holder embedded inside each fsnotify_event. | 146 | * There is one fsnotify_event_holder embedded inside each fsnotify_event. |
@@ -196,11 +161,23 @@ alloc_holder: | |||
196 | 161 | ||
197 | if (group->q_len >= group->max_events) { | 162 | if (group->q_len >= group->max_events) { |
198 | event = q_overflow_event; | 163 | event = q_overflow_event; |
199 | ret = -EOVERFLOW; | 164 | rc = -EOVERFLOW; |
200 | /* sorry, no private data on the overflow event */ | 165 | /* sorry, no private data on the overflow event */ |
201 | priv = NULL; | 166 | priv = NULL; |
202 | } | 167 | } |
203 | 168 | ||
169 | if (!list_empty(list) && merge) { | ||
170 | int ret; | ||
171 | |||
172 | ret = merge(list, event); | ||
173 | if (ret) { | ||
174 | mutex_unlock(&group->notification_mutex); | ||
175 | if (holder != &event->holder) | ||
176 | fsnotify_destroy_event_holder(holder); | ||
177 | return ret; | ||
178 | } | ||
179 | } | ||
180 | |||
204 | spin_lock(&event->lock); | 181 | spin_lock(&event->lock); |
205 | 182 | ||
206 | if (list_empty(&event->holder.event_list)) { | 183 | if (list_empty(&event->holder.event_list)) { |
@@ -215,18 +192,6 @@ alloc_holder: | |||
215 | goto alloc_holder; | 192 | goto alloc_holder; |
216 | } | 193 | } |
217 | 194 | ||
218 | if (!list_empty(list)) { | ||
219 | last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); | ||
220 | last_event = last_holder->event; | ||
221 | if (event_compare(last_event, event)) { | ||
222 | spin_unlock(&event->lock); | ||
223 | mutex_unlock(&group->notification_mutex); | ||
224 | if (holder != &event->holder) | ||
225 | fsnotify_destroy_event_holder(holder); | ||
226 | return -EEXIST; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | group->q_len++; | 195 | group->q_len++; |
231 | holder->event = event; | 196 | holder->event = event; |
232 | 197 | ||
@@ -238,7 +203,7 @@ alloc_holder: | |||
238 | mutex_unlock(&group->notification_mutex); | 203 | mutex_unlock(&group->notification_mutex); |
239 | 204 | ||
240 | wake_up(&group->notification_waitq); | 205 | wake_up(&group->notification_waitq); |
241 | return ret; | 206 | return rc; |
242 | } | 207 | } |
243 | 208 | ||
244 | /* | 209 | /* |
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 0e0c2b76b067..25789d45fad8 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h | |||
@@ -328,8 +328,10 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc | |||
328 | struct fsnotify_event *event); | 328 | struct fsnotify_event *event); |
329 | 329 | ||
330 | /* attach the event to the group notification queue */ | 330 | /* attach the event to the group notification queue */ |
331 | extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, | 331 | extern int fsnotify_add_notify_event(struct fsnotify_group *group, |
332 | struct fsnotify_event_private_data *priv); | 332 | struct fsnotify_event *event, |
333 | struct fsnotify_event_private_data *priv, | ||
334 | int (*merge)(struct list_head *, struct fsnotify_event *)); | ||
333 | /* true if the group notification queue is empty */ | 335 | /* true if the group notification queue is empty */ |
334 | extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); | 336 | extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); |
335 | /* return, but do not dequeue the first event on the notification queue */ | 337 | /* return, but do not dequeue the first event on the notification queue */ |