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 "lib/util/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 #define NOTIFY_ENABLE "notify:enable"
60 #define NOTIFY_ENABLE_DEFAULT True
62 static NTSTATUS notify_remove_all(struct notify_context *notify);
63 static void notify_handler(struct messaging_context *msg_ctx, void *private,
64 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
67 destroy the notify context
69 static int notify_destructor(struct notify_context *notify)
71 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
72 notify_remove_all(notify);
77 Open up the notify.tdb database. You should close it down using
78 talloc_free(). We need the messaging_ctx to allow for notifications
81 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server,
82 struct messaging_context *messaging_ctx,
83 struct event_context *ev,
84 struct share_config *scfg)
87 struct notify_context *notify;
89 if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != True) {
93 notify = talloc(mem_ctx, struct notify_context);
98 path = smbd_tmp_path(notify, "notify.tdb");
99 notify->w = tdb_wrap_open(notify, path, 0,
101 O_RDWR|O_CREAT, 0600);
103 if (notify->w == NULL) {
108 notify->server = server;
109 notify->messaging_ctx = messaging_ctx;
111 notify->array = NULL;
112 notify->seqnum = tdb_get_seqnum(notify->w->tdb);
114 talloc_set_destructor(notify, notify_destructor);
116 /* register with the messaging subsystem for the notify
118 messaging_register(notify->messaging_ctx, notify,
119 MSG_PVFS_NOTIFY, notify_handler);
121 notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
130 static NTSTATUS notify_lock(struct notify_context *notify)
132 if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
133 return NT_STATUS_INTERNAL_DB_CORRUPTION;
141 static void notify_unlock(struct notify_context *notify)
143 tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
147 load the notify array
149 static NTSTATUS notify_load(struct notify_context *notify)
156 seqnum = tdb_get_seqnum(notify->w->tdb);
158 if (seqnum == notify->seqnum && notify->array != NULL) {
162 notify->seqnum = seqnum;
164 talloc_free(notify->array);
165 notify->array = talloc_zero(notify, struct notify_array);
166 NT_STATUS_HAVE_NO_MEMORY(notify->array);
168 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
169 if (dbuf.dptr == NULL) {
173 blob.data = dbuf.dptr;
174 blob.length = dbuf.dsize;
176 status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
177 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
184 compare notify entries for sorting
186 static int notify_compare(const void *p1, const void *p2)
188 const struct notify_entry *e1 = p1, *e2 = p2;
189 return strcmp(e1->path, e2->path);
193 save the notify array
195 static NTSTATUS notify_save(struct notify_context *notify)
203 /* if possible, remove some depth arrays */
204 while (notify->array->num_depths > 0 &&
205 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
206 notify->array->num_depths--;
209 /* we might just be able to delete the record */
210 if (notify->array->num_depths == 0) {
211 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
213 return NT_STATUS_INTERNAL_DB_CORRUPTION;
218 tmp_ctx = talloc_new(notify);
220 status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
221 (ndr_push_flags_fn_t)ndr_push_notify_array);
222 if (!NT_STATUS_IS_OK(status)) {
223 talloc_free(tmp_ctx);
227 dbuf.dptr = blob.data;
228 dbuf.dsize = blob.length;
230 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
231 talloc_free(tmp_ctx);
233 return NT_STATUS_INTERNAL_DB_CORRUPTION;
241 handle incoming notify messages
243 static void notify_handler(struct messaging_context *msg_ctx, void *private,
244 uint32_t msg_type, uint32_t server_id, DATA_BLOB *data)
246 struct notify_context *notify = talloc_get_type(private, struct notify_context);
248 struct notify_event ev;
249 TALLOC_CTX *tmp_ctx = talloc_new(notify);
250 struct notify_list *listel;
252 status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
253 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
254 if (!NT_STATUS_IS_OK(status)) {
255 talloc_free(tmp_ctx);
259 for (listel=notify->list;listel;listel=listel->next) {
260 if (listel->private == ev.private) {
261 listel->callback(listel->private, &ev);
266 talloc_free(tmp_ctx);
270 callback from sys_notify telling us about changes from the OS
272 static void sys_notify_callback(struct sys_notify_context *ctx,
273 void *ptr, struct notify_event *ev)
275 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
276 ev->private = listel;
277 listel->callback(listel->private, ev);
281 add an entry to the notify array
283 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
284 void *private, int depth)
287 struct notify_depth *d;
288 struct notify_entry *ee;
290 /* possibly expand the depths array */
291 if (depth >= notify->array->num_depths) {
292 d = talloc_realloc(notify->array, notify->array->depth,
293 struct notify_depth, depth+1);
294 NT_STATUS_HAVE_NO_MEMORY(d);
295 for (i=notify->array->num_depths;i<=depth;i++) {
298 notify->array->depth = d;
299 notify->array->num_depths = depth+1;
301 d = ¬ify->array->depth[depth];
303 /* expand the entries array */
304 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
306 NT_STATUS_HAVE_NO_MEMORY(ee);
309 d->entries[d->num_entries] = *e;
310 d->entries[d->num_entries].private = private;
311 d->entries[d->num_entries].server = notify->server;
312 d->entries[d->num_entries].path_len = strlen(e->path);
315 d->max_mask |= e->filter;
316 d->max_mask_subdir |= e->subdir_filter;
318 if (d->num_entries > 1) {
319 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
322 /* recalculate the maximum masks */
324 d->max_mask_subdir = 0;
326 for (i=0;i<d->num_entries;i++) {
327 d->max_mask |= d->entries[i].filter;
328 d->max_mask_subdir |= d->entries[i].subdir_filter;
331 return notify_save(notify);
335 add a notify watch. This is called when a notify is first setup on a open
338 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
339 void (*callback)(void *, const struct notify_event *),
342 struct notify_entry e = *e0;
344 char *tmp_path = NULL;
345 struct notify_list *listel;
349 /* see if change notify is enabled at all */
350 if (notify == NULL) {
351 return NT_STATUS_NOT_IMPLEMENTED;
354 status = notify_lock(notify);
355 NT_STATUS_NOT_OK_RETURN(status);
357 status = notify_load(notify);
358 if (!NT_STATUS_IS_OK(status)) {
362 /* cope with /. on the end of the path */
363 len = strlen(e.path);
364 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
365 tmp_path = talloc_strndup(notify, e.path, len-2);
366 if (tmp_path == NULL) {
367 status = NT_STATUS_NO_MEMORY;
373 depth = count_chars(e.path, '/');
375 listel = talloc_zero(notify, struct notify_list);
376 if (listel == NULL) {
377 status = NT_STATUS_NO_MEMORY;
381 listel->private = private;
382 listel->callback = callback;
383 listel->depth = depth;
384 DLIST_ADD(notify->list, listel);
386 /* ignore failures from sys_notify */
387 if (notify->sys_notify_ctx != NULL) {
389 this call will modify e.filter and e.subdir_filter
390 to remove bits handled by the backend
392 status = sys_notify_watch(notify->sys_notify_ctx, &e,
393 sys_notify_callback, listel,
394 &listel->sys_notify_handle);
395 if (NT_STATUS_IS_OK(status)) {
396 talloc_steal(listel, listel->sys_notify_handle);
400 /* if the system notify handler couldn't handle some of the
401 filter bits, or couldn't handle a request for recursion
402 then we need to install it in the array used for the
403 intra-samba notify handling */
404 if (e.filter != 0 || e.subdir_filter != 0) {
405 status = notify_add_array(notify, &e, private, depth);
409 notify_unlock(notify);
410 talloc_free(tmp_path);
416 remove a notify watch. Called when the directory handle is closed
418 NTSTATUS notify_remove(struct notify_context *notify, void *private)
421 struct notify_list *listel;
423 struct notify_depth *d;
425 /* see if change notify is enabled at all */
426 if (notify == NULL) {
427 return NT_STATUS_NOT_IMPLEMENTED;
430 for (listel=notify->list;listel;listel=listel->next) {
431 if (listel->private == private) {
432 DLIST_REMOVE(notify->list, listel);
436 if (listel == NULL) {
437 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
440 depth = listel->depth;
444 status = notify_lock(notify);
445 NT_STATUS_NOT_OK_RETURN(status);
447 status = notify_load(notify);
448 if (!NT_STATUS_IS_OK(status)) {
449 notify_unlock(notify);
453 if (depth >= notify->array->num_depths) {
454 notify_unlock(notify);
455 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
458 /* we only have to search at the depth of this element */
459 d = ¬ify->array->depth[depth];
461 for (i=0;i<d->num_entries;i++) {
462 if (private == d->entries[i].private &&
463 notify->server == d->entries[i].server) {
467 if (i == d->num_entries) {
468 notify_unlock(notify);
469 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
472 if (i < d->num_entries-1) {
473 memmove(&d->entries[i], &d->entries[i+1],
474 sizeof(d->entries[i])*(d->num_entries-(i+1)));
478 status = notify_save(notify);
480 notify_unlock(notify);
486 remove all notify watches for this messaging server
488 static NTSTATUS notify_remove_all(struct notify_context *notify)
491 int i, depth, del_count=0;
493 if (notify->list == NULL) {
497 status = notify_lock(notify);
498 NT_STATUS_NOT_OK_RETURN(status);
500 status = notify_load(notify);
501 if (!NT_STATUS_IS_OK(status)) {
502 notify_unlock(notify);
506 /* we have to search for all entries across all depths, looking for matches
508 for (depth=0;depth<notify->array->num_depths;depth++) {
509 struct notify_depth *d = ¬ify->array->depth[depth];
510 for (i=0;i<d->num_entries;i++) {
511 if (notify->server == d->entries[i].server) {
512 if (i < d->num_entries-1) {
513 memmove(&d->entries[i], &d->entries[i+1],
514 sizeof(d->entries[i])*(d->num_entries-(i+1)));
524 status = notify_save(notify);
527 notify_unlock(notify);
534 send a notify message to another messaging server
536 static void notify_send(struct notify_context *notify, struct notify_entry *e,
537 const char *path, uint32_t action)
539 struct notify_event ev;
546 ev.private = e->private;
548 tmp_ctx = talloc_new(notify);
550 status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
551 (ndr_push_flags_fn_t)ndr_push_notify_event);
552 if (!NT_STATUS_IS_OK(status)) {
553 talloc_free(tmp_ctx);
557 status = messaging_send(notify->messaging_ctx, e->server,
558 MSG_PVFS_NOTIFY, &data);
559 talloc_free(tmp_ctx);
564 trigger a notify message for anyone waiting on a matching event
566 This function is called a lot, and needs to be very fast. The unusual data structure
567 and traversal is designed to be fast in the average case, even for large numbers of
570 void notify_trigger(struct notify_context *notify,
571 uint32_t action, uint32_t filter, const char *path)
575 const char *p, *next_p;
577 /* see if change notify is enabled at all */
578 if (notify == NULL) {
582 status = notify_load(notify);
583 if (!NT_STATUS_IS_OK(status)) {
587 /* loop along the given path, working with each directory depth separately */
589 p && depth < notify->array->num_depths;
591 int p_len = p - path;
593 struct notify_depth *d = ¬ify->array->depth[depth];
594 next_p = strchr(p+1, '/');
596 /* see if there are any entries at this depth */
597 if (d->num_entries == 0) continue;
599 /* try to skip based on the maximum mask. If next_p is
600 NULL then we know it will be a 'this directory'
601 match, otherwise it must be a subdir match */
602 if (next_p != NULL) {
603 if (0 == (filter & d->max_mask_subdir)) {
607 if (0 == (filter & d->max_mask)) {
612 /* we know there is an entry here worth looking
613 for. Use a bisection search to find the first entry
614 with a matching path */
616 max_i = d->num_entries-1;
618 while (min_i < max_i) {
619 struct notify_entry *e;
623 cmp = strncmp(path, e->path, p_len);
625 if (p_len == e->path_len) {
630 } else if (cmp < 0) {
637 if (min_i != max_i) {
642 /* we now know that the entries start at min_i */
643 for (i=min_i;i<d->num_entries;i++) {
644 struct notify_entry *e = &d->entries[i];
645 if (p_len != e->path_len ||
646 strncmp(path, e->path, p_len) != 0) break;
647 if (next_p != NULL) {
648 if (0 == (filter & e->subdir_filter)) {
652 if (0 == (filter & e->filter)) {
656 notify_send(notify, e, path + e->path_len + 1, action);