diff options
Diffstat (limited to 'fs/notify/fanotify/fanotify.c')
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 21 |
1 files changed, 16 insertions, 5 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 95a330d2f8a1..4feed8601e29 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c | |||
@@ -27,6 +27,7 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) | |||
27 | return false; | 27 | return false; |
28 | } | 28 | } |
29 | 29 | ||
30 | /* Note, if we return an event in *arg that a reference is being held... */ | ||
30 | static int fanotify_merge(struct list_head *list, | 31 | static int fanotify_merge(struct list_head *list, |
31 | struct fsnotify_event *event, | 32 | struct fsnotify_event *event, |
32 | void **arg) | 33 | void **arg) |
@@ -34,17 +35,22 @@ static int fanotify_merge(struct list_head *list, | |||
34 | struct fsnotify_event_holder *test_holder; | 35 | struct fsnotify_event_holder *test_holder; |
35 | struct fsnotify_event *test_event; | 36 | struct fsnotify_event *test_event; |
36 | struct fsnotify_event *new_event; | 37 | struct fsnotify_event *new_event; |
38 | struct fsnotify_event **return_event = (struct fsnotify_event **)arg; | ||
37 | int ret = 0; | 39 | int ret = 0; |
38 | 40 | ||
39 | pr_debug("%s: list=%p event=%p\n", __func__, list, event); | 41 | pr_debug("%s: list=%p event=%p\n", __func__, list, event); |
40 | 42 | ||
43 | *return_event = NULL; | ||
44 | |||
41 | /* and the list better be locked by something too! */ | 45 | /* and the list better be locked by something too! */ |
42 | 46 | ||
43 | list_for_each_entry_reverse(test_holder, list, event_list) { | 47 | list_for_each_entry_reverse(test_holder, list, event_list) { |
44 | test_event = test_holder->event; | 48 | test_event = test_holder->event; |
45 | if (should_merge(test_event, event)) { | 49 | if (should_merge(test_event, event)) { |
46 | ret = -EEXIST; | 50 | fsnotify_get_event(test_event); |
51 | *return_event = test_event; | ||
47 | 52 | ||
53 | ret = -EEXIST; | ||
48 | /* if they are exactly the same we are done */ | 54 | /* if they are exactly the same we are done */ |
49 | if (test_event->mask == event->mask) | 55 | if (test_event->mask == event->mask) |
50 | goto out; | 56 | goto out; |
@@ -66,11 +72,14 @@ static int fanotify_merge(struct list_head *list, | |||
66 | goto out; | 72 | goto out; |
67 | } | 73 | } |
68 | 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 | |||
69 | /* build new event and replace it on the list */ | 80 | /* build new event and replace it on the list */ |
70 | new_event->mask = (test_event->mask | event->mask); | 81 | new_event->mask = (test_event->mask | event->mask); |
71 | fsnotify_replace_event(test_holder, new_event); | 82 | fsnotify_replace_event(test_holder, new_event); |
72 | /* match ref from fsnotify_clone_event() */ | ||
73 | fsnotify_put_event(new_event); | ||
74 | 83 | ||
75 | break; | 84 | break; |
76 | } | 85 | } |
@@ -82,7 +91,7 @@ out: | |||
82 | static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) | 91 | static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) |
83 | { | 92 | { |
84 | int ret; | 93 | int ret; |
85 | 94 | struct fsnotify_event *used_event; | |
86 | 95 | ||
87 | BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); | 96 | BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); |
88 | BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); | 97 | BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); |
@@ -94,10 +103,12 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e | |||
94 | 103 | ||
95 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); | 104 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); |
96 | 105 | ||
97 | ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, NULL); | 106 | ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, (void **)&used_event); |
98 | /* -EEXIST means this event was merged with another, not that it was an error */ | 107 | /* -EEXIST means this event was merged with another, not that it was an error */ |
99 | if (ret == -EEXIST) | 108 | if (ret == -EEXIST) |
100 | ret = 0; | 109 | ret = 0; |
110 | if (used_event) | ||
111 | fsnotify_put_event(used_event); | ||
101 | return ret; | 112 | return ret; |
102 | } | 113 | } |
103 | 114 | ||