/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- pvfs_notify_send_next
- pvfs_notify_send
- pvfs_notify_destructor
- pvfs_notify_callback
- pvfs_notify_setup
- pvfs_notify_end
- pvfs_notify
1 /*
2 Unix SMB/CIFS implementation.
3
4 POSIX NTVFS backend - notify
5
6 Copyright (C) Andrew Tridgell 2006
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "vfs_posix.h"
24 #include "lib/messaging/irpc.h"
25 #include "messaging/messaging.h"
26 #include "../lib/util/dlinklist.h"
27 #include "lib/events/events.h"
28
29 /* pending notifies buffer, hung off struct pvfs_file for open directories
30 that have used change notify */
31 struct pvfs_notify_buffer {
32 struct pvfs_file *f;
33 uint32_t num_changes;
34 struct notify_changes *changes;
35 uint32_t max_buffer_size;
36 uint32_t current_buffer_size;
37
38 /* a list of requests waiting for events on this handle */
39 struct notify_pending {
40 struct notify_pending *next, *prev;
41 struct ntvfs_request *req;
42 union smb_notify *info;
43 } *pending;
44 };
45
46 /*
47 send a notify on the next event run.
48 */
49 static void pvfs_notify_send_next(struct tevent_context *ev, struct tevent_timer *te,
/* [<][>][^][v][top][bottom][index][help] */
50 struct timeval t, void *ptr)
51 {
52 struct ntvfs_request *req = talloc_get_type(ptr, struct ntvfs_request);
53 req->async_states->send_fn(req);
54 }
55
56
57 /*
58 send a reply to a pending notify request
59 */
60 static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer,
/* [<][>][^][v][top][bottom][index][help] */
61 NTSTATUS status, bool immediate)
62 {
63 struct notify_pending *pending = notify_buffer->pending;
64 struct ntvfs_request *req;
65 union smb_notify *info;
66
67 if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size &&
68 notify_buffer->num_changes != 0) {
69 /* on buffer overflow return no changes and destroys the notify buffer */
70 notify_buffer->num_changes = 0;
71 while (notify_buffer->pending) {
72 pvfs_notify_send(notify_buffer, NT_STATUS_OK, immediate);
73 }
74 talloc_free(notify_buffer);
75 return;
76 }
77
78 /* see if there is anyone waiting */
79 if (notify_buffer->pending == NULL) {
80 return;
81 }
82
83 DLIST_REMOVE(notify_buffer->pending, pending);
84
85 req = pending->req;
86 info = pending->info;
87
88 info->nttrans.out.num_changes = notify_buffer->num_changes;
89 info->nttrans.out.changes = talloc_steal(req, notify_buffer->changes);
90 notify_buffer->num_changes = 0;
91 notify_buffer->changes = NULL;
92 notify_buffer->current_buffer_size = 0;
93
94 talloc_free(pending);
95
96 if (info->nttrans.out.num_changes != 0) {
97 status = NT_STATUS_OK;
98 }
99
100 req->async_states->status = status;
101
102 if (immediate) {
103 req->async_states->send_fn(req);
104 return;
105 }
106
107 /* we can't call pvfs_notify_send() directly here, as that
108 would free the request, and the ntvfs modules above us
109 could use it, so call it on the next event */
110 event_add_timed(req->ctx->event_ctx,
111 req, timeval_zero(), pvfs_notify_send_next, req);
112 }
113
114 /*
115 destroy a notify buffer. Called when the handle is closed
116 */
117 static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
/* [<][>][^][v][top][bottom][index][help] */
118 {
119 notify_remove(n->f->pvfs->notify_context, n);
120 n->f->notify_buffer = NULL;
121 pvfs_notify_send(n, NT_STATUS_OK, true);
122 return 0;
123 }
124
125
126 /*
127 called when a async notify event comes in
128 */
129 static void pvfs_notify_callback(void *private_data, const struct notify_event *ev)
/* [<][>][^][v][top][bottom][index][help] */
130 {
131 struct pvfs_notify_buffer *n = talloc_get_type(private_data, struct pvfs_notify_buffer);
132 size_t len;
133 struct notify_changes *n2;
134 char *new_path;
135
136 n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
137 if (n2 == NULL) {
138 /* nothing much we can do for this */
139 return;
140 }
141 n->changes = n2;
142
143 new_path = talloc_strdup(n->changes, ev->path);
144 if (new_path == NULL) {
145 return;
146 }
147 string_replace(new_path, '/', '\\');
148
149 n->changes[n->num_changes].action = ev->action;
150 n->changes[n->num_changes].name.s = new_path;
151 n->num_changes++;
152
153 /*
154 work out how much room this will take in the buffer
155 */
156 len = 12 + strlen_m(ev->path)*2;
157 if (len & 3) {
158 len += 4 - (len & 3);
159 }
160 n->current_buffer_size += len;
161
162 /* send what we have, unless its the first part of a rename */
163 if (ev->action != NOTIFY_ACTION_OLD_NAME) {
164 pvfs_notify_send(n, NT_STATUS_OK, true);
165 }
166 }
167
168 /*
169 setup a notify buffer on a directory handle
170 */
171 static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f,
/* [<][>][^][v][top][bottom][index][help] */
172 uint32_t buffer_size, uint32_t filter, bool recursive)
173 {
174 NTSTATUS status;
175 struct notify_entry e;
176
177 f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
178 NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
179
180 f->notify_buffer->max_buffer_size = buffer_size;
181 f->notify_buffer->f = f;
182
183 e.filter = filter;
184 e.path = f->handle->name->full_name;
185 if (recursive) {
186 e.subdir_filter = filter;
187 } else {
188 e.subdir_filter = 0;
189 }
190
191 status = notify_add(pvfs->notify_context, &e,
192 pvfs_notify_callback, f->notify_buffer);
193 NT_STATUS_NOT_OK_RETURN(status);
194
195 talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
196
197 return NT_STATUS_OK;
198 }
199
200 /*
201 called from the pvfs_wait code when either an event has come in, or
202 the notify request has been cancelled
203 */
204 static void pvfs_notify_end(void *private_data, enum pvfs_wait_notice reason)
/* [<][>][^][v][top][bottom][index][help] */
205 {
206 struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private_data,
207 struct pvfs_notify_buffer);
208 if (reason == PVFS_WAIT_CANCEL) {
209 pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, false);
210 } else {
211 pvfs_notify_send(notify_buffer, NT_STATUS_OK, true);
212 }
213 }
214
215 /* change notify request - always async. This request blocks until the
216 event buffer is non-empty */
217 NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
/* [<][>][^][v][top][bottom][index][help] */
218 struct ntvfs_request *req,
219 union smb_notify *info)
220 {
221 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
222 struct pvfs_state);
223 struct pvfs_file *f;
224 NTSTATUS status;
225 struct notify_pending *pending;
226
227 if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
228 return ntvfs_map_notify(ntvfs, req, info);
229 }
230
231 f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
232 if (!f) {
233 return NT_STATUS_INVALID_HANDLE;
234 }
235
236 /* this request doesn't make sense unless its async */
237 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
238 return NT_STATUS_INVALID_PARAMETER;
239 }
240
241 /* its only valid for directories */
242 if (f->handle->fd != -1) {
243 return NT_STATUS_INVALID_PARAMETER;
244 }
245
246 /* if the handle doesn't currently have a notify buffer then
247 create one */
248 if (f->notify_buffer == NULL) {
249 status = pvfs_notify_setup(pvfs, f,
250 info->nttrans.in.buffer_size,
251 info->nttrans.in.completion_filter,
252 info->nttrans.in.recursive);
253 NT_STATUS_NOT_OK_RETURN(status);
254 }
255
256 /* we update the max_buffer_size on each call, but we do not
257 update the recursive flag or filter */
258 f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
259
260 pending = talloc(f->notify_buffer, struct notify_pending);
261 NT_STATUS_HAVE_NO_MEMORY(pending);
262
263 pending->req = talloc_reference(pending, req);
264 NT_STATUS_HAVE_NO_MEMORY(pending->req);
265 pending->info = info;
266
267 DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
268
269 /* if the buffer is empty then start waiting */
270 if (f->notify_buffer->num_changes == 0) {
271 struct pvfs_wait *wait_handle;
272 wait_handle = pvfs_wait_message(pvfs, req, -1,
273 timeval_zero(),
274 pvfs_notify_end,
275 f->notify_buffer);
276 NT_STATUS_HAVE_NO_MEMORY(wait_handle);
277 talloc_steal(req, wait_handle);
278 return NT_STATUS_OK;
279 }
280
281 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
282 pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, false);
283
284 return NT_STATUS_OK;
285 }