2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - notify
6 Copyright (C) Andrew Tridgell 2006
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.
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.
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.
24 #include "vfs_posix.h"
25 #include "lib/messaging/irpc.h"
26 #include "messaging/messaging.h"
27 #include "dlinklist.h"
29 /* pending notifies buffer, hung off struct pvfs_file for open directories
30 that have used change notify */
31 struct pvfs_notify_buffer {
34 struct notify_changes *changes;
35 uint32_t max_buffer_size;
36 uint32_t current_buffer_size;
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 struct smb_notify *info;
47 send a reply to a pending notify request
49 static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer, NTSTATUS status)
51 struct notify_pending *pending = notify_buffer->pending;
52 struct ntvfs_request *req;
53 struct smb_notify *info;
55 if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size &&
56 notify_buffer->num_changes != 0) {
57 /* on buffer overflow return no changes and destroys the notify buffer */
58 notify_buffer->num_changes = 0;
59 while (notify_buffer->pending) {
60 pvfs_notify_send(notify_buffer, NT_STATUS_OK);
62 talloc_free(notify_buffer);
66 /* see if there is anyone waiting */
67 if (notify_buffer->pending == NULL) {
71 DLIST_REMOVE(notify_buffer->pending, pending);
76 info->out.num_changes = notify_buffer->num_changes;
77 info->out.changes = talloc_steal(req, notify_buffer->changes);
78 notify_buffer->num_changes = 0;
79 notify_buffer->changes = NULL;
80 notify_buffer->current_buffer_size = 0;
84 if (info->out.num_changes != 0) {
85 status = NT_STATUS_OK;
88 req->async_states->status = status;
89 req->async_states->send_fn(req);
93 destroy a notify buffer. Called when the handle is closed
95 static int pvfs_notify_destructor(void *ptr)
97 struct pvfs_notify_buffer *n = talloc_get_type(ptr, struct pvfs_notify_buffer);
98 notify_remove(n->f->pvfs->notify_context, n);
99 n->f->notify_buffer = NULL;
100 pvfs_notify_send(n, NT_STATUS_OK);
106 called when a async notify event comes in
108 static void pvfs_notify_callback(void *private, const struct notify_event *ev)
110 struct pvfs_notify_buffer *n = talloc_get_type(private, struct pvfs_notify_buffer);
113 n->changes = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
114 n->changes[n->num_changes].action = ev->action;
115 n->changes[n->num_changes].name.s = talloc_strdup(n->changes, ev->path);
119 work out how much room this will take in the buffer
121 len = 12 + strlen_m(ev->path)*2;
123 len += 4 - (len & 3);
125 n->current_buffer_size += len;
127 /* send what we have */
128 pvfs_notify_send(n, NT_STATUS_OK);
132 setup a notify buffer on a directory handle
134 static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f,
135 uint32_t buffer_size, uint32_t filter, BOOL recursive)
138 struct notify_entry e;
140 f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
141 NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
143 f->notify_buffer->max_buffer_size = buffer_size;
144 f->notify_buffer->f = f;
147 e.path = f->handle->name->full_name;
149 e.subdir_filter = filter;
154 status = notify_add(pvfs->notify_context, &e,
155 pvfs_notify_callback, f->notify_buffer);
156 NT_STATUS_NOT_OK_RETURN(status);
158 talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
164 called from the pvfs_wait code when either an event has come in, or
165 the notify request has been cancelled
167 static void pvfs_notify_end(void *private, enum pvfs_wait_notice reason)
169 struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private,
170 struct pvfs_notify_buffer);
171 if (reason == PVFS_WAIT_CANCEL) {
172 pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED);
174 pvfs_notify_send(notify_buffer, NT_STATUS_OK);
178 /* change notify request - always async. This request blocks until the
179 event buffer is non-empty */
180 NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
181 struct ntvfs_request *req,
182 struct smb_notify *info)
184 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
188 struct notify_pending *pending;
190 f = pvfs_find_fd(pvfs, req, info->in.file.fnum);
192 return NT_STATUS_INVALID_HANDLE;
195 /* this request doesn't make sense unless its async */
196 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
197 return NT_STATUS_INVALID_PARAMETER;
200 /* its only valid for directories */
201 if (f->handle->fd != -1) {
202 return NT_STATUS_INVALID_PARAMETER;
205 /* if the handle doesn't currently have a notify buffer then
207 if (f->notify_buffer == NULL) {
208 status = pvfs_notify_setup(pvfs, f,
209 info->in.buffer_size,
210 info->in.completion_filter,
212 NT_STATUS_NOT_OK_RETURN(status);
215 f->notify_buffer->max_buffer_size = info->in.buffer_size;
217 pending = talloc(f->notify_buffer, struct notify_pending);
218 NT_STATUS_HAVE_NO_MEMORY(pending);
220 pending->req = talloc_reference(pending, req);
221 pending->info = info;
223 DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
225 /* if the buffer is empty then start waiting */
226 if (f->notify_buffer->num_changes == 0) {
228 pvfs_wait_message(pvfs, req, -1, timeval_zero(),
229 pvfs_notify_end, f->notify_buffer);
230 NT_STATUS_HAVE_NO_MEMORY(wait_handle);
231 talloc_steal(req, wait_handle);
235 pvfs_notify_send(f->notify_buffer, NT_STATUS_OK);