aboutsummaryrefslogtreecommitdiffstats
path: root/fs/notify/fanotify
diff options
context:
space:
mode:
Diffstat (limited to 'fs/notify/fanotify')
-rw-r--r--fs/notify/fanotify/fanotify.c103
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! */
34static int fanotify_merge(struct list_head *list, 34static 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 }
90out: 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
124static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) 117static 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 **)&notify_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
158out:
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