source: trunk/server/source4/ntvfs/posix/pvfs_notify.c

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 8.4 KB
Line 
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 */
31struct 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*/
50static void pvfs_notify_send_next(struct tevent_context *ev, struct tevent_timer *te,
51 struct timeval t, void *ptr)
52{
53 struct ntvfs_request *req = talloc_get_type(ptr, struct ntvfs_request);
54 req->async_states->send_fn(req);
55}
56
57
58/*
59 send a reply to a pending notify request
60*/
61static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer,
62 NTSTATUS status, bool immediate)
63{
64 struct notify_pending *pending = notify_buffer->pending;
65 struct ntvfs_request *req;
66 union smb_notify *info;
67
68 if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size &&
69 notify_buffer->num_changes != 0) {
70 /* on buffer overflow return no changes and destroys the notify buffer */
71 notify_buffer->num_changes = 0;
72 while (notify_buffer->pending) {
73 pvfs_notify_send(notify_buffer, NT_STATUS_OK, immediate);
74 }
75 notify_buffer->overflowed = true;
76 return;
77 }
78
79 /* see if there is anyone waiting */
80 if (notify_buffer->pending == NULL) {
81 return;
82 }
83
84 DLIST_REMOVE(notify_buffer->pending, pending);
85
86 req = pending->req;
87 info = pending->info;
88
89 info->nttrans.out.num_changes = notify_buffer->num_changes;
90 info->nttrans.out.changes = talloc_steal(req, notify_buffer->changes);
91 notify_buffer->num_changes = 0;
92 notify_buffer->overflowed = false;
93 notify_buffer->changes = NULL;
94 notify_buffer->current_buffer_size = 0;
95
96 talloc_free(pending);
97
98 if (info->nttrans.out.num_changes != 0) {
99 status = NT_STATUS_OK;
100 }
101
102 req->async_states->status = status;
103
104 if (immediate) {
105 req->async_states->send_fn(req);
106 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 event_add_timed(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 */
119static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
120{
121 notify_remove(n->f->pvfs->notify_context, n);
122 n->f->notify_buffer = NULL;
123 pvfs_notify_send(n, NT_STATUS_OK, true);
124 return 0;
125}
126
127
128/*
129 called when a async notify event comes in
130*/
131static void pvfs_notify_callback(void *private_data, const struct notify_event *ev)
132{
133 struct pvfs_notify_buffer *n = talloc_get_type(private_data, struct pvfs_notify_buffer);
134 size_t len;
135 struct notify_changes *n2;
136 char *new_path;
137
138 if (n->overflowed) {
139 return;
140 }
141
142 n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
143 if (n2 == NULL) {
144 /* nothing much we can do for this */
145 return;
146 }
147 n->changes = n2;
148
149 new_path = talloc_strdup(n->changes, ev->path);
150 if (new_path == NULL) {
151 return;
152 }
153 string_replace(new_path, '/', '\\');
154
155 n->changes[n->num_changes].action = ev->action;
156 n->changes[n->num_changes].name.s = new_path;
157 n->num_changes++;
158
159 /*
160 work out how much room this will take in the buffer
161 */
162 len = 12 + strlen_m(ev->path)*2;
163 if (len & 3) {
164 len += 4 - (len & 3);
165 }
166 n->current_buffer_size += len;
167
168 /* send what we have, unless its the first part of a rename */
169 if (ev->action != NOTIFY_ACTION_OLD_NAME) {
170 pvfs_notify_send(n, NT_STATUS_OK, true);
171 }
172}
173
174/*
175 setup a notify buffer on a directory handle
176*/
177static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f,
178 uint32_t buffer_size, uint32_t filter, bool recursive)
179{
180 NTSTATUS status;
181 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 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 ZERO_STRUCT(e);
190
191 f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
192 NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
193
194 f->notify_buffer->max_buffer_size = buffer_size;
195 f->notify_buffer->f = f;
196
197 e.filter = filter;
198 e.path = f->handle->name->full_name;
199 if (recursive) {
200 e.subdir_filter = filter;
201 } else {
202 e.subdir_filter = 0;
203 }
204
205 status = notify_add(pvfs->notify_context, &e,
206 pvfs_notify_callback, f->notify_buffer);
207 NT_STATUS_NOT_OK_RETURN(status);
208
209 talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
210
211 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*/
218static void pvfs_notify_end(void *private_data, enum pvfs_wait_notice reason)
219{
220 struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private_data,
221 struct pvfs_notify_buffer);
222 if (reason == PVFS_WAIT_CANCEL) {
223 pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, false);
224 } else {
225 pvfs_notify_send(notify_buffer, NT_STATUS_OK, true);
226 }
227}
228
229/* change notify request - always async. This request blocks until the
230 event buffer is non-empty */
231NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
232 struct ntvfs_request *req,
233 union smb_notify *info)
234{
235 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
236 struct pvfs_state);
237 struct pvfs_file *f;
238 NTSTATUS status;
239 struct notify_pending *pending;
240
241 if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
242 return ntvfs_map_notify(ntvfs, req, info);
243 }
244
245 f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
246 if (!f) {
247 return NT_STATUS_INVALID_HANDLE;
248 }
249
250 /* this request doesn't make sense unless its async */
251 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
252 return NT_STATUS_INVALID_PARAMETER;
253 }
254
255 /* its only valid for directories */
256 if (f->handle->fd != -1) {
257 return NT_STATUS_INVALID_PARAMETER;
258 }
259
260 /* if the handle doesn't currently have a notify buffer then
261 create one */
262 if (f->notify_buffer == NULL) {
263 status = pvfs_notify_setup(pvfs, f,
264 info->nttrans.in.buffer_size,
265 info->nttrans.in.completion_filter,
266 info->nttrans.in.recursive);
267 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 f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
273
274 pending = talloc(f->notify_buffer, struct notify_pending);
275 NT_STATUS_HAVE_NO_MEMORY(pending);
276
277 pending->req = talloc_reference(pending, req);
278 NT_STATUS_HAVE_NO_MEMORY(pending->req);
279 pending->info = info;
280
281 DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
282
283 /* if the buffer is empty then start waiting */
284 if (f->notify_buffer->num_changes == 0 &&
285 !f->notify_buffer->overflowed) {
286 struct pvfs_wait *wait_handle;
287 wait_handle = pvfs_wait_message(pvfs, req, -1,
288 timeval_zero(),
289 pvfs_notify_end,
290 f->notify_buffer);
291 NT_STATUS_HAVE_NO_MEMORY(wait_handle);
292 talloc_steal(req, wait_handle);
293 return NT_STATUS_OK;
294 }
295
296 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
297 pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, false);
298
299 return NT_STATUS_OK;
300}
Note: See TracBrowser for help on using the repository browser.