diff options
Diffstat (limited to 'fs/notify/fanotify')
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 103 |
1 files changed, 46 insertions, 57 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 | ||