2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2006
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 this is the change notify database. It implements mechanisms for
23 storing current change notify waiters in a tdb, and checking if a
24 given event matches any of the stored notify waiiters.
28 #include "system/filesys.h"
29 #include "lib/tdb/include/tdb.h"
30 #include "lib/tdb/include/tdbutil.h"
31 #include "messaging/messaging.h"
33 #include "lib/messaging/irpc.h"
34 #include "librpc/gen_ndr/ndr_notify.h"
35 #include "dlinklist.h"
36 #include "ntvfs/sysdep/sys_notify.h"
38 struct notify_context {
41 struct messaging_context *messaging_ctx;
42 struct notify_list *list;
43 struct notify_array *array;
45 struct sys_notify_context *sys_notify_ctx;
50 struct notify_list *next, *prev;
52 void (*callback)(void *, const struct notify_event *);
53 void *sys_notify_handle;
57 #define NOTIFY_KEY "notify array"
59 static NTSTATUS notify_remove_all(struct notify_context *notify);
60 static void notify_handler(struct messaging_context *msg_ctx, void *private,
61 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
64 destroy the notify context
66 static int notify_destructor(void *p)
68 struct notify_context *notify = talloc_get_type(p, struct notify_context);
69 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
70 notify_remove_all(notify);
75 Open up the notify.tdb database. You should close it down using
76 talloc_free(). We need the messaging_ctx to allow for notifications
79 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server,
80 struct messaging_context *messaging_ctx,
81 struct event_context *ev, int snum)
84 struct notify_context *notify;
86 if (lp_parm_bool(snum, "notify", "enable", True) != True) {
90 notify = talloc(mem_ctx, struct notify_context);
95 path = smbd_tmp_path(notify, "notify.tdb");
96 notify->w = tdb_wrap_open(notify, path, 0,
98 O_RDWR|O_CREAT, 0600);
100 if (notify->w == NULL) {
105 notify->server = server;
106 notify->messaging_ctx = messaging_ctx;
108 notify->array = NULL;
109 notify->seqnum = tdb_get_seqnum(notify->w->tdb);
111 talloc_set_destructor(notify, notify_destructor);
113 /* register with the messaging subsystem for the notify
115 messaging_register(notify->messaging_ctx, notify,
116 MSG_PVFS_NOTIFY, notify_handler);
118 notify->sys_notify_ctx = sys_notify_init(snum, notify, ev);
127 static NTSTATUS notify_lock(struct notify_context *notify)
129 if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
130 return NT_STATUS_INTERNAL_DB_CORRUPTION;
138 static void notify_unlock(struct notify_context *notify)
140 tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
144 load the notify array
146 static NTSTATUS notify_load(struct notify_context *notify)
153 seqnum = tdb_get_seqnum(notify->w->tdb);
155 if (seqnum == notify->seqnum && notify->array != NULL) {
159 notify->seqnum = seqnum;
161 talloc_free(notify->array);
162 notify->array = talloc_zero(notify, struct notify_array);
163 NT_STATUS_HAVE_NO_MEMORY(notify->array);
165 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
166 if (dbuf.dptr == NULL) {
170 blob.data = dbuf.dptr;
171 blob.length = dbuf.dsize;
173 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
174 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
181 compare notify entries for sorting
183 static int notify_compare(const void *p1, const void *p2)
185 const struct notify_entry *e1 = p1, *e2 = p2;
186 return strcmp(e1->path, e2->path);
190 save the notify array
192 static NTSTATUS notify_save(struct notify_context *notify)
200 /* if possible, remove some depth arrays */
201 while (notify->array->num_depths > 0 &&
202 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
203 notify->array->num_depths--;
206 /* we might just be able to delete the record */
207 if (notify->array->num_depths == 0) {
208 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
210 return NT_STATUS_INTERNAL_DB_CORRUPTION;
215 tmp_ctx = talloc_new(notify);
217 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
218 (ndr_push_flags_fn_t)ndr_push_notify_array);
219 if (!NT_STATUS_IS_OK(status)) {
220 talloc_free(tmp_ctx);
224 dbuf.dptr = blob.data;
225 dbuf.dsize = blob.length;
227 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
228 talloc_free(tmp_ctx);
230 return NT_STATUS_INTERNAL_DB_CORRUPTION;
238 handle incoming notify messages
240 static void notify_handler(struct messaging_context *msg_ctx, void *private,
241 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data)
243 struct notify_context *notify = talloc_get_type(private, struct notify_context);
245 struct notify_event ev;
246 TALLOC_CTX *tmp_ctx = talloc_new(notify);
247 struct notify_list *listel;
249 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
250 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
251 if (!NT_STATUS_IS_OK(status)) {
252 talloc_free(tmp_ctx);
256 for (listel=notify->list;listel;listel=listel->next) {
257 if (listel->private == ev.private) {
258 listel->callback(listel->private, &ev);
263 talloc_free(tmp_ctx);
267 callback from sys_notify telling us about changes from the OS
269 static void sys_notify_callback(struct sys_notify_context *ctx,
270 void *ptr, struct notify_event *ev)
272 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
273 ev->private = listel;
274 listel->callback(listel->private, ev);
278 add an entry to the notify array
280 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
281 void *private, int depth)
284 struct notify_depth *d;
285 struct notify_entry *ee;
287 /* possibly expand the depths array */
288 if (depth >= notify->array->num_depths) {
289 d = talloc_realloc(notify->array, notify->array->depth,
290 struct notify_depth, depth+1);
291 NT_STATUS_HAVE_NO_MEMORY(d);
292 for (i=notify->array->num_depths;i<=depth;i++) {
295 notify->array->depth = d;
296 notify->array->num_depths = depth+1;
298 d = ¬ify->array->depth[depth];
300 /* expand the entries array */
301 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
303 NT_STATUS_HAVE_NO_MEMORY(ee);
306 d->entries[d->num_entries] = *e;
307 d->entries[d->num_entries].private = private;
308 d->entries[d->num_entries].server = notify->server;
309 d->entries[d->num_entries].path_len = strlen(e->path);
312 d->max_mask |= e->filter;
313 d->max_mask_subdir |= e->subdir_filter;
315 if (d->num_entries > 1) {
316 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
319 /* recalculate the maximum masks */
321 d->max_mask_subdir = 0;
323 for (i=0;i<d->num_entries;i++) {
324 d->max_mask |= d->entries[i].filter;
325 d->max_mask_subdir |= d->entries[i].subdir_filter;
328 return notify_save(notify);
332 add a notify watch. This is called when a notify is first setup on a open
335 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
336 void (*callback)(void *, const struct notify_event *),
339 struct notify_entry e = *e0;
341 char *tmp_path = NULL;
342 struct notify_list *listel;
346 /* see if change notify is enabled at all */
347 if (notify == NULL) {
348 return NT_STATUS_NOT_IMPLEMENTED;
351 status = notify_lock(notify);
352 NT_STATUS_NOT_OK_RETURN(status);
354 status = notify_load(notify);
355 if (!NT_STATUS_IS_OK(status)) {
359 /* cope with /. on the end of the path */
360 len = strlen(e.path);
361 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
362 tmp_path = talloc_strndup(notify, e.path, len-2);
363 if (tmp_path == NULL) {
364 status = NT_STATUS_NO_MEMORY;
370 depth = count_chars(e.path, '/');
372 listel = talloc_zero(notify, struct notify_list);
373 if (listel == NULL) {
374 status = NT_STATUS_NO_MEMORY;
378 listel->private = private;
379 listel->callback = callback;
380 listel->depth = depth;
381 DLIST_ADD(notify->list, listel);
383 /* ignore failures from sys_notify */
384 if (notify->sys_notify_ctx != NULL) {
386 this call will modify e.filter and e.subdir_filter
387 to remove bits handled by the backend
389 status = sys_notify_watch(notify->sys_notify_ctx, &e,
390 sys_notify_callback, listel,
391 &listel->sys_notify_handle);
392 if (NT_STATUS_IS_OK(status)) {
393 talloc_steal(listel, listel->sys_notify_handle);
397 /* if the system notify handler couldn't handle some of the
398 filter bits, or couldn't handle a request for recursion
399 then we need to install it in the array used for the
400 intra-samba notify handling */
401 if (e.filter != 0 || e.subdir_filter != 0) {
402 status = notify_add_array(notify, &e, private, depth);
406 notify_unlock(notify);
407 talloc_free(tmp_path);
413 remove a notify watch. Called when the directory handle is closed
415 NTSTATUS notify_remove(struct notify_context *notify, void *private)
418 struct notify_list *listel;
420 struct notify_depth *d;
422 /* see if change notify is enabled at all */
423 if (notify == NULL) {
424 return NT_STATUS_NOT_IMPLEMENTED;
427 for (listel=notify->list;listel;listel=listel->next) {
428 if (listel->private == private) {
429 DLIST_REMOVE(notify->list, listel);
433 if (listel == NULL) {
434 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
437 depth = listel->depth;
441 status = notify_lock(notify);
442 NT_STATUS_NOT_OK_RETURN(status);
444 status = notify_load(notify);
445 if (!NT_STATUS_IS_OK(status)) {
446 notify_unlock(notify);
450 /* we only have to search at the depth of this element */
451 d = ¬ify->array->depth[depth];
453 for (i=0;i<d->num_entries;i++) {
454 if (private == d->entries[i].private &&
455 notify->server == d->entries[i].server) {
459 if (i == d->num_entries) {
460 notify_unlock(notify);
461 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
464 if (i < d->num_entries-1) {
465 memmove(&d->entries[i], &d->entries[i+1],
466 sizeof(d->entries[i])*(d->num_entries-(i+1)));
470 status = notify_save(notify);
472 notify_unlock(notify);
478 remove all notify watches for this messaging server
480 static NTSTATUS notify_remove_all(struct notify_context *notify)
483 int i, depth, del_count=0;
485 if (notify->list == NULL) {
489 status = notify_lock(notify);
490 NT_STATUS_NOT_OK_RETURN(status);
492 status = notify_load(notify);
493 if (!NT_STATUS_IS_OK(status)) {
494 notify_unlock(notify);
498 /* we have to search for all entries across all depths, looking for matches
500 for (depth=0;depth<notify->array->num_depths;depth++) {
501 struct notify_depth *d = ¬ify->array->depth[depth];
502 for (i=0;i<d->num_entries;i++) {
503 if (notify->server == d->entries[i].server) {
504 if (i < d->num_entries-1) {
505 memmove(&d->entries[i], &d->entries[i+1],
506 sizeof(d->entries[i])*(d->num_entries-(i+1)));
516 status = notify_save(notify);
519 notify_unlock(notify);
526 send a notify message to another messaging server
528 static void notify_send(struct notify_context *notify, struct notify_entry *e,
529 const char *path, uint32_t action)
531 struct notify_event ev;
538 ev.private = e->private;
540 tmp_ctx = talloc_new(notify);
542 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
543 (ndr_push_flags_fn_t)ndr_push_notify_event);
544 if (!NT_STATUS_IS_OK(status)) {
545 talloc_free(tmp_ctx);
549 status = messaging_send(notify->messaging_ctx, e->server,
550 MSG_PVFS_NOTIFY, &data);
551 talloc_free(tmp_ctx);
556 trigger a notify message for anyone waiting on a matching event
558 This function is called a lot, and needs to be very fast. The unusual data structure
559 and traversal is designed to be fast in the average case, even for large numbers of
562 void notify_trigger(struct notify_context *notify,
563 uint32_t action, uint32_t filter, const char *path)
567 const char *p, *next_p;
569 /* see if change notify is enabled at all */
570 if (notify == NULL) {
574 status = notify_load(notify);
575 if (!NT_STATUS_IS_OK(status)) {
579 /* loop along the given path, working with each directory depth separately */
581 p && depth < notify->array->num_depths;
583 int p_len = p - path;
585 struct notify_depth *d = ¬ify->array->depth[depth];
586 next_p = strchr(p+1, '/');
588 /* see if there are any entries at this depth */
589 if (d->num_entries == 0) continue;
591 /* try to skip based on the maximum mask. If next_p is
592 NULL then we know it will be a 'this directory'
593 match, otherwise it must be a subdir match */
594 if (next_p != NULL) {
595 if (0 == (filter & d->max_mask_subdir)) {
599 if (0 == (filter & d->max_mask)) {
604 /* we know there is an entry here worth looking
605 for. Use a bisection search to find the first entry
606 with a matching path */
608 max_i = d->num_entries-1;
610 while (min_i < max_i) {
611 struct notify_entry *e;
614 int cmp = strncmp(path, e->path, p_len);
616 if (p_len == e->path_len) {
621 } else if (cmp < 0) {
628 if (min_i != max_i) {
633 /* we now know that the entries start at min_i */
634 for (i=min_i;i<d->num_entries;i++) {
635 struct notify_entry *e = &d->entries[i];
636 if (p_len != e->path_len ||
637 strncmp(path, e->path, p_len) != 0) break;
638 if (next_p != NULL) {
639 if (0 == (filter & e->subdir_filter)) {
643 if (0 == (filter & e->filter)) {
647 notify_send(notify, e, path + e->path_len + 1, action);