diff options
author | Eric Paris <eparis@redhat.com> | 2010-07-28 10:18:37 -0400 |
---|---|---|
committer | Eric Paris <eparis@redhat.com> | 2010-07-28 10:18:50 -0400 |
commit | f70ab54cc6c3907b0727ba332b3976f80f3846d0 (patch) | |
tree | 2a22097325a668a0d08d4ea3354d0e6c95fddd86 | |
parent | 5ba08e2eeb06355f66ed62ae97bb87d145973a9a (diff) |
fsnotify: fsnotify_add_notify_event should return an event
Rather than the horrific void ** argument and such just to pass the
fanotify_merge event back to the caller of fsnotify_add_notify_event() have
those things return an event if it was different than the event suggusted to
be added.
Signed-off-by: Eric Paris <eparis@redhat.com>
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 103 | ||||
-rw-r--r-- | fs/notify/inotify/inotify_fsnotify.c | 28 | ||||
-rw-r--r-- | fs/notify/inotify/inotify_user.c | 11 | ||||
-rw-r--r-- | fs/notify/notification.c | 42 | ||||
-rw-r--r-- | include/linux/fsnotify_backend.h | 12 |
5 files changed, 101 insertions, 95 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index bbcfccd4a8ea..f3c40c0e2b86 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c | |||
@@ -30,65 +30,58 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) | |||
30 | return false; | 30 | return false; |
31 | } | 31 | } |
32 | 32 | ||
33 | /* Note, if we return an event in *arg that a reference is being held... */ | 33 | /* and the list better be locked by something too! */ |
34 | static int fanotify_merge(struct list_head *list, | 34 | static struct fsnotify_event *fanotify_merge(struct list_head *list, |
35 | struct fsnotify_event *event, | 35 | struct fsnotify_event *event) |
36 | void **arg) | ||
37 | { | 36 | { |
38 | struct fsnotify_event_holder *test_holder; | 37 | struct fsnotify_event_holder *test_holder; |
39 | struct fsnotify_event *test_event; | 38 | struct fsnotify_event *test_event = NULL; |
40 | struct fsnotify_event *new_event; | 39 | struct fsnotify_event *new_event; |
41 | struct fsnotify_event **return_event = (struct fsnotify_event **)arg; | ||
42 | int ret = 0; | ||
43 | 40 | ||
44 | pr_debug("%s: list=%p event=%p\n", __func__, list, event); | 41 | pr_debug("%s: list=%p event=%p\n", __func__, list, event); |
45 | 42 | ||
46 | *return_event = NULL; | ||
47 | |||
48 | /* and the list better be locked by something too! */ | ||
49 | 43 | ||
50 | list_for_each_entry_reverse(test_holder, list, event_list) { | 44 | list_for_each_entry_reverse(test_holder, list, event_list) { |
51 | test_event = test_holder->event; | 45 | if (should_merge(test_holder->event, event)) { |
52 | if (should_merge(test_event, event)) { | 46 | test_event = test_holder->event; |
53 | fsnotify_get_event(test_event); | ||
54 | *return_event = test_event; | ||
55 | |||
56 | ret = -EEXIST; | ||
57 | /* if they are exactly the same we are done */ | ||
58 | if (test_event->mask == event->mask) | ||
59 | goto out; | ||
60 | |||
61 | /* | ||
62 | * if the refcnt == 1 this is the only queue | ||
63 | * for this event and so we can update the mask | ||
64 | * in place. | ||
65 | */ | ||
66 | if (atomic_read(&test_event->refcnt) == 1) { | ||
67 | test_event->mask |= event->mask; | ||
68 | goto out; | ||
69 | } | ||
70 | |||
71 | /* can't allocate memory, merge was no possible */ | ||
72 | new_event = fsnotify_clone_event(test_event); | ||
73 | if (unlikely(!new_event)) { | ||
74 | ret = 0; | ||
75 | goto out; | ||
76 | } | ||
77 | |||
78 | /* we didn't return the test_event, so drop that ref */ | ||
79 | fsnotify_put_event(test_event); | ||
80 | /* the reference we return on new_event is from clone */ | ||
81 | *return_event = new_event; | ||
82 | |||
83 | /* build new event and replace it on the list */ | ||
84 | new_event->mask = (test_event->mask | event->mask); | ||
85 | fsnotify_replace_event(test_holder, new_event); | ||
86 | |||
87 | break; | 47 | break; |
88 | } | 48 | } |
89 | } | 49 | } |
90 | out: | 50 | |
91 | return ret; | 51 | if (!test_event) |
52 | return NULL; | ||
53 | |||
54 | fsnotify_get_event(test_event); | ||
55 | |||
56 | /* if they are exactly the same we are done */ | ||
57 | if (test_event->mask == event->mask) | ||
58 | return test_event; | ||
59 | |||
60 | /* | ||
61 | * if the refcnt == 2 this is the only queue | ||
62 | * for this event and so we can update the mask | ||
63 | * in place. | ||
64 | */ | ||
65 | if (atomic_read(&test_event->refcnt) == 2) { | ||
66 | test_event->mask |= event->mask; | ||
67 | return test_event; | ||
68 | } | ||
69 | |||
70 | new_event = fsnotify_clone_event(test_event); | ||
71 | |||
72 | /* done with test_event */ | ||
73 | fsnotify_put_event(test_event); | ||
74 | |||
75 | /* couldn't allocate memory, merge was not possible */ | ||
76 | if (unlikely(!new_event)) | ||
77 | return ERR_PTR(-ENOMEM); | ||
78 | |||
79 | /* build new event and replace it on the list */ | ||
80 | new_event->mask = (test_event->mask | event->mask); | ||
81 | fsnotify_replace_event(test_holder, new_event); | ||
82 | |||
83 | /* we hold a reference on new_event from clone_event */ | ||
84 | return new_event; | ||
92 | } | 85 | } |
93 | 86 | ||
94 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | 87 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS |
@@ -123,7 +116,7 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, | |||
123 | 116 | ||
124 | static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) | 117 | static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) |
125 | { | 118 | { |
126 | int ret; | 119 | int ret = 0; |
127 | struct fsnotify_event *notify_event = NULL; | 120 | struct fsnotify_event *notify_event = NULL; |
128 | 121 | ||
129 | BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); | 122 | BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); |
@@ -138,13 +131,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e | |||
138 | 131 | ||
139 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); | 132 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); |
140 | 133 | ||
141 | ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, | 134 | notify_event = fsnotify_add_notify_event(group, event, NULL, fanotify_merge); |
142 | (void **)¬ify_event); | 135 | if (IS_ERR(notify_event)) |
143 | /* -EEXIST means this event was merged with another, not that it was an error */ | 136 | return PTR_ERR(notify_event); |
144 | if (ret == -EEXIST) | ||
145 | ret = 0; | ||
146 | if (ret) | ||
147 | goto out; | ||
148 | 137 | ||
149 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | 138 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS |
150 | if (event->mask & FAN_ALL_PERM_EVENTS) { | 139 | if (event->mask & FAN_ALL_PERM_EVENTS) { |
@@ -155,9 +144,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e | |||
155 | } | 144 | } |
156 | #endif | 145 | #endif |
157 | 146 | ||
158 | out: | ||
159 | if (notify_event) | 147 | if (notify_event) |
160 | fsnotify_put_event(notify_event); | 148 | fsnotify_put_event(notify_event); |
149 | |||
161 | return ret; | 150 | return ret; |
162 | } | 151 | } |
163 | 152 | ||
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 906b72761b17..73a1106b3542 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c | |||
@@ -68,13 +68,11 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new | |||
68 | return false; | 68 | return false; |
69 | } | 69 | } |
70 | 70 | ||
71 | static int inotify_merge(struct list_head *list, | 71 | static struct fsnotify_event *inotify_merge(struct list_head *list, |
72 | struct fsnotify_event *event, | 72 | struct fsnotify_event *event) |
73 | void **arg) | ||
74 | { | 73 | { |
75 | struct fsnotify_event_holder *last_holder; | 74 | struct fsnotify_event_holder *last_holder; |
76 | struct fsnotify_event *last_event; | 75 | struct fsnotify_event *last_event; |
77 | int ret = 0; | ||
78 | 76 | ||
79 | /* and the list better be locked by something too */ | 77 | /* and the list better be locked by something too */ |
80 | spin_lock(&event->lock); | 78 | spin_lock(&event->lock); |
@@ -82,11 +80,13 @@ static int inotify_merge(struct list_head *list, | |||
82 | last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); | 80 | last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); |
83 | last_event = last_holder->event; | 81 | last_event = last_holder->event; |
84 | if (event_compare(last_event, event)) | 82 | if (event_compare(last_event, event)) |
85 | ret = -EEXIST; | 83 | fsnotify_get_event(last_event); |
84 | else | ||
85 | last_event = NULL; | ||
86 | 86 | ||
87 | spin_unlock(&event->lock); | 87 | spin_unlock(&event->lock); |
88 | 88 | ||
89 | return ret; | 89 | return last_event; |
90 | } | 90 | } |
91 | 91 | ||
92 | static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) | 92 | static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) |
@@ -96,7 +96,8 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev | |||
96 | struct inode *to_tell; | 96 | struct inode *to_tell; |
97 | struct inotify_event_private_data *event_priv; | 97 | struct inotify_event_private_data *event_priv; |
98 | struct fsnotify_event_private_data *fsn_event_priv; | 98 | struct fsnotify_event_private_data *fsn_event_priv; |
99 | int wd, ret; | 99 | struct fsnotify_event *added_event; |
100 | int wd, ret = 0; | ||
100 | 101 | ||
101 | pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, | 102 | pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, |
102 | event, event->to_tell, event->mask); | 103 | event, event->to_tell, event->mask); |
@@ -120,14 +121,13 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev | |||
120 | fsn_event_priv->group = group; | 121 | fsn_event_priv->group = group; |
121 | event_priv->wd = wd; | 122 | event_priv->wd = wd; |
122 | 123 | ||
123 | ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge, NULL); | 124 | added_event = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); |
124 | if (ret) { | 125 | if (added_event) { |
125 | inotify_free_event_priv(fsn_event_priv); | 126 | inotify_free_event_priv(fsn_event_priv); |
126 | /* EEXIST says we tail matched, EOVERFLOW isn't something | 127 | if (!IS_ERR(added_event)) |
127 | * to report up the stack. */ | 128 | fsnotify_put_event(added_event); |
128 | if ((ret == -EEXIST) || | 129 | else |
129 | (ret == -EOVERFLOW)) | 130 | ret = PTR_ERR(added_event); |
130 | ret = 0; | ||
131 | } | 131 | } |
132 | 132 | ||
133 | if (fsn_mark->mask & IN_ONESHOT) | 133 | if (fsn_mark->mask & IN_ONESHOT) |
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 1068e1ca9cb0..a4cd227c4c76 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
@@ -516,7 +516,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, | |||
516 | struct fsnotify_group *group) | 516 | struct fsnotify_group *group) |
517 | { | 517 | { |
518 | struct inotify_inode_mark *i_mark; | 518 | struct inotify_inode_mark *i_mark; |
519 | struct fsnotify_event *ignored_event; | 519 | struct fsnotify_event *ignored_event, *notify_event; |
520 | struct inotify_event_private_data *event_priv; | 520 | struct inotify_event_private_data *event_priv; |
521 | struct fsnotify_event_private_data *fsn_event_priv; | 521 | struct fsnotify_event_private_data *fsn_event_priv; |
522 | int ret; | 522 | int ret; |
@@ -538,9 +538,14 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, | |||
538 | fsn_event_priv->group = group; | 538 | fsn_event_priv->group = group; |
539 | event_priv->wd = i_mark->wd; | 539 | event_priv->wd = i_mark->wd; |
540 | 540 | ||
541 | ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL, NULL); | 541 | notify_event = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); |
542 | if (ret) | 542 | if (notify_event) { |
543 | if (IS_ERR(notify_event)) | ||
544 | ret = PTR_ERR(notify_event); | ||
545 | else | ||
546 | fsnotify_put_event(notify_event); | ||
543 | inotify_free_event_priv(fsn_event_priv); | 547 | inotify_free_event_priv(fsn_event_priv); |
548 | } | ||
544 | 549 | ||
545 | skip_send_ignore: | 550 | skip_send_ignore: |
546 | 551 | ||
diff --git a/fs/notify/notification.c b/fs/notify/notification.c index e6dde25fb99b..f39260f8f865 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c | |||
@@ -137,16 +137,14 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot | |||
137 | * event off the queue to deal with. If the event is successfully added to the | 137 | * event off the queue to deal with. If the event is successfully added to the |
138 | * group's notification queue, a reference is taken on event. | 138 | * group's notification queue, a reference is taken on event. |
139 | */ | 139 | */ |
140 | int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, | 140 | struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, |
141 | struct fsnotify_event_private_data *priv, | 141 | struct fsnotify_event_private_data *priv, |
142 | int (*merge)(struct list_head *, | 142 | struct fsnotify_event *(*merge)(struct list_head *, |
143 | struct fsnotify_event *, | 143 | struct fsnotify_event *)) |
144 | void **arg), | ||
145 | void **arg) | ||
146 | { | 144 | { |
145 | struct fsnotify_event *return_event = NULL; | ||
147 | struct fsnotify_event_holder *holder = NULL; | 146 | struct fsnotify_event_holder *holder = NULL; |
148 | struct list_head *list = &group->notification_list; | 147 | struct list_head *list = &group->notification_list; |
149 | int rc = 0; | ||
150 | 148 | ||
151 | pr_debug("%s: group=%p event=%p priv=%p\n", __func__, group, event, priv); | 149 | pr_debug("%s: group=%p event=%p priv=%p\n", __func__, group, event, priv); |
152 | 150 | ||
@@ -162,27 +160,37 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even | |||
162 | alloc_holder: | 160 | alloc_holder: |
163 | holder = fsnotify_alloc_event_holder(); | 161 | holder = fsnotify_alloc_event_holder(); |
164 | if (!holder) | 162 | if (!holder) |
165 | return -ENOMEM; | 163 | return ERR_PTR(-ENOMEM); |
166 | } | 164 | } |
167 | 165 | ||
168 | mutex_lock(&group->notification_mutex); | 166 | mutex_lock(&group->notification_mutex); |
169 | 167 | ||
170 | if (group->q_len >= group->max_events) { | 168 | if (group->q_len >= group->max_events) { |
171 | event = q_overflow_event; | 169 | event = q_overflow_event; |
172 | rc = -EOVERFLOW; | 170 | |
171 | /* | ||
172 | * we need to return the overflow event | ||
173 | * which means we need a ref | ||
174 | */ | ||
175 | fsnotify_get_event(event); | ||
176 | return_event = event; | ||
177 | |||
173 | /* sorry, no private data on the overflow event */ | 178 | /* sorry, no private data on the overflow event */ |
174 | priv = NULL; | 179 | priv = NULL; |
175 | } | 180 | } |
176 | 181 | ||
177 | if (!list_empty(list) && merge) { | 182 | if (!list_empty(list) && merge) { |
178 | int ret; | 183 | struct fsnotify_event *tmp; |
179 | 184 | ||
180 | ret = merge(list, event, arg); | 185 | tmp = merge(list, event); |
181 | if (ret) { | 186 | if (tmp) { |
182 | mutex_unlock(&group->notification_mutex); | 187 | mutex_unlock(&group->notification_mutex); |
188 | |||
189 | if (return_event) | ||
190 | fsnotify_put_event(return_event); | ||
183 | if (holder != &event->holder) | 191 | if (holder != &event->holder) |
184 | fsnotify_destroy_event_holder(holder); | 192 | fsnotify_destroy_event_holder(holder); |
185 | return ret; | 193 | return tmp; |
186 | } | 194 | } |
187 | } | 195 | } |
188 | 196 | ||
@@ -197,6 +205,12 @@ alloc_holder: | |||
197 | * event holder was used, go back and get a new one */ | 205 | * event holder was used, go back and get a new one */ |
198 | spin_unlock(&event->lock); | 206 | spin_unlock(&event->lock); |
199 | mutex_unlock(&group->notification_mutex); | 207 | mutex_unlock(&group->notification_mutex); |
208 | |||
209 | if (return_event) { | ||
210 | fsnotify_put_event(return_event); | ||
211 | return_event = NULL; | ||
212 | } | ||
213 | |||
200 | goto alloc_holder; | 214 | goto alloc_holder; |
201 | } | 215 | } |
202 | 216 | ||
@@ -211,7 +225,7 @@ alloc_holder: | |||
211 | mutex_unlock(&group->notification_mutex); | 225 | mutex_unlock(&group->notification_mutex); |
212 | 226 | ||
213 | wake_up(&group->notification_waitq); | 227 | wake_up(&group->notification_waitq); |
214 | return rc; | 228 | return return_event; |
215 | } | 229 | } |
216 | 230 | ||
217 | /* | 231 | /* |
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index a83859d7d36e..564b5ea4a831 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h | |||
@@ -379,13 +379,11 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc | |||
379 | struct fsnotify_event *event); | 379 | struct fsnotify_event *event); |
380 | 380 | ||
381 | /* attach the event to the group notification queue */ | 381 | /* attach the event to the group notification queue */ |
382 | extern int fsnotify_add_notify_event(struct fsnotify_group *group, | 382 | extern struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, |
383 | struct fsnotify_event *event, | 383 | struct fsnotify_event *event, |
384 | struct fsnotify_event_private_data *priv, | 384 | struct fsnotify_event_private_data *priv, |
385 | int (*merge)(struct list_head *, | 385 | struct fsnotify_event *(*merge)(struct list_head *, |
386 | struct fsnotify_event *, | 386 | struct fsnotify_event *)); |
387 | void **), | ||
388 | void **arg); | ||
389 | /* true if the group notification queue is empty */ | 387 | /* true if the group notification queue is empty */ |
390 | extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); | 388 | extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); |
391 | /* return, but do not dequeue the first event on the notification queue */ | 389 | /* return, but do not dequeue the first event on the notification queue */ |