diff options
-rw-r--r-- | fs/notify/dnotify/dnotify.c | 1 | ||||
-rw-r--r-- | fs/notify/notification.c | 52 | ||||
-rw-r--r-- | include/linux/fsnotify_backend.h | 24 |
3 files changed, 68 insertions, 9 deletions
diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index d9d80f502c6f..12f9e6b1ffe2 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c | |||
@@ -183,6 +183,7 @@ static struct fsnotify_ops dnotify_fsnotify_ops = { | |||
183 | .should_send_event = dnotify_should_send_event, | 183 | .should_send_event = dnotify_should_send_event, |
184 | .free_group_priv = NULL, | 184 | .free_group_priv = NULL, |
185 | .freeing_mark = dnotify_freeing_mark, | 185 | .freeing_mark = dnotify_freeing_mark, |
186 | .free_event_priv = NULL, | ||
186 | }; | 187 | }; |
187 | 188 | ||
188 | /* | 189 | /* |
diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 346f6e5c3553..959b73e756fd 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c | |||
@@ -90,6 +90,8 @@ void fsnotify_put_event(struct fsnotify_event *event) | |||
90 | if (event->data_type == FSNOTIFY_EVENT_PATH) | 90 | if (event->data_type == FSNOTIFY_EVENT_PATH) |
91 | path_put(&event->path); | 91 | path_put(&event->path); |
92 | 92 | ||
93 | BUG_ON(!list_empty(&event->private_data_list)); | ||
94 | |||
93 | kfree(event->file_name); | 95 | kfree(event->file_name); |
94 | kmem_cache_free(fsnotify_event_cachep, event); | 96 | kmem_cache_free(fsnotify_event_cachep, event); |
95 | } | 97 | } |
@@ -106,7 +108,29 @@ void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) | |||
106 | } | 108 | } |
107 | 109 | ||
108 | /* | 110 | /* |
109 | * check if 2 events contain the same information. | 111 | * Find the private data that the group previously attached to this event when |
112 | * the group added the event to the notification queue (fsnotify_add_notify_event) | ||
113 | */ | ||
114 | struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnotify_group *group, struct fsnotify_event *event) | ||
115 | { | ||
116 | struct fsnotify_event_private_data *lpriv; | ||
117 | struct fsnotify_event_private_data *priv = NULL; | ||
118 | |||
119 | assert_spin_locked(&event->lock); | ||
120 | |||
121 | list_for_each_entry(lpriv, &event->private_data_list, event_list) { | ||
122 | if (lpriv->group == group) { | ||
123 | priv = lpriv; | ||
124 | list_del(&priv->event_list); | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | return priv; | ||
129 | } | ||
130 | |||
131 | /* | ||
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. | ||
110 | */ | 134 | */ |
111 | static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) | 135 | static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) |
112 | { | 136 | { |
@@ -134,13 +158,17 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new | |||
134 | * event off the queue to deal with. If the event is successfully added to the | 158 | * event off the queue to deal with. If the event is successfully added to the |
135 | * group's notification queue, a reference is taken on event. | 159 | * group's notification queue, a reference is taken on event. |
136 | */ | 160 | */ |
137 | int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event) | 161 | int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, |
162 | struct fsnotify_event_private_data *priv) | ||
138 | { | 163 | { |
139 | struct fsnotify_event_holder *holder = NULL; | 164 | struct fsnotify_event_holder *holder = NULL; |
140 | struct list_head *list = &group->notification_list; | 165 | struct list_head *list = &group->notification_list; |
141 | struct fsnotify_event_holder *last_holder; | 166 | struct fsnotify_event_holder *last_holder; |
142 | struct fsnotify_event *last_event; | 167 | struct fsnotify_event *last_event; |
143 | 168 | ||
169 | /* easy to tell if priv was attached to the event */ | ||
170 | INIT_LIST_HEAD(&priv->event_list); | ||
171 | |||
144 | /* | 172 | /* |
145 | * There is one fsnotify_event_holder embedded inside each fsnotify_event. | 173 | * There is one fsnotify_event_holder embedded inside each fsnotify_event. |
146 | * Check if we expect to be able to use that holder. If not alloc a new | 174 | * Check if we expect to be able to use that holder. If not alloc a new |
@@ -158,8 +186,11 @@ alloc_holder: | |||
158 | 186 | ||
159 | mutex_lock(&group->notification_mutex); | 187 | mutex_lock(&group->notification_mutex); |
160 | 188 | ||
161 | if (group->q_len >= group->max_events) | 189 | if (group->q_len >= group->max_events) { |
162 | event = &q_overflow_event; | 190 | event = &q_overflow_event; |
191 | /* sorry, no private data on the overflow event */ | ||
192 | priv = NULL; | ||
193 | } | ||
163 | 194 | ||
164 | spin_lock(&event->lock); | 195 | spin_lock(&event->lock); |
165 | 196 | ||
@@ -183,7 +214,7 @@ alloc_holder: | |||
183 | mutex_unlock(&group->notification_mutex); | 214 | mutex_unlock(&group->notification_mutex); |
184 | if (holder != &event->holder) | 215 | if (holder != &event->holder) |
185 | fsnotify_destroy_event_holder(holder); | 216 | fsnotify_destroy_event_holder(holder); |
186 | return 0; | 217 | return -EEXIST; |
187 | } | 218 | } |
188 | } | 219 | } |
189 | 220 | ||
@@ -192,6 +223,8 @@ alloc_holder: | |||
192 | 223 | ||
193 | fsnotify_get_event(event); | 224 | fsnotify_get_event(event); |
194 | list_add_tail(&holder->event_list, list); | 225 | list_add_tail(&holder->event_list, list); |
226 | if (priv) | ||
227 | list_add_tail(&priv->event_list, &event->private_data_list); | ||
195 | spin_unlock(&event->lock); | 228 | spin_unlock(&event->lock); |
196 | mutex_unlock(&group->notification_mutex); | 229 | mutex_unlock(&group->notification_mutex); |
197 | 230 | ||
@@ -252,10 +285,19 @@ struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group) | |||
252 | void fsnotify_flush_notify(struct fsnotify_group *group) | 285 | void fsnotify_flush_notify(struct fsnotify_group *group) |
253 | { | 286 | { |
254 | struct fsnotify_event *event; | 287 | struct fsnotify_event *event; |
288 | struct fsnotify_event_private_data *priv; | ||
255 | 289 | ||
256 | mutex_lock(&group->notification_mutex); | 290 | mutex_lock(&group->notification_mutex); |
257 | while (!fsnotify_notify_queue_is_empty(group)) { | 291 | while (!fsnotify_notify_queue_is_empty(group)) { |
258 | event = fsnotify_remove_notify_event(group); | 292 | event = fsnotify_remove_notify_event(group); |
293 | /* if they don't implement free_event_priv they better not have attached any */ | ||
294 | if (group->ops->free_event_priv) { | ||
295 | spin_lock(&event->lock); | ||
296 | priv = fsnotify_remove_priv_from_event(group, event); | ||
297 | spin_unlock(&event->lock); | ||
298 | if (priv) | ||
299 | group->ops->free_event_priv(priv); | ||
300 | } | ||
259 | fsnotify_put_event(event); /* matches fsnotify_add_notify_event */ | 301 | fsnotify_put_event(event); /* matches fsnotify_add_notify_event */ |
260 | } | 302 | } |
261 | mutex_unlock(&group->notification_mutex); | 303 | mutex_unlock(&group->notification_mutex); |
@@ -274,6 +316,8 @@ static void initialize_event(struct fsnotify_event *event) | |||
274 | event->inode = NULL; | 316 | event->inode = NULL; |
275 | event->data_type = FSNOTIFY_EVENT_NONE; | 317 | event->data_type = FSNOTIFY_EVENT_NONE; |
276 | 318 | ||
319 | INIT_LIST_HEAD(&event->private_data_list); | ||
320 | |||
277 | event->to_tell = NULL; | 321 | event->to_tell = NULL; |
278 | 322 | ||
279 | event->file_name = NULL; | 323 | event->file_name = NULL; |
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index b78b5573d227..efdf9e442d86 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h | |||
@@ -63,6 +63,7 @@ | |||
63 | struct fsnotify_group; | 63 | struct fsnotify_group; |
64 | struct fsnotify_event; | 64 | struct fsnotify_event; |
65 | struct fsnotify_mark_entry; | 65 | struct fsnotify_mark_entry; |
66 | struct fsnotify_event_private_data; | ||
66 | 67 | ||
67 | /* | 68 | /* |
68 | * Each group much define these ops. The fsnotify infrastructure will call | 69 | * Each group much define these ops. The fsnotify infrastructure will call |
@@ -81,6 +82,7 @@ struct fsnotify_ops { | |||
81 | int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); | 82 | int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); |
82 | void (*free_group_priv)(struct fsnotify_group *group); | 83 | void (*free_group_priv)(struct fsnotify_group *group); |
83 | void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group); | 84 | void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group); |
85 | void (*free_event_priv)(struct fsnotify_event_private_data *priv); | ||
84 | }; | 86 | }; |
85 | 87 | ||
86 | /* | 88 | /* |
@@ -158,6 +160,15 @@ struct fsnotify_event_holder { | |||
158 | }; | 160 | }; |
159 | 161 | ||
160 | /* | 162 | /* |
163 | * Inotify needs to tack data onto an event. This struct lets us later find the | ||
164 | * correct private data of the correct group. | ||
165 | */ | ||
166 | struct fsnotify_event_private_data { | ||
167 | struct fsnotify_group *group; | ||
168 | struct list_head event_list; | ||
169 | }; | ||
170 | |||
171 | /* | ||
161 | * all of the information about the original object we want to now send to | 172 | * all of the information about the original object we want to now send to |
162 | * a group. If you want to carry more info from the accessing task to the | 173 | * a group. If you want to carry more info from the accessing task to the |
163 | * listener this structure is where you need to be adding fields. | 174 | * listener this structure is where you need to be adding fields. |
@@ -196,6 +207,8 @@ struct fsnotify_event { | |||
196 | u32 sync_cookie; /* used to corrolate events, namely inotify mv events */ | 207 | u32 sync_cookie; /* used to corrolate events, namely inotify mv events */ |
197 | char *file_name; | 208 | char *file_name; |
198 | size_t name_len; | 209 | size_t name_len; |
210 | |||
211 | struct list_head private_data_list; /* groups can store private data here */ | ||
199 | }; | 212 | }; |
200 | 213 | ||
201 | /* | 214 | /* |
@@ -294,17 +307,18 @@ extern void fsnotify_put_group(struct fsnotify_group *group); | |||
294 | /* take a reference to an event */ | 307 | /* take a reference to an event */ |
295 | extern void fsnotify_get_event(struct fsnotify_event *event); | 308 | extern void fsnotify_get_event(struct fsnotify_event *event); |
296 | extern void fsnotify_put_event(struct fsnotify_event *event); | 309 | extern void fsnotify_put_event(struct fsnotify_event *event); |
297 | /* find private data previously attached to an event */ | 310 | /* find private data previously attached to an event and unlink it */ |
298 | extern struct fsnotify_event_private_data *fsnotify_get_priv_from_event(struct fsnotify_group *group, | 311 | extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnotify_group *group, |
299 | struct fsnotify_event *event); | 312 | struct fsnotify_event *event); |
300 | 313 | ||
301 | /* attach the event to the group notification queue */ | 314 | /* attach the event to the group notification queue */ |
302 | extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event); | 315 | extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, |
316 | struct fsnotify_event_private_data *priv); | ||
303 | /* true if the group notification queue is empty */ | 317 | /* true if the group notification queue is empty */ |
304 | extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); | 318 | extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); |
305 | /* return, but do not dequeue the first event on the notification queue */ | 319 | /* return, but do not dequeue the first event on the notification queue */ |
306 | extern struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group); | 320 | extern struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group); |
307 | /* reutnr AND dequeue the first event on the notification queue */ | 321 | /* return AND dequeue the first event on the notification queue */ |
308 | extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group); | 322 | extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group); |
309 | 323 | ||
310 | /* functions used to manipulate the marks attached to inodes */ | 324 | /* functions used to manipulate the marks attached to inodes */ |