Commit e4aff117368cfdd3567ee41844d216d079b55173
1 parent
47882c6f51
Exists in
master
and in
7 other branches
fsnotify: allow groups to add private data to events
inotify needs per group information attached to events. This patch allows groups to attach private information and implements a callback so that information can be freed when an event is being destroyed. Signed-off-by: Eric Paris <eparis@redhat.com> Acked-by: Al Viro <viro@zeniv.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de>
Showing 3 changed files with 68 additions and 9 deletions Side-by-side Diff
fs/notify/dnotify/dnotify.c
fs/notify/notification.c
... | ... | @@ -90,6 +90,8 @@ |
90 | 90 | if (event->data_type == FSNOTIFY_EVENT_PATH) |
91 | 91 | path_put(&event->path); |
92 | 92 | |
93 | + BUG_ON(!list_empty(&event->private_data_list)); | |
94 | + | |
93 | 95 | kfree(event->file_name); |
94 | 96 | kmem_cache_free(fsnotify_event_cachep, event); |
95 | 97 | } |
96 | 98 | |
... | ... | @@ -106,8 +108,30 @@ |
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) | |
110 | 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. | |
134 | + */ | |
111 | 135 | static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) |
112 | 136 | { |
113 | 137 | if ((old->mask == new->mask) && |
114 | 138 | |
... | ... | @@ -134,13 +158,17 @@ |
134 | 158 | * event off the queue to deal with. If the event is successfully added to the |
135 | 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 | 164 | struct fsnotify_event_holder *holder = NULL; |
140 | 165 | struct list_head *list = &group->notification_list; |
141 | 166 | struct fsnotify_event_holder *last_holder; |
142 | 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 | 173 | * There is one fsnotify_event_holder embedded inside each fsnotify_event. |
146 | 174 | * Check if we expect to be able to use that holder. If not alloc a new |
147 | 175 | |
... | ... | @@ -158,8 +186,11 @@ |
158 | 186 | |
159 | 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 | 190 | event = &q_overflow_event; |
191 | + /* sorry, no private data on the overflow event */ | |
192 | + priv = NULL; | |
193 | + } | |
163 | 194 | |
164 | 195 | spin_lock(&event->lock); |
165 | 196 | |
... | ... | @@ -183,7 +214,7 @@ |
183 | 214 | mutex_unlock(&group->notification_mutex); |
184 | 215 | if (holder != &event->holder) |
185 | 216 | fsnotify_destroy_event_holder(holder); |
186 | - return 0; | |
217 | + return -EEXIST; | |
187 | 218 | } |
188 | 219 | } |
189 | 220 | |
... | ... | @@ -192,6 +223,8 @@ |
192 | 223 | |
193 | 224 | fsnotify_get_event(event); |
194 | 225 | list_add_tail(&holder->event_list, list); |
226 | + if (priv) | |
227 | + list_add_tail(&priv->event_list, &event->private_data_list); | |
195 | 228 | spin_unlock(&event->lock); |
196 | 229 | mutex_unlock(&group->notification_mutex); |
197 | 230 | |
198 | 231 | |
... | ... | @@ -252,10 +285,19 @@ |
252 | 285 | void fsnotify_flush_notify(struct fsnotify_group *group) |
253 | 286 | { |
254 | 287 | struct fsnotify_event *event; |
288 | + struct fsnotify_event_private_data *priv; | |
255 | 289 | |
256 | 290 | mutex_lock(&group->notification_mutex); |
257 | 291 | while (!fsnotify_notify_queue_is_empty(group)) { |
258 | 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 | 301 | fsnotify_put_event(event); /* matches fsnotify_add_notify_event */ |
260 | 302 | } |
261 | 303 | mutex_unlock(&group->notification_mutex); |
... | ... | @@ -273,6 +315,8 @@ |
273 | 315 | event->path.mnt = NULL; |
274 | 316 | event->inode = NULL; |
275 | 317 | event->data_type = FSNOTIFY_EVENT_NONE; |
318 | + | |
319 | + INIT_LIST_HEAD(&event->private_data_list); | |
276 | 320 | |
277 | 321 | event->to_tell = NULL; |
278 | 322 |
include/linux/fsnotify_backend.h
... | ... | @@ -63,6 +63,7 @@ |
63 | 63 | struct fsnotify_group; |
64 | 64 | struct fsnotify_event; |
65 | 65 | struct fsnotify_mark_entry; |
66 | +struct fsnotify_event_private_data; | |
66 | 67 | |
67 | 68 | /* |
68 | 69 | * Each group much define these ops. The fsnotify infrastructure will call |
... | ... | @@ -81,6 +82,7 @@ |
81 | 82 | int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); |
82 | 83 | void (*free_group_priv)(struct fsnotify_group *group); |
83 | 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 @@ |
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 | 172 | * all of the information about the original object we want to now send to |
162 | 173 | * a group. If you want to carry more info from the accessing task to the |
163 | 174 | * listener this structure is where you need to be adding fields. |
... | ... | @@ -196,6 +207,8 @@ |
196 | 207 | u32 sync_cookie; /* used to corrolate events, namely inotify mv events */ |
197 | 208 | char *file_name; |
198 | 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 | /* |
202 | 215 | |
203 | 216 | |
... | ... | @@ -294,17 +307,18 @@ |
294 | 307 | /* take a reference to an event */ |
295 | 308 | extern void fsnotify_get_event(struct fsnotify_event *event); |
296 | 309 | extern void fsnotify_put_event(struct fsnotify_event *event); |
297 | -/* find private data previously attached to an event */ | |
298 | -extern struct fsnotify_event_private_data *fsnotify_get_priv_from_event(struct fsnotify_group *group, | |
299 | - struct fsnotify_event *event); | |
310 | +/* find private data previously attached to an event and unlink it */ | |
311 | +extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnotify_group *group, | |
312 | + struct fsnotify_event *event); | |
300 | 313 | |
301 | 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 | 317 | /* true if the group notification queue is empty */ |
304 | 318 | extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); |
305 | 319 | /* return, but do not dequeue the first event on the notification queue */ |
306 | 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 | 322 | extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group); |
309 | 323 | |
310 | 324 | /* functions used to manipulate the marks attached to inodes */ |