• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source3/smbd/
1/*
2   Unix SMB/CIFS implementation.
3   change notify handling
4   Copyright (C) Andrew Tridgell 2000
5   Copyright (C) Jeremy Allison 1994-1998
6   Copyright (C) Volker Lendecke 2007
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 "smbd/globals.h"
24
25struct notify_change_request {
26	struct notify_change_request *prev, *next;
27	struct files_struct *fsp;	/* backpointer for cancel by mid */
28	struct smb_request *req;
29	uint32 filter;
30	uint32 max_param;
31	void (*reply_fn)(struct smb_request *req,
32			 NTSTATUS error_code,
33			 uint8_t *buf, size_t len);
34	struct notify_mid_map *mid_map;
35	void *backend_data;
36};
37
38static void notify_fsp(files_struct *fsp, uint32 action, const char *name);
39
40/*
41 * For NTCancel, we need to find the notify_change_request indexed by
42 * mid. Separate list here.
43 */
44
45struct notify_mid_map {
46	struct notify_mid_map *prev, *next;
47	struct notify_change_request *req;
48	uint16 mid;
49};
50
51static bool notify_change_record_identical(struct notify_change *c1,
52					struct notify_change *c2)
53{
54	/* Note this is deliberately case sensitive. */
55	if (c1->action == c2->action &&
56			strcmp(c1->name, c2->name) == 0) {
57		return True;
58	}
59	return False;
60}
61
62static bool notify_marshall_changes(int num_changes,
63				uint32 max_offset,
64				struct notify_change *changes,
65				prs_struct *ps)
66{
67	int i;
68	UNISTR uni_name;
69
70	if (num_changes == -1) {
71		return false;
72	}
73
74	uni_name.buffer = NULL;
75
76	for (i=0; i<num_changes; i++) {
77		struct notify_change *c;
78		size_t namelen;
79		int    rem = 0;
80		uint32 u32_tmp;	/* Temp arg to prs_uint32 to avoid
81				 * signed/unsigned issues */
82
83		/* Coalesce any identical records. */
84		while (i+1 < num_changes &&
85			notify_change_record_identical(&changes[i],
86						&changes[i+1])) {
87			i++;
88		}
89
90		c = &changes[i];
91
92		if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_UTF16LE,
93			c->name, strlen(c->name)+1, &uni_name.buffer,
94			&namelen, True) || (uni_name.buffer == NULL)) {
95			goto fail;
96		}
97
98		namelen -= 2;	/* Dump NULL termination */
99
100		/*
101		 * Offset to next entry, only if there is one
102		 */
103
104		u32_tmp = (i == num_changes-1) ? 0 : namelen + 12;
105
106		/* Align on 4-byte boundary according to MS-CIFS 2.2.7.4.2 */
107		if ((rem = u32_tmp % 4 ) != 0)
108			u32_tmp += 4 - rem;
109
110		if (!prs_uint32("offset", ps, 1, &u32_tmp)) goto fail;
111
112		u32_tmp = c->action;
113		if (!prs_uint32("action", ps, 1, &u32_tmp)) goto fail;
114
115		u32_tmp = namelen;
116		if (!prs_uint32("namelen", ps, 1, &u32_tmp)) goto fail;
117
118		if (!prs_unistr("name", ps, 1, &uni_name)) goto fail;
119
120		/*
121		 * Not NULL terminated, decrease by the 2 UCS2 \0 chars
122		 */
123		prs_set_offset(ps, prs_offset(ps)-2);
124
125		if (rem != 0) {
126			if (!prs_align_custom(ps, 4)) goto fail;
127		}
128
129		TALLOC_FREE(uni_name.buffer);
130
131		if (prs_offset(ps) > max_offset) {
132			/* Too much data for client. */
133			DEBUG(10, ("Client only wanted %d bytes, trying to "
134				   "marshall %d bytes\n", (int)max_offset,
135				   (int)prs_offset(ps)));
136			return False;
137		}
138	}
139
140	return True;
141
142 fail:
143	TALLOC_FREE(uni_name.buffer);
144	return False;
145}
146
147/****************************************************************************
148 Setup the common parts of the return packet and send it.
149*****************************************************************************/
150
151void change_notify_reply(connection_struct *conn,
152			 struct smb_request *req,
153			 NTSTATUS error_code,
154			 uint32_t max_param,
155			 struct notify_change_buf *notify_buf,
156			 void (*reply_fn)(struct smb_request *req,
157				NTSTATUS error_code,
158				uint8_t *buf, size_t len))
159{
160	prs_struct ps;
161
162	if (!NT_STATUS_IS_OK(error_code)) {
163		reply_fn(req, error_code, NULL, 0);
164		return;
165	}
166
167	if (max_param == 0 || notify_buf == NULL) {
168		reply_fn(req, NT_STATUS_OK, NULL, 0);
169		return;
170	}
171
172	prs_init_empty(&ps, NULL, MARSHALL);
173
174	if (!notify_marshall_changes(notify_buf->num_changes, max_param,
175					notify_buf->changes, &ps)) {
176		/*
177		 * We exceed what the client is willing to accept. Send
178		 * nothing.
179		 */
180		prs_mem_free(&ps);
181		prs_init_empty(&ps, NULL, MARSHALL);
182	}
183
184	reply_fn(req, NT_STATUS_OK, (uint8_t *)prs_data_p(&ps), prs_offset(&ps));
185
186	prs_mem_free(&ps);
187
188	TALLOC_FREE(notify_buf->changes);
189	notify_buf->num_changes = 0;
190}
191
192static void notify_callback(void *private_data, const struct notify_event *e)
193{
194	files_struct *fsp = (files_struct *)private_data;
195	DEBUG(10, ("notify_callback called for %s\n", fsp_str_dbg(fsp)));
196	notify_fsp(fsp, e->action, e->path);
197}
198
199NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter,
200			      bool recursive)
201{
202	char *fullpath;
203	struct notify_entry e;
204	NTSTATUS status;
205
206	SMB_ASSERT(fsp->notify == NULL);
207
208	if (!(fsp->notify = TALLOC_ZERO_P(NULL, struct notify_change_buf))) {
209		DEBUG(0, ("talloc failed\n"));
210		return NT_STATUS_NO_MEMORY;
211	}
212
213	/* Do notify operations on the base_name. */
214	if (asprintf(&fullpath, "%s/%s", fsp->conn->connectpath,
215		     fsp->fsp_name->base_name) == -1) {
216		DEBUG(0, ("asprintf failed\n"));
217		TALLOC_FREE(fsp->notify);
218		return NT_STATUS_NO_MEMORY;
219	}
220
221	ZERO_STRUCT(e);
222	e.path = fullpath;
223	e.dir_fd = fsp->fh->fd;
224	e.dir_id = fsp->file_id;
225	e.filter = filter;
226	e.subdir_filter = 0;
227	if (recursive) {
228		e.subdir_filter = filter;
229	}
230
231	status = notify_add(fsp->conn->notify_ctx, &e, notify_callback, fsp);
232	SAFE_FREE(fullpath);
233
234	return status;
235}
236
237NTSTATUS change_notify_add_request(struct smb_request *req,
238				uint32 max_param,
239				uint32 filter, bool recursive,
240				struct files_struct *fsp,
241				void (*reply_fn)(struct smb_request *req,
242					NTSTATUS error_code,
243					uint8_t *buf, size_t len))
244{
245	struct notify_change_request *request = NULL;
246	struct notify_mid_map *map = NULL;
247	struct smbd_server_connection *sconn = smbd_server_conn;
248
249	DEBUG(10, ("change_notify_add_request: Adding request for %s: "
250		   "max_param = %d\n", fsp_str_dbg(fsp), (int)max_param));
251
252	if (!(request = talloc(NULL, struct notify_change_request))
253	    || !(map = talloc(request, struct notify_mid_map))) {
254		TALLOC_FREE(request);
255		return NT_STATUS_NO_MEMORY;
256	}
257
258	request->mid_map = map;
259	map->req = request;
260
261	request->req = talloc_move(request, &req);
262	request->max_param = max_param;
263	request->filter = filter;
264	request->fsp = fsp;
265	request->reply_fn = reply_fn;
266	request->backend_data = NULL;
267
268	DLIST_ADD_END(fsp->notify->requests, request,
269		      struct notify_change_request *);
270
271	map->mid = request->req->mid;
272	DLIST_ADD(sconn->smb1.notify_mid_maps, map);
273
274	return NT_STATUS_OK;
275}
276
277static void change_notify_remove_request(struct notify_change_request *remove_req)
278{
279	files_struct *fsp;
280	struct notify_change_request *req;
281	struct smbd_server_connection *sconn = smbd_server_conn;
282
283	/*
284	 * Paranoia checks, the fsp referenced must must have the request in
285	 * its list of pending requests
286	 */
287
288	fsp = remove_req->fsp;
289	SMB_ASSERT(fsp->notify != NULL);
290
291	for (req = fsp->notify->requests; req; req = req->next) {
292		if (req == remove_req) {
293			break;
294		}
295	}
296
297	if (req == NULL) {
298		smb_panic("notify_req not found in fsp's requests");
299	}
300
301	DLIST_REMOVE(fsp->notify->requests, req);
302	DLIST_REMOVE(sconn->smb1.notify_mid_maps, req->mid_map);
303	TALLOC_FREE(req);
304}
305
306/****************************************************************************
307 Delete entries by mid from the change notify pending queue. Always send reply.
308*****************************************************************************/
309
310void remove_pending_change_notify_requests_by_mid(uint16 mid)
311{
312	struct notify_mid_map *map;
313	struct smbd_server_connection *sconn = smbd_server_conn;
314
315	for (map = sconn->smb1.notify_mid_maps; map; map = map->next) {
316		if (map->mid == mid) {
317			break;
318		}
319	}
320
321	if (map == NULL) {
322		return;
323	}
324
325	change_notify_reply(map->req->fsp->conn, map->req->req,
326			    NT_STATUS_CANCELLED, 0, NULL, map->req->reply_fn);
327	change_notify_remove_request(map->req);
328}
329
330void smbd_notify_cancel_by_smbreq(struct smbd_server_connection *sconn,
331				  const struct smb_request *smbreq)
332{
333	struct notify_mid_map *map;
334
335	for (map = sconn->smb1.notify_mid_maps; map; map = map->next) {
336		if (map->req->req == smbreq) {
337			break;
338		}
339	}
340
341	if (map == NULL) {
342		return;
343	}
344
345	change_notify_reply(map->req->fsp->conn, map->req->req,
346			    NT_STATUS_CANCELLED, 0, NULL, map->req->reply_fn);
347	change_notify_remove_request(map->req);
348}
349
350/****************************************************************************
351 Delete entries by fnum from the change notify pending queue.
352*****************************************************************************/
353
354void remove_pending_change_notify_requests_by_fid(files_struct *fsp,
355						  NTSTATUS status)
356{
357	if (fsp->notify == NULL) {
358		return;
359	}
360
361	while (fsp->notify->requests != NULL) {
362		change_notify_reply(fsp->conn, fsp->notify->requests->req,
363				    status, 0, NULL,
364				    fsp->notify->requests->reply_fn);
365		change_notify_remove_request(fsp->notify->requests);
366	}
367}
368
369void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
370		  const char *path)
371{
372	char *fullpath;
373	char *parent;
374	const char *name;
375
376	if (path[0] == '.' && path[1] == '/') {
377		path += 2;
378	}
379	if (parent_dirname(talloc_tos(), path, &parent, &name)) {
380		struct smb_filename smb_fname_parent;
381
382		ZERO_STRUCT(smb_fname_parent);
383		smb_fname_parent.base_name = parent;
384
385		if (SMB_VFS_STAT(conn, &smb_fname_parent) != -1) {
386			notify_onelevel(conn->notify_ctx, action, filter,
387			    SMB_VFS_FILE_ID_CREATE(conn, &smb_fname_parent.st),
388			    name);
389		}
390	}
391
392	fullpath = talloc_asprintf(talloc_tos(), "%s/%s", conn->connectpath,
393				   path);
394	if (fullpath == NULL) {
395		DEBUG(0, ("asprintf failed\n"));
396		return;
397	}
398	notify_trigger(conn->notify_ctx, action, filter, fullpath);
399	TALLOC_FREE(fullpath);
400}
401
402static void notify_fsp(files_struct *fsp, uint32 action, const char *name)
403{
404	struct notify_change *change, *changes;
405	char *tmp;
406
407	if (fsp->notify == NULL) {
408		/*
409		 * Nobody is waiting, don't queue
410		 */
411		return;
412	}
413
414	/*
415	 * Someone has triggered a notify previously, queue the change for
416	 * later.
417	 */
418
419	if ((fsp->notify->num_changes > 1000) || (name == NULL)) {
420		/*
421		 * The real number depends on the client buf, just provide a
422		 * guard against a DoS here.  If name == NULL the CN backend is
423		 * alerting us to a problem.  Possibly dropped events.  Clear
424		 * queued changes and send the catch-all response to the client
425		 * if a request is pending.
426		 */
427		TALLOC_FREE(fsp->notify->changes);
428		fsp->notify->num_changes = -1;
429		if (fsp->notify->requests != NULL) {
430			change_notify_reply(fsp->conn,
431					    fsp->notify->requests->req,
432					    NT_STATUS_OK,
433					    fsp->notify->requests->max_param,
434					    fsp->notify,
435					    fsp->notify->requests->reply_fn);
436			change_notify_remove_request(fsp->notify->requests);
437		}
438		return;
439	}
440
441	/* If we've exceeded the server side queue or received a NULL name
442	 * from the underlying CN implementation, don't queue up any more
443	 * requests until we can send a catch-all response to the client */
444	if (fsp->notify->num_changes == -1) {
445		return;
446	}
447
448	if (!(changes = TALLOC_REALLOC_ARRAY(
449		      fsp->notify, fsp->notify->changes,
450		      struct notify_change, fsp->notify->num_changes+1))) {
451		DEBUG(0, ("talloc_realloc failed\n"));
452		return;
453	}
454
455	fsp->notify->changes = changes;
456
457	change = &(fsp->notify->changes[fsp->notify->num_changes]);
458
459	if (!(tmp = talloc_strdup(changes, name))) {
460		DEBUG(0, ("talloc_strdup failed\n"));
461		return;
462	}
463
464	string_replace(tmp, '/', '\\');
465	change->name = tmp;
466
467	change->action = action;
468	fsp->notify->num_changes += 1;
469
470	if (fsp->notify->requests == NULL) {
471		/*
472		 * Nobody is waiting, so don't send anything. The ot
473		 */
474		return;
475	}
476
477	if (action == NOTIFY_ACTION_OLD_NAME) {
478		/*
479		 * We have to send the two rename events in one reply. So hold
480		 * the first part back.
481		 */
482		return;
483	}
484
485	/*
486	 * Someone is waiting for the change, trigger the reply immediately.
487	 *
488	 * TODO: do we have to walk the lists of requests pending?
489	 */
490
491	change_notify_reply(fsp->conn,
492			    fsp->notify->requests->req,
493			    NT_STATUS_OK,
494			    fsp->notify->requests->max_param,
495			    fsp->notify,
496			    fsp->notify->requests->reply_fn);
497
498	change_notify_remove_request(fsp->notify->requests);
499}
500
501char *notify_filter_string(TALLOC_CTX *mem_ctx, uint32 filter)
502{
503	char *result = NULL;
504
505	result = talloc_strdup(mem_ctx, "");
506
507	if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
508		result = talloc_asprintf_append(result, "FILE_NAME|");
509	if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
510		result = talloc_asprintf_append(result, "DIR_NAME|");
511	if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
512		result = talloc_asprintf_append(result, "ATTRIBUTES|");
513	if (filter & FILE_NOTIFY_CHANGE_SIZE)
514		result = talloc_asprintf_append(result, "SIZE|");
515	if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
516		result = talloc_asprintf_append(result, "LAST_WRITE|");
517	if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
518		result = talloc_asprintf_append(result, "LAST_ACCESS|");
519	if (filter & FILE_NOTIFY_CHANGE_CREATION)
520		result = talloc_asprintf_append(result, "CREATION|");
521	if (filter & FILE_NOTIFY_CHANGE_EA)
522		result = talloc_asprintf_append(result, "EA|");
523	if (filter & FILE_NOTIFY_CHANGE_SECURITY)
524		result = talloc_asprintf_append(result, "SECURITY|");
525	if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME)
526		result = talloc_asprintf_append(result, "STREAM_NAME|");
527	if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE)
528		result = talloc_asprintf_append(result, "STREAM_SIZE|");
529	if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE)
530		result = talloc_asprintf_append(result, "STREAM_WRITE|");
531
532	if (result == NULL) return NULL;
533	if (*result == '\0') return result;
534
535	result[strlen(result)-1] = '\0';
536	return result;
537}
538
539struct sys_notify_context *sys_notify_context_create(connection_struct *conn,
540						     TALLOC_CTX *mem_ctx,
541						     struct event_context *ev)
542{
543	struct sys_notify_context *ctx;
544
545	if (!(ctx = TALLOC_P(mem_ctx, struct sys_notify_context))) {
546		DEBUG(0, ("talloc failed\n"));
547		return NULL;
548	}
549
550	ctx->ev = ev;
551	ctx->conn = conn;
552	ctx->private_data = NULL;
553	return ctx;
554}
555
556NTSTATUS sys_notify_watch(struct sys_notify_context *ctx,
557			  struct notify_entry *e,
558			  void (*callback)(struct sys_notify_context *ctx,
559					   void *private_data,
560					   struct notify_event *ev),
561			  void *private_data, void *handle)
562{
563	return SMB_VFS_NOTIFY_WATCH(ctx->conn, ctx, e, callback, private_data,
564				    handle);
565}
566
567