r17930: Merge noinclude branch:
[gd/samba-autobuild/.git] / source4 / ntvfs / posix / pvfs_notify.c
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "vfs_posix.h"
25 #include "lib/messaging/irpc.h"
26 #include "messaging/messaging.h"
27 #include "lib/util/dlinklist.h"
28 #include "lib/events/events.h"
29
30 /* pending notifies buffer, hung off struct pvfs_file for open directories
31    that have used change notify */
32 struct pvfs_notify_buffer {
33         struct pvfs_file *f;
34         uint32_t num_changes;
35         struct notify_changes *changes;
36         uint32_t max_buffer_size;
37         uint32_t current_buffer_size;
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 static void pvfs_notify_send_next(struct event_context *ev, struct timed_event *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 */
61 static 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                 talloc_free(notify_buffer);
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->changes = NULL;
93         notify_buffer->current_buffer_size = 0;
94
95         talloc_free(pending);
96
97         if (info->nttrans.out.num_changes != 0) {
98                 status = NT_STATUS_OK;
99         }
100
101         req->async_states->status = status;
102
103         if (immediate) {
104                 req->async_states->send_fn(req);
105                 return;
106         } 
107
108         /* we can't call pvfs_notify_send() directly here, as that
109            would free the request, and the ntvfs modules above us
110            could use it, so call it on the next event */
111         event_add_timed(req->ctx->event_ctx, 
112                         req, timeval_zero(), pvfs_notify_send_next, req);
113 }
114
115 /*
116   destroy a notify buffer. Called when the handle is closed
117  */
118 static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
119 {
120         notify_remove(n->f->pvfs->notify_context, n);
121         n->f->notify_buffer = NULL;
122         pvfs_notify_send(n, NT_STATUS_OK, True);
123         return 0;
124 }
125
126
127 /*
128   called when a async notify event comes in
129 */
130 static void pvfs_notify_callback(void *private, const struct notify_event *ev)
131 {
132         struct pvfs_notify_buffer *n = talloc_get_type(private, struct pvfs_notify_buffer);
133         size_t len;
134         struct notify_changes *n2;
135         char *new_path;
136
137         n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
138         if (n2 == NULL) {
139                 /* nothing much we can do for this */
140                 return;
141         }
142         n->changes = n2;
143
144         new_path = talloc_strdup(n->changes, ev->path);
145         if (new_path == NULL) {
146                 return;
147         }
148         string_replace(new_path, '/', '\\');
149
150         n->changes[n->num_changes].action = ev->action;
151         n->changes[n->num_changes].name.s = new_path;
152         n->num_changes++;
153
154         /*
155           work out how much room this will take in the buffer
156         */
157         len = 12 + strlen_m(ev->path)*2;
158         if (len & 3) {
159                 len += 4 - (len & 3);
160         }
161         n->current_buffer_size += len;
162
163         /* send what we have, unless its the first part of a rename */
164         if (ev->action != NOTIFY_ACTION_OLD_NAME) {
165                 pvfs_notify_send(n, NT_STATUS_OK, True);
166         }
167 }
168
169 /*
170   setup a notify buffer on a directory handle
171 */
172 static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f, 
173                                   uint32_t buffer_size, uint32_t filter, BOOL recursive)
174 {
175         NTSTATUS status;
176         struct notify_entry e;
177
178         f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
179         NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
180
181         f->notify_buffer->max_buffer_size = buffer_size;
182         f->notify_buffer->f = f;
183
184         e.filter    = filter;
185         e.path      = f->handle->name->full_name;
186         if (recursive) {
187                 e.subdir_filter = filter;
188         } else {
189                 e.subdir_filter = 0;
190         }
191
192         status = notify_add(pvfs->notify_context, &e, 
193                             pvfs_notify_callback, f->notify_buffer);
194         NT_STATUS_NOT_OK_RETURN(status);
195
196         talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
197
198         return NT_STATUS_OK;
199 }
200
201 /*
202   called from the pvfs_wait code when either an event has come in, or
203   the notify request has been cancelled
204 */
205 static void pvfs_notify_end(void *private, enum pvfs_wait_notice reason)
206 {
207         struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private, 
208                                                                    struct pvfs_notify_buffer);
209         if (reason == PVFS_WAIT_CANCEL) {
210                 pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, False);
211         } else {
212                 pvfs_notify_send(notify_buffer, NT_STATUS_OK, True);
213         }
214 }
215
216 /* change notify request - always async. This request blocks until the
217    event buffer is non-empty */
218 NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs, 
219                      struct ntvfs_request *req,
220                      union smb_notify *info)
221 {
222         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data, 
223                                                   struct pvfs_state);
224         struct pvfs_file *f;
225         NTSTATUS status;
226         struct notify_pending *pending;
227
228         if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
229                 return ntvfs_map_notify(ntvfs, req, info);
230         }
231
232         f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
233         if (!f) {
234                 return NT_STATUS_INVALID_HANDLE;
235         }
236
237         /* this request doesn't make sense unless its async */
238         if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
239                 return NT_STATUS_INVALID_PARAMETER;
240         }
241
242         /* its only valid for directories */
243         if (f->handle->fd != -1) {
244                 return NT_STATUS_INVALID_PARAMETER;
245         }
246
247         /* if the handle doesn't currently have a notify buffer then
248            create one */
249         if (f->notify_buffer == NULL) {
250                 status = pvfs_notify_setup(pvfs, f, 
251                                            info->nttrans.in.buffer_size, 
252                                            info->nttrans.in.completion_filter,
253                                            info->nttrans.in.recursive);
254                 NT_STATUS_NOT_OK_RETURN(status);
255         }
256
257         /* we update the max_buffer_size on each call, but we do not
258            update the recursive flag or filter */
259         f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
260
261         pending = talloc(f->notify_buffer, struct notify_pending);
262         NT_STATUS_HAVE_NO_MEMORY(pending);
263
264         pending->req = talloc_reference(pending, req);
265         NT_STATUS_HAVE_NO_MEMORY(pending->req); 
266         pending->info = info;
267
268         DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
269
270         /* if the buffer is empty then start waiting */
271         if (f->notify_buffer->num_changes == 0) {
272                 void *wait_handle =
273                         pvfs_wait_message(pvfs, req, -1, timeval_zero(), 
274                                           pvfs_notify_end, f->notify_buffer);
275                 NT_STATUS_HAVE_NO_MEMORY(wait_handle);
276                 talloc_steal(req, wait_handle);
277                 return NT_STATUS_OK;
278         }
279
280         req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
281         pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, False);
282
283         return NT_STATUS_OK;
284 }