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/util/util_tdb.h"
31 #include "messaging/messaging.h"
33 #include "lib/messaging/irpc.h"
34 #include "librpc/gen_ndr/ndr_notify.h"
35 #include "lib/util/dlinklist.h"
36 #include "ntvfs/common/ntvfs_common.h"
37 #include "ntvfs/sysdep/sys_notify.h"
38 #include "cluster/cluster.h"
40 struct notify_context {
42 struct server_id server;
43 struct messaging_context *messaging_ctx;
44 struct notify_list *list;
45 struct notify_array *array;
47 struct sys_notify_context *sys_notify_ctx;
52 struct notify_list *next, *prev;
54 void (*callback)(void *, const struct notify_event *);
55 void *sys_notify_handle;
59 #define NOTIFY_KEY "notify array"
61 #define NOTIFY_ENABLE "notify:enable"
62 #define NOTIFY_ENABLE_DEFAULT True
64 static NTSTATUS notify_remove_all(struct notify_context *notify);
65 static void notify_handler(struct messaging_context *msg_ctx, void *private,
66 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
69 destroy the notify context
71 static int notify_destructor(struct notify_context *notify)
73 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
74 notify_remove_all(notify);
79 Open up the notify.tdb database. You should close it down using
80 talloc_free(). We need the messaging_ctx to allow for notifications
83 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
84 struct messaging_context *messaging_ctx,
85 struct event_context *ev,
86 struct share_config *scfg)
89 struct notify_context *notify;
91 if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != True) {
95 notify = talloc(mem_ctx, struct notify_context);
100 path = smbd_tmp_path(notify, "notify.tdb");
101 notify->w = tdb_wrap_open(notify, path, 0,
103 O_RDWR|O_CREAT, 0600);
105 if (notify->w == NULL) {
110 notify->server = server;
111 notify->messaging_ctx = messaging_ctx;
113 notify->array = NULL;
114 notify->seqnum = tdb_get_seqnum(notify->w->tdb);
116 talloc_set_destructor(notify, notify_destructor);
118 /* register with the messaging subsystem for the notify
120 messaging_register(notify->messaging_ctx, notify,
121 MSG_PVFS_NOTIFY, notify_handler);
123 notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
132 static NTSTATUS notify_lock(struct notify_context *notify)
134 if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
135 return NT_STATUS_INTERNAL_DB_CORRUPTION;
143 static void notify_unlock(struct notify_context *notify)
145 tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
149 load the notify array
151 static NTSTATUS notify_load(struct notify_context *notify)
158 seqnum = tdb_get_seqnum(notify->w->tdb);
160 if (seqnum == notify->seqnum && notify->array != NULL) {
164 notify->seqnum = seqnum;
166 talloc_free(notify->array);
167 notify->array = talloc_zero(notify, struct notify_array);
168 NT_STATUS_HAVE_NO_MEMORY(notify->array);
170 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
171 if (dbuf.dptr == NULL) {
175 blob.data = dbuf.dptr;
176 blob.length = dbuf.dsize;
178 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
179 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
186 compare notify entries for sorting
188 static int notify_compare(const void *p1, const void *p2)
190 const struct notify_entry *e1 = p1, *e2 = p2;
191 return strcmp(e1->path, e2->path);
195 save the notify array
197 static NTSTATUS notify_save(struct notify_context *notify)
205 /* if possible, remove some depth arrays */
206 while (notify->array->num_depths > 0 &&
207 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
208 notify->array->num_depths--;
211 /* we might just be able to delete the record */
212 if (notify->array->num_depths == 0) {
213 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
215 return NT_STATUS_INTERNAL_DB_CORRUPTION;
220 tmp_ctx = talloc_new(notify);
222 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
223 (ndr_push_flags_fn_t)ndr_push_notify_array);
224 if (!NT_STATUS_IS_OK(status)) {
225 talloc_free(tmp_ctx);
229 dbuf.dptr = blob.data;
230 dbuf.dsize = blob.length;
232 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
233 talloc_free(tmp_ctx);
235 return NT_STATUS_INTERNAL_DB_CORRUPTION;
243 handle incoming notify messages
245 static void notify_handler(struct messaging_context *msg_ctx, void *private,
246 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
248 struct notify_context *notify = talloc_get_type(private, struct notify_context);
250 struct notify_event ev;
251 TALLOC_CTX *tmp_ctx = talloc_new(notify);
252 struct notify_list *listel;
254 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
255 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
256 if (!NT_STATUS_IS_OK(status)) {
257 talloc_free(tmp_ctx);
261 for (listel=notify->list;listel;listel=listel->next) {
262 if (listel->private == ev.private) {
263 listel->callback(listel->private, &ev);
268 talloc_free(tmp_ctx);
272 callback from sys_notify telling us about changes from the OS
274 static void sys_notify_callback(struct sys_notify_context *ctx,
275 void *ptr, struct notify_event *ev)
277 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
278 ev->private = listel;
279 listel->callback(listel->private, ev);
283 add an entry to the notify array
285 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
286 void *private, int depth)
289 struct notify_depth *d;
290 struct notify_entry *ee;
292 /* possibly expand the depths array */
293 if (depth >= notify->array->num_depths) {
294 d = talloc_realloc(notify->array, notify->array->depth,
295 struct notify_depth, depth+1);
296 NT_STATUS_HAVE_NO_MEMORY(d);
297 for (i=notify->array->num_depths;i<=depth;i++) {
300 notify->array->depth = d;
301 notify->array->num_depths = depth+1;
303 d = ¬ify->array->depth[depth];
305 /* expand the entries array */
306 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
308 NT_STATUS_HAVE_NO_MEMORY(ee);
311 d->entries[d->num_entries] = *e;
312 d->entries[d->num_entries].private = private;
313 d->entries[d->num_entries].server = notify->server;
314 d->entries[d->num_entries].path_len = strlen(e->path);
317 d->max_mask |= e->filter;
318 d->max_mask_subdir |= e->subdir_filter;
320 if (d->num_entries > 1) {
321 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
324 /* recalculate the maximum masks */
326 d->max_mask_subdir = 0;
328 for (i=0;i<d->num_entries;i++) {
329 d->max_mask |= d->entries[i].filter;
330 d->max_mask_subdir |= d->entries[i].subdir_filter;
333 return notify_save(notify);
337 add a notify watch. This is called when a notify is first setup on a open
340 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
341 void (*callback)(void *, const struct notify_event *),
344 struct notify_entry e = *e0;
346 char *tmp_path = NULL;
347 struct notify_list *listel;
351 /* see if change notify is enabled at all */
352 if (notify == NULL) {
353 return NT_STATUS_NOT_IMPLEMENTED;
356 status = notify_lock(notify);
357 NT_STATUS_NOT_OK_RETURN(status);
359 status = notify_load(notify);
360 if (!NT_STATUS_IS_OK(status)) {
364 /* cope with /. on the end of the path */
365 len = strlen(e.path);
366 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
367 tmp_path = talloc_strndup(notify, e.path, len-2);
368 if (tmp_path == NULL) {
369 status = NT_STATUS_NO_MEMORY;
375 depth = count_chars(e.path, '/');
377 listel = talloc_zero(notify, struct notify_list);
378 if (listel == NULL) {
379 status = NT_STATUS_NO_MEMORY;
383 listel->private = private;
384 listel->callback = callback;
385 listel->depth = depth;
386 DLIST_ADD(notify->list, listel);
388 /* ignore failures from sys_notify */
389 if (notify->sys_notify_ctx != NULL) {
391 this call will modify e.filter and e.subdir_filter
392 to remove bits handled by the backend
394 status = sys_notify_watch(notify->sys_notify_ctx, &e,
395 sys_notify_callback, listel,
396 &listel->sys_notify_handle);
397 if (NT_STATUS_IS_OK(status)) {
398 talloc_steal(listel, listel->sys_notify_handle);
402 /* if the system notify handler couldn't handle some of the
403 filter bits, or couldn't handle a request for recursion
404 then we need to install it in the array used for the
405 intra-samba notify handling */
406 if (e.filter != 0 || e.subdir_filter != 0) {
407 status = notify_add_array(notify, &e, private, depth);
411 notify_unlock(notify);
412 talloc_free(tmp_path);
418 remove a notify watch. Called when the directory handle is closed
420 NTSTATUS notify_remove(struct notify_context *notify, void *private)
423 struct notify_list *listel;
425 struct notify_depth *d;
427 /* see if change notify is enabled at all */
428 if (notify == NULL) {
429 return NT_STATUS_NOT_IMPLEMENTED;
432 for (listel=notify->list;listel;listel=listel->next) {
433 if (listel->private == private) {
434 DLIST_REMOVE(notify->list, listel);
438 if (listel == NULL) {
439 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
442 depth = listel->depth;
446 status = notify_lock(notify);
447 NT_STATUS_NOT_OK_RETURN(status);
449 status = notify_load(notify);
450 if (!NT_STATUS_IS_OK(status)) {
451 notify_unlock(notify);
455 if (depth >= notify->array->num_depths) {
456 notify_unlock(notify);
457 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
460 /* we only have to search at the depth of this element */
461 d = ¬ify->array->depth[depth];
463 for (i=0;i<d->num_entries;i++) {
464 if (private == d->entries[i].private &&
465 cluster_id_equal(¬ify->server, &d->entries[i].server)) {
469 if (i == d->num_entries) {
470 notify_unlock(notify);
471 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
474 if (i < d->num_entries-1) {
475 memmove(&d->entries[i], &d->entries[i+1],
476 sizeof(d->entries[i])*(d->num_entries-(i+1)));
480 status = notify_save(notify);
482 notify_unlock(notify);
488 remove all notify watches for this messaging server
490 static NTSTATUS notify_remove_all(struct notify_context *notify)
493 int i, depth, del_count=0;
495 if (notify->list == NULL) {
499 status = notify_lock(notify);
500 NT_STATUS_NOT_OK_RETURN(status);
502 status = notify_load(notify);
503 if (!NT_STATUS_IS_OK(status)) {
504 notify_unlock(notify);
508 /* we have to search for all entries across all depths, looking for matches
510 for (depth=0;depth<notify->array->num_depths;depth++) {
511 struct notify_depth *d = ¬ify->array->depth[depth];
512 for (i=0;i<d->num_entries;i++) {
513 if (cluster_id_equal(¬ify->server, &d->entries[i].server)) {
514 if (i < d->num_entries-1) {
515 memmove(&d->entries[i], &d->entries[i+1],
516 sizeof(d->entries[i])*(d->num_entries-(i+1)));
526 status = notify_save(notify);
529 notify_unlock(notify);
536 send a notify message to another messaging server
538 static void notify_send(struct notify_context *notify, struct notify_entry *e,
539 const char *path, uint32_t action)
541 struct notify_event ev;
548 ev.private = e->private;
550 tmp_ctx = talloc_new(notify);
552 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
553 (ndr_push_flags_fn_t)ndr_push_notify_event);
554 if (!NT_STATUS_IS_OK(status)) {
555 talloc_free(tmp_ctx);
559 status = messaging_send(notify->messaging_ctx, e->server,
560 MSG_PVFS_NOTIFY, &data);
561 talloc_free(tmp_ctx);
566 trigger a notify message for anyone waiting on a matching event
568 This function is called a lot, and needs to be very fast. The unusual data structure
569 and traversal is designed to be fast in the average case, even for large numbers of
572 void notify_trigger(struct notify_context *notify,
573 uint32_t action, uint32_t filter, const char *path)
577 const char *p, *next_p;
579 /* see if change notify is enabled at all */
580 if (notify == NULL) {
584 status = notify_load(notify);
585 if (!NT_STATUS_IS_OK(status)) {
589 /* loop along the given path, working with each directory depth separately */
591 p && depth < notify->array->num_depths;
593 int p_len = p - path;
595 struct notify_depth *d = ¬ify->array->depth[depth];
596 next_p = strchr(p+1, '/');
598 /* see if there are any entries at this depth */
599 if (d->num_entries == 0) continue;
601 /* try to skip based on the maximum mask. If next_p is
602 NULL then we know it will be a 'this directory'
603 match, otherwise it must be a subdir match */
604 if (next_p != NULL) {
605 if (0 == (filter & d->max_mask_subdir)) {
609 if (0 == (filter & d->max_mask)) {
614 /* we know there is an entry here worth looking
615 for. Use a bisection search to find the first entry
616 with a matching path */
618 max_i = d->num_entries-1;
620 while (min_i < max_i) {
621 struct notify_entry *e;
625 cmp = strncmp(path, e->path, p_len);
627 if (p_len == e->path_len) {
632 } else if (cmp < 0) {
639 if (min_i != max_i) {
644 /* we now know that the entries start at min_i */
645 for (i=min_i;i<d->num_entries;i++) {
646 struct notify_entry *e = &d->entries[i];
647 if (p_len != e->path_len ||
648 strncmp(path, e->path, p_len) != 0) break;
649 if (next_p != NULL) {
650 if (0 == (filter & e->subdir_filter)) {
654 if (0 == (filter & e->filter)) {
658 notify_send(notify, e, path + e->path_len + 1, action);