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 3 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, see <http://www.gnu.org/licenses/>.
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"
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
;
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
;
48 send a notify on the next event run.
50 static void pvfs_notify_send_next(struct tevent_context
*ev
, struct tevent_timer
*te
,
51 struct timeval t
, void *ptr
)
53 struct ntvfs_request
*req
= talloc_get_type(ptr
, struct ntvfs_request
);
54 req
->async_states
->send_fn(req
);
59 send a reply to a pending notify request
61 static void pvfs_notify_send(struct pvfs_notify_buffer
*notify_buffer
,
62 NTSTATUS status
, bool immediate
)
64 struct notify_pending
*pending
= notify_buffer
->pending
;
65 struct ntvfs_request
*req
;
66 union smb_notify
*info
;
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
);
75 notify_buffer
->overflowed
= true;
79 /* see if there is anyone waiting */
80 if (notify_buffer
->pending
== NULL
) {
84 DLIST_REMOVE(notify_buffer
->pending
, pending
);
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;
98 if (info
->nttrans
.out
.num_changes
!= 0) {
99 status
= NT_STATUS_OK
;
102 req
->async_states
->status
= status
;
105 req
->async_states
->send_fn(req
);
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
);
117 destroy a notify buffer. Called when the handle is closed
119 static int pvfs_notify_destructor(struct pvfs_notify_buffer
*n
)
121 notify_remove(n
->f
->pvfs
->notify_context
, n
);
122 n
->f
->notify_buffer
= NULL
;
123 pvfs_notify_send(n
, NT_STATUS_OK
, true);
129 called when a async notify event comes in
131 static void pvfs_notify_callback(void *private_data
, const struct notify_event
*ev
)
133 struct pvfs_notify_buffer
*n
= talloc_get_type(private_data
, struct pvfs_notify_buffer
);
135 struct notify_changes
*n2
;
142 n2
= talloc_realloc(n
, n
->changes
, struct notify_changes
, n
->num_changes
+1);
144 /* nothing much we can do for this */
149 new_path
= talloc_strdup(n
->changes
, ev
->path
);
150 if (new_path
== NULL
) {
153 string_replace(new_path
, '/', '\\');
155 n
->changes
[n
->num_changes
].action
= ev
->action
;
156 n
->changes
[n
->num_changes
].name
.s
= new_path
;
160 work out how much room this will take in the buffer
162 len
= 12 + strlen_m(ev
->path
)*2;
164 len
+= 4 - (len
& 3);
166 n
->current_buffer_size
+= len
;
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);
175 setup a notify buffer on a directory handle
177 static NTSTATUS
pvfs_notify_setup(struct pvfs_state
*pvfs
, struct pvfs_file
*f
,
178 uint32_t buffer_size
, uint32_t filter
, bool recursive
)
181 struct notify_entry e
;
183 f
->notify_buffer
= talloc_zero(f
, struct pvfs_notify_buffer
);
184 NT_STATUS_HAVE_NO_MEMORY(f
->notify_buffer
);
186 f
->notify_buffer
->max_buffer_size
= buffer_size
;
187 f
->notify_buffer
->f
= f
;
190 e
.path
= f
->handle
->name
->full_name
;
192 e
.subdir_filter
= filter
;
197 status
= notify_add(pvfs
->notify_context
, &e
,
198 pvfs_notify_callback
, f
->notify_buffer
);
199 NT_STATUS_NOT_OK_RETURN(status
);
201 talloc_set_destructor(f
->notify_buffer
, pvfs_notify_destructor
);
207 called from the pvfs_wait code when either an event has come in, or
208 the notify request has been cancelled
210 static void pvfs_notify_end(void *private_data
, enum pvfs_wait_notice reason
)
212 struct pvfs_notify_buffer
*notify_buffer
= talloc_get_type(private_data
,
213 struct pvfs_notify_buffer
);
214 if (reason
== PVFS_WAIT_CANCEL
) {
215 pvfs_notify_send(notify_buffer
, NT_STATUS_CANCELLED
, false);
217 pvfs_notify_send(notify_buffer
, NT_STATUS_OK
, true);
221 /* change notify request - always async. This request blocks until the
222 event buffer is non-empty */
223 NTSTATUS
pvfs_notify(struct ntvfs_module_context
*ntvfs
,
224 struct ntvfs_request
*req
,
225 union smb_notify
*info
)
227 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
231 struct notify_pending
*pending
;
233 if (info
->nttrans
.level
!= RAW_NOTIFY_NTTRANS
) {
234 return ntvfs_map_notify(ntvfs
, req
, info
);
237 f
= pvfs_find_fd(pvfs
, req
, info
->nttrans
.in
.file
.ntvfs
);
239 return NT_STATUS_INVALID_HANDLE
;
242 /* this request doesn't make sense unless its async */
243 if (!(req
->async_states
->state
& NTVFS_ASYNC_STATE_MAY_ASYNC
)) {
244 return NT_STATUS_INVALID_PARAMETER
;
247 /* its only valid for directories */
248 if (f
->handle
->fd
!= -1) {
249 return NT_STATUS_INVALID_PARAMETER
;
252 /* if the handle doesn't currently have a notify buffer then
254 if (f
->notify_buffer
== NULL
) {
255 status
= pvfs_notify_setup(pvfs
, f
,
256 info
->nttrans
.in
.buffer_size
,
257 info
->nttrans
.in
.completion_filter
,
258 info
->nttrans
.in
.recursive
);
259 NT_STATUS_NOT_OK_RETURN(status
);
262 /* we update the max_buffer_size on each call, but we do not
263 update the recursive flag or filter */
264 f
->notify_buffer
->max_buffer_size
= info
->nttrans
.in
.buffer_size
;
266 pending
= talloc(f
->notify_buffer
, struct notify_pending
);
267 NT_STATUS_HAVE_NO_MEMORY(pending
);
269 pending
->req
= talloc_reference(pending
, req
);
270 NT_STATUS_HAVE_NO_MEMORY(pending
->req
);
271 pending
->info
= info
;
273 DLIST_ADD_END(f
->notify_buffer
->pending
, pending
, struct notify_pending
*);
275 /* if the buffer is empty then start waiting */
276 if (f
->notify_buffer
->num_changes
== 0 &&
277 !f
->notify_buffer
->overflowed
) {
278 struct pvfs_wait
*wait_handle
;
279 wait_handle
= pvfs_wait_message(pvfs
, req
, -1,
283 NT_STATUS_HAVE_NO_MEMORY(wait_handle
);
284 talloc_steal(req
, wait_handle
);
288 req
->async_states
->state
|= NTVFS_ASYNC_STATE_ASYNC
;
289 pvfs_notify_send(f
->notify_buffer
, NT_STATUS_OK
, false);