Line data Source code
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 : bool overflowed;
38 :
39 : /* a list of requests waiting for events on this handle */
40 : struct notify_pending {
41 : struct notify_pending *next, *prev;
42 : struct ntvfs_request *req;
43 : union smb_notify *info;
44 : } *pending;
45 : };
46 :
47 : /*
48 : send a notify on the next event run.
49 : */
50 519 : static void pvfs_notify_send_next(struct tevent_context *ev, struct tevent_timer *te,
51 : struct timeval t, void *ptr)
52 : {
53 519 : struct ntvfs_request *req = talloc_get_type(ptr, struct ntvfs_request);
54 519 : req->async_states->send_fn(req);
55 519 : }
56 :
57 :
58 : /*
59 : send a reply to a pending notify request
60 : */
61 1389 : static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer,
62 : NTSTATUS status, bool immediate)
63 : {
64 1389 : struct notify_pending *pending = notify_buffer->pending;
65 0 : struct ntvfs_request *req;
66 0 : union smb_notify *info;
67 :
68 1389 : if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size &&
69 2 : notify_buffer->num_changes != 0) {
70 : /* on buffer overflow return no changes and destroys the notify buffer */
71 1 : notify_buffer->num_changes = 0;
72 1 : while (notify_buffer->pending) {
73 0 : pvfs_notify_send(notify_buffer, NT_STATUS_OK, immediate);
74 : }
75 1 : notify_buffer->overflowed = true;
76 1 : return;
77 : }
78 :
79 : /* see if there is anyone waiting */
80 1388 : if (notify_buffer->pending == NULL) {
81 832 : return;
82 : }
83 :
84 556 : DLIST_REMOVE(notify_buffer->pending, pending);
85 :
86 556 : req = pending->req;
87 556 : info = pending->info;
88 :
89 556 : info->nttrans.out.num_changes = notify_buffer->num_changes;
90 556 : info->nttrans.out.changes = talloc_steal(req, notify_buffer->changes);
91 556 : notify_buffer->num_changes = 0;
92 556 : notify_buffer->overflowed = false;
93 556 : notify_buffer->changes = NULL;
94 556 : notify_buffer->current_buffer_size = 0;
95 :
96 556 : talloc_free(pending);
97 :
98 556 : if (info->nttrans.out.num_changes != 0) {
99 58 : status = NT_STATUS_OK;
100 : }
101 :
102 556 : req->async_states->status = status;
103 :
104 556 : if (immediate) {
105 37 : req->async_states->send_fn(req);
106 37 : return;
107 : }
108 :
109 : /* we can't call pvfs_notify_send() directly here, as that
110 : would free the request, and the ntvfs modules above us
111 : could use it, so call it on the next event */
112 519 : tevent_add_timer(req->ctx->event_ctx,
113 : req, timeval_zero(), pvfs_notify_send_next, req);
114 : }
115 :
116 : /*
117 : destroy a notify buffer. Called when the handle is closed
118 : */
119 513 : static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
120 : {
121 513 : notify_remove(n->f->pvfs->notify_context, n);
122 513 : n->f->notify_buffer = NULL;
123 513 : pvfs_notify_send(n, NT_STATUS_OK, true);
124 513 : return 0;
125 : }
126 :
127 :
128 : /*
129 : called when a async notify event comes in
130 : */
131 429 : static void pvfs_notify_callback(void *private_data, const struct notify_event *ev)
132 : {
133 429 : struct pvfs_notify_buffer *n = talloc_get_type(private_data, struct pvfs_notify_buffer);
134 0 : size_t len;
135 0 : struct notify_changes *n2;
136 0 : char *new_path;
137 :
138 429 : if (n->overflowed) {
139 68 : return;
140 : }
141 :
142 361 : n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
143 361 : if (n2 == NULL) {
144 : /* nothing much we can do for this */
145 0 : return;
146 : }
147 361 : n->changes = n2;
148 :
149 361 : new_path = talloc_strdup(n->changes, ev->path);
150 361 : if (new_path == NULL) {
151 0 : return;
152 : }
153 361 : string_replace(new_path, '/', '\\');
154 :
155 361 : n->changes[n->num_changes].action = ev->action;
156 361 : n->changes[n->num_changes].name.s = new_path;
157 361 : n->num_changes++;
158 :
159 : /*
160 : work out how much room this will take in the buffer
161 : */
162 361 : len = 12 + strlen_m(ev->path)*2;
163 361 : if (len & 3) {
164 140 : len += 4 - (len & 3);
165 : }
166 361 : n->current_buffer_size += len;
167 :
168 : /* send what we have, unless its the first part of a rename */
169 361 : if (ev->action != NOTIFY_ACTION_OLD_NAME) {
170 357 : pvfs_notify_send(n, NT_STATUS_OK, true);
171 : }
172 : }
173 :
174 : /*
175 : setup a notify buffer on a directory handle
176 : */
177 513 : static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f,
178 : uint32_t buffer_size, uint32_t filter, bool recursive)
179 : {
180 0 : NTSTATUS status;
181 0 : struct notify_entry e;
182 :
183 : /* We may not fill in all the elements in this entry -
184 : * structure may in future be shared with Samba3 */
185 513 : ZERO_STRUCT(e);
186 :
187 : /* We may not fill in all the elements in this entry -
188 : * structure may in future be shared with Samba3 */
189 513 : ZERO_STRUCT(e);
190 :
191 513 : f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
192 513 : NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
193 :
194 513 : f->notify_buffer->max_buffer_size = buffer_size;
195 513 : f->notify_buffer->f = f;
196 :
197 513 : e.filter = filter;
198 513 : e.path = f->handle->name->full_name;
199 513 : if (recursive) {
200 508 : e.subdir_filter = filter;
201 : } else {
202 5 : e.subdir_filter = 0;
203 : }
204 :
205 513 : status = notify_add(pvfs->notify_context, &e,
206 513 : pvfs_notify_callback, f->notify_buffer);
207 513 : NT_STATUS_NOT_OK_RETURN(status);
208 :
209 513 : talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
210 :
211 513 : return NT_STATUS_OK;
212 : }
213 :
214 : /*
215 : called from the pvfs_wait code when either an event has come in, or
216 : the notify request has been cancelled
217 : */
218 492 : static void pvfs_notify_end(void *private_data, enum pvfs_wait_notice reason)
219 : {
220 492 : struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private_data,
221 : struct pvfs_notify_buffer);
222 492 : if (reason == PVFS_WAIT_CANCEL) {
223 492 : pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, false);
224 : } else {
225 0 : pvfs_notify_send(notify_buffer, NT_STATUS_OK, true);
226 : }
227 492 : }
228 :
229 : /* change notify request - always async. This request blocks until the
230 : event buffer is non-empty */
231 563 : NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
232 : struct ntvfs_request *req,
233 : union smb_notify *info)
234 : {
235 563 : struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
236 : struct pvfs_state);
237 0 : struct pvfs_file *f;
238 0 : NTSTATUS status;
239 0 : struct notify_pending *pending;
240 :
241 563 : if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
242 0 : return ntvfs_map_notify(ntvfs, req, info);
243 : }
244 :
245 563 : f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
246 563 : if (!f) {
247 0 : return NT_STATUS_INVALID_HANDLE;
248 : }
249 :
250 : /* this request doesn't make sense unless its async */
251 563 : if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
252 0 : return NT_STATUS_INVALID_PARAMETER;
253 : }
254 :
255 : /* its only valid for directories */
256 563 : if (f->handle->fd != -1) {
257 7 : return NT_STATUS_INVALID_PARAMETER;
258 : }
259 :
260 : /* if the handle doesn't currently have a notify buffer then
261 : create one */
262 556 : if (f->notify_buffer == NULL) {
263 513 : status = pvfs_notify_setup(pvfs, f,
264 : info->nttrans.in.buffer_size,
265 : info->nttrans.in.completion_filter,
266 513 : info->nttrans.in.recursive);
267 513 : NT_STATUS_NOT_OK_RETURN(status);
268 : }
269 :
270 : /* we update the max_buffer_size on each call, but we do not
271 : update the recursive flag or filter */
272 556 : f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
273 :
274 556 : pending = talloc(f->notify_buffer, struct notify_pending);
275 556 : NT_STATUS_HAVE_NO_MEMORY(pending);
276 :
277 556 : pending->req = talloc_reference(pending, req);
278 556 : NT_STATUS_HAVE_NO_MEMORY(pending->req);
279 556 : pending->info = info;
280 :
281 556 : DLIST_ADD_END(f->notify_buffer->pending, pending);
282 :
283 : /* if the buffer is empty then start waiting */
284 556 : if (f->notify_buffer->num_changes == 0 &&
285 530 : !f->notify_buffer->overflowed) {
286 0 : struct pvfs_wait *wait_handle;
287 529 : wait_handle = pvfs_wait_message(pvfs, req, -1,
288 : timeval_zero(),
289 : pvfs_notify_end,
290 529 : f->notify_buffer);
291 529 : NT_STATUS_HAVE_NO_MEMORY(wait_handle);
292 529 : talloc_steal(req, wait_handle);
293 529 : return NT_STATUS_OK;
294 : }
295 :
296 27 : req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
297 27 : pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, false);
298 :
299 27 : return NT_STATUS_OK;
300 : }
|