• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/ntvfs/posix/
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	f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
184	NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
185
186	f->notify_buffer->max_buffer_size = buffer_size;
187	f->notify_buffer->f = f;
188
189	e.filter    = filter;
190	e.path      = f->handle->name->full_name;
191	if (recursive) {
192		e.subdir_filter = filter;
193	} else {
194		e.subdir_filter = 0;
195	}
196
197	status = notify_add(pvfs->notify_context, &e,
198			    pvfs_notify_callback, f->notify_buffer);
199	NT_STATUS_NOT_OK_RETURN(status);
200
201	talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
202
203	return NT_STATUS_OK;
204}
205
206/*
207  called from the pvfs_wait code when either an event has come in, or
208  the notify request has been cancelled
209*/
210static void pvfs_notify_end(void *private_data, enum pvfs_wait_notice reason)
211{
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);
216	} else {
217		pvfs_notify_send(notify_buffer, NT_STATUS_OK, true);
218	}
219}
220
221/* change notify request - always async. This request blocks until the
222   event buffer is non-empty */
223NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
224		     struct ntvfs_request *req,
225		     union smb_notify *info)
226{
227	struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
228						  struct pvfs_state);
229	struct pvfs_file *f;
230	NTSTATUS status;
231	struct notify_pending *pending;
232
233	if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
234		return ntvfs_map_notify(ntvfs, req, info);
235	}
236
237	f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
238	if (!f) {
239		return NT_STATUS_INVALID_HANDLE;
240	}
241
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;
245	}
246
247	/* its only valid for directories */
248	if (f->handle->fd != -1) {
249		return NT_STATUS_INVALID_PARAMETER;
250	}
251
252	/* if the handle doesn't currently have a notify buffer then
253	   create one */
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);
260	}
261
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;
265
266	pending = talloc(f->notify_buffer, struct notify_pending);
267	NT_STATUS_HAVE_NO_MEMORY(pending);
268
269	pending->req = talloc_reference(pending, req);
270	NT_STATUS_HAVE_NO_MEMORY(pending->req);
271	pending->info = info;
272
273	DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
274
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,
280						timeval_zero(),
281						pvfs_notify_end,
282						f->notify_buffer);
283		NT_STATUS_HAVE_NO_MEMORY(wait_handle);
284		talloc_steal(req, wait_handle);
285		return NT_STATUS_OK;
286	}
287
288	req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
289	pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, false);
290
291	return NT_STATUS_OK;
292}
293