tdb: Make "tdb_purge_dead" internally public
[Samba.git] / source4 / ntvfs / common / notify.c
blob445f38f742b7125ca084fe504fb47778719e495b
1 /*
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 3 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, see <http://www.gnu.org/licenses/>.
21 this is the change notify database. It implements mechanisms for
22 storing current change notify waiters in a tdb, and checking if a
23 given event matches any of the stored notify waiiters.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "messaging/messaging.h"
29 #include "lib/messaging/irpc.h"
30 #include "librpc/gen_ndr/ndr_notify.h"
31 #include "../lib/util/dlinklist.h"
32 #include "ntvfs/common/ntvfs_common.h"
33 #include "ntvfs/sysdep/sys_notify.h"
34 #include "cluster/cluster.h"
35 #include "param/param.h"
36 #include "lib/util/tsort.h"
37 #include "lib/dbwrap/dbwrap.h"
38 #include "../lib/util/util_tdb.h"
40 struct notify_context {
41 struct db_context *db;
42 struct server_id server;
43 struct imessaging_context *imessaging_ctx;
44 struct notify_list *list;
45 struct notify_array *array;
46 int64_t seqnum;
47 struct sys_notify_context *sys_notify_ctx;
51 struct notify_list {
52 struct notify_list *next, *prev;
53 void *private_data;
54 void (*callback)(void *, const struct notify_event *);
55 void *sys_notify_handle;
56 int depth;
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 imessaging_context *msg_ctx, void *private_data,
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 imessaging_deregister(notify->imessaging_ctx, MSG_PVFS_NOTIFY, notify);
74 notify_remove_all(notify);
75 return 0;
79 Open up the notify.tdb database. You should close it down using
80 talloc_free(). We need the imessaging_ctx to allow for notifications
81 via internal messages
83 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
84 struct imessaging_context *imessaging_ctx,
85 struct loadparm_context *lp_ctx,
86 struct tevent_context *ev,
87 struct share_config *scfg)
89 struct notify_context *notify;
91 if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != true) {
92 return NULL;
95 if (ev == NULL) {
96 return NULL;
99 notify = talloc(mem_ctx, struct notify_context);
100 if (notify == NULL) {
101 return NULL;
104 notify->db = cluster_db_tmp_open(notify, lp_ctx, "notify", TDB_SEQNUM);
105 if (notify->db == NULL) {
106 talloc_free(notify);
107 return NULL;
110 notify->server = server;
111 notify->imessaging_ctx = imessaging_ctx;
112 notify->list = NULL;
113 notify->array = NULL;
114 notify->seqnum = dbwrap_get_seqnum(notify->db);
116 talloc_set_destructor(notify, notify_destructor);
118 /* register with the messaging subsystem for the notify
119 message type */
120 imessaging_register(notify->imessaging_ctx, notify,
121 MSG_PVFS_NOTIFY, notify_handler);
123 notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
125 return notify;
130 lock the notify db
132 static struct db_record *notify_lock(struct notify_context *notify)
134 TDB_DATA key = string_term_tdb_data(NOTIFY_KEY);
136 return dbwrap_fetch_locked(notify->db, notify, key);
139 static void notify_unlock(struct db_record *lock)
141 talloc_free(lock);
145 load the notify array
147 static NTSTATUS notify_load(struct notify_context *notify)
149 TDB_DATA dbuf;
150 DATA_BLOB blob;
151 enum ndr_err_code ndr_err;
152 int seqnum;
153 NTSTATUS status;
155 seqnum = dbwrap_get_seqnum(notify->db);
157 if (seqnum == notify->seqnum && notify->array != NULL) {
158 return NT_STATUS_OK;
161 notify->seqnum = seqnum;
163 talloc_free(notify->array);
164 notify->array = talloc_zero(notify, struct notify_array);
165 NT_STATUS_HAVE_NO_MEMORY(notify->array);
167 status = dbwrap_fetch_bystring(notify->db, notify, NOTIFY_KEY, &dbuf);
168 if (!NT_STATUS_IS_OK(status)) {
169 return NT_STATUS_OK;
172 blob.data = dbuf.dptr;
173 blob.length = dbuf.dsize;
175 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
176 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
177 talloc_free(dbuf.dptr);
178 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
179 return ndr_map_error2ntstatus(ndr_err);
182 return NT_STATUS_OK;
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)
199 TDB_DATA dbuf;
200 DATA_BLOB blob;
201 enum ndr_err_code ndr_err;
202 TALLOC_CTX *tmp_ctx;
203 NTSTATUS status;
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 return dbwrap_delete_bystring(notify->db, NOTIFY_KEY);
216 tmp_ctx = talloc_new(notify);
217 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
219 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
220 (ndr_push_flags_fn_t)ndr_push_notify_array);
221 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
222 talloc_free(tmp_ctx);
223 return ndr_map_error2ntstatus(ndr_err);
226 dbuf.dptr = blob.data;
227 dbuf.dsize = blob.length;
229 status = dbwrap_store_bystring(notify->db, NOTIFY_KEY, dbuf,
230 TDB_REPLACE);
231 talloc_free(tmp_ctx);
232 return status;
237 handle incoming notify messages
239 static void notify_handler(struct imessaging_context *msg_ctx, void *private_data,
240 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
242 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
243 enum ndr_err_code ndr_err;
244 struct notify_event ev;
245 TALLOC_CTX *tmp_ctx = talloc_new(notify);
246 struct notify_list *listel;
248 if (tmp_ctx == NULL) {
249 return;
252 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
253 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
255 talloc_free(tmp_ctx);
256 return;
259 for (listel=notify->list;listel;listel=listel->next) {
260 if (listel->private_data == ev.private_data) {
261 listel->callback(listel->private_data, &ev);
262 break;
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_data = listel;
277 listel->callback(listel->private_data, 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_data, int depth)
286 int i;
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++) {
296 ZERO_STRUCT(d[i]);
298 notify->array->depth = d;
299 notify->array->num_depths = depth+1;
301 d = &notify->array->depth[depth];
303 /* expand the entries array */
304 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
305 d->num_entries+1);
306 NT_STATUS_HAVE_NO_MEMORY(ee);
307 d->entries = ee;
309 d->entries[d->num_entries] = *e;
310 d->entries[d->num_entries].private_data = private_data;
311 d->entries[d->num_entries].server = notify->server;
312 d->entries[d->num_entries].path_len = strlen(e->path);
313 d->num_entries++;
315 d->max_mask |= e->filter;
316 d->max_mask_subdir |= e->subdir_filter;
318 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
320 /* recalculate the maximum masks */
321 d->max_mask = 0;
322 d->max_mask_subdir = 0;
324 for (i=0;i<d->num_entries;i++) {
325 d->max_mask |= d->entries[i].filter;
326 d->max_mask_subdir |= d->entries[i].subdir_filter;
329 return notify_save(notify);
333 add a notify watch. This is called when a notify is first setup on a open
334 directory handle.
336 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
337 void (*callback)(void *, const struct notify_event *),
338 void *private_data)
340 struct notify_entry e = *e0;
341 NTSTATUS status;
342 char *tmp_path = NULL;
343 struct notify_list *listel;
344 size_t len;
345 int depth;
346 struct db_record *locked;
348 /* see if change notify is enabled at all */
349 if (notify == NULL) {
350 return NT_STATUS_NOT_IMPLEMENTED;
353 locked = notify_lock(notify);
354 if (!locked) {
355 return NT_STATUS_INTERNAL_DB_CORRUPTION;
358 status = notify_load(notify);
359 if (!NT_STATUS_IS_OK(status)) {
360 goto done;
363 /* cope with /. on the end of the path */
364 len = strlen(e.path);
365 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
366 tmp_path = talloc_strndup(notify, e.path, len-2);
367 if (tmp_path == NULL) {
368 status = NT_STATUS_NO_MEMORY;
369 goto done;
371 e.path = tmp_path;
374 depth = count_chars(e.path, '/');
376 listel = talloc_zero(notify, struct notify_list);
377 if (listel == NULL) {
378 status = NT_STATUS_NO_MEMORY;
379 goto done;
382 listel->private_data = private_data;
383 listel->callback = callback;
384 listel->depth = depth;
385 DLIST_ADD(notify->list, listel);
387 /* ignore failures from sys_notify */
388 if (notify->sys_notify_ctx != NULL) {
390 this call will modify e.filter and e.subdir_filter
391 to remove bits handled by the backend
393 status = sys_notify_watch(notify->sys_notify_ctx, &e,
394 sys_notify_callback, listel,
395 &listel->sys_notify_handle);
396 if (NT_STATUS_IS_OK(status)) {
397 talloc_steal(listel, listel->sys_notify_handle);
401 /* if the system notify handler couldn't handle some of the
402 filter bits, or couldn't handle a request for recursion
403 then we need to install it in the array used for the
404 intra-samba notify handling */
405 if (e.filter != 0 || e.subdir_filter != 0) {
406 status = notify_add_array(notify, &e, private_data, depth);
409 done:
410 notify_unlock(locked);
411 talloc_free(tmp_path);
413 return status;
417 remove a notify watch. Called when the directory handle is closed
419 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
421 NTSTATUS status;
422 struct notify_list *listel;
423 int i, depth;
424 struct notify_depth *d;
425 struct db_record *locked;
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_data == private_data) {
434 DLIST_REMOVE(notify->list, listel);
435 break;
438 if (listel == NULL) {
439 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
442 depth = listel->depth;
444 talloc_free(listel);
446 locked = notify_lock(notify);
447 if (!locked) {
448 return NT_STATUS_INTERNAL_DB_CORRUPTION;
451 status = notify_load(notify);
452 if (!NT_STATUS_IS_OK(status)) {
453 notify_unlock(locked);
454 return status;
457 if (depth >= notify->array->num_depths) {
458 notify_unlock(locked);
459 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
462 /* we only have to search at the depth of this element */
463 d = &notify->array->depth[depth];
465 for (i=0;i<d->num_entries;i++) {
466 if (private_data == d->entries[i].private_data &&
467 cluster_id_equal(&notify->server, &d->entries[i].server)) {
468 break;
471 if (i == d->num_entries) {
472 notify_unlock(locked);
473 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
476 if (i < d->num_entries-1) {
477 memmove(&d->entries[i], &d->entries[i+1],
478 sizeof(d->entries[i])*(d->num_entries-(i+1)));
480 d->num_entries--;
482 status = notify_save(notify);
484 notify_unlock(locked);
486 return status;
490 remove all notify watches for this messaging server
492 static NTSTATUS notify_remove_all(struct notify_context *notify)
494 NTSTATUS status;
495 int i, depth, del_count=0;
496 struct db_record *locked;
498 if (notify->list == NULL) {
499 return NT_STATUS_OK;
502 locked = notify_lock(notify);
503 if (!locked) {
504 return NT_STATUS_INTERNAL_DB_CORRUPTION;
507 status = notify_load(notify);
508 if (!NT_STATUS_IS_OK(status)) {
509 notify_unlock(locked);
510 return status;
513 /* we have to search for all entries across all depths, looking for matches
514 for our server id */
515 for (depth=0;depth<notify->array->num_depths;depth++) {
516 struct notify_depth *d = &notify->array->depth[depth];
517 for (i=0;i<d->num_entries;i++) {
518 if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
519 if (i < d->num_entries-1) {
520 memmove(&d->entries[i], &d->entries[i+1],
521 sizeof(d->entries[i])*(d->num_entries-(i+1)));
523 i--;
524 d->num_entries--;
525 del_count++;
530 if (del_count > 0) {
531 status = notify_save(notify);
534 notify_unlock(locked);
536 return status;
541 send a notify message to another messaging server
543 static void notify_send(struct notify_context *notify, struct notify_entry *e,
544 const char *path, uint32_t action)
546 struct notify_event ev;
547 DATA_BLOB data;
548 NTSTATUS status;
549 enum ndr_err_code ndr_err;
550 TALLOC_CTX *tmp_ctx;
552 ev.action = action;
553 ev.path = path;
554 ev.private_data = e->private_data;
556 tmp_ctx = talloc_new(notify);
558 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev, (ndr_push_flags_fn_t)ndr_push_notify_event);
559 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
560 talloc_free(tmp_ctx);
561 return;
564 status = imessaging_send(notify->imessaging_ctx, e->server,
565 MSG_PVFS_NOTIFY, &data);
566 talloc_free(tmp_ctx);
571 trigger a notify message for anyone waiting on a matching event
573 This function is called a lot, and needs to be very fast. The unusual data structure
574 and traversal is designed to be fast in the average case, even for large numbers of
575 notifies
577 void notify_trigger(struct notify_context *notify,
578 uint32_t action, uint32_t filter, const char *path)
580 NTSTATUS status;
581 int depth;
582 const char *p, *next_p;
584 /* see if change notify is enabled at all */
585 if (notify == NULL) {
586 return;
589 status = notify_load(notify);
590 if (!NT_STATUS_IS_OK(status)) {
591 return;
594 /* loop along the given path, working with each directory depth separately */
595 for (depth=0,p=path;
596 p && depth < notify->array->num_depths;
597 p=next_p,depth++) {
598 int p_len = p - path;
599 int min_i, max_i, i;
600 struct notify_depth *d = &notify->array->depth[depth];
601 next_p = strchr(p+1, '/');
603 /* see if there are any entries at this depth */
604 if (d->num_entries == 0) continue;
606 /* try to skip based on the maximum mask. If next_p is
607 NULL then we know it will be a 'this directory'
608 match, otherwise it must be a subdir match */
609 if (next_p != NULL) {
610 if (0 == (filter & d->max_mask_subdir)) {
611 continue;
613 } else {
614 if (0 == (filter & d->max_mask)) {
615 continue;
619 /* we know there is an entry here worth looking
620 for. Use a bisection search to find the first entry
621 with a matching path */
622 min_i = 0;
623 max_i = d->num_entries-1;
625 while (min_i < max_i) {
626 struct notify_entry *e;
627 int cmp;
628 i = (min_i+max_i)/2;
629 e = &d->entries[i];
630 cmp = strncmp(path, e->path, p_len);
631 if (cmp == 0) {
632 if (p_len == e->path_len) {
633 max_i = i;
634 } else {
635 max_i = i-1;
637 } else if (cmp < 0) {
638 max_i = i-1;
639 } else {
640 min_i = i+1;
644 if (min_i != max_i) {
645 /* none match */
646 continue;
649 /* we now know that the entries start at min_i */
650 for (i=min_i;i<d->num_entries;i++) {
651 struct notify_entry *e = &d->entries[i];
652 if (p_len != e->path_len ||
653 strncmp(path, e->path, p_len) != 0) break;
654 if (next_p != NULL) {
655 if (0 == (filter & e->subdir_filter)) {
656 continue;
658 } else {
659 if (0 == (filter & e->filter)) {
660 continue;
663 notify_send(notify, e, path + e->path_len + 1, action);