build: check libc first for several libraries
[Samba/gebeck_regimport.git] / source3 / smbd / notify_internal.c
blobbdd4f5059e69b75b1855140da3397a8c43cd4b2d
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 "librpc/gen_ndr/ndr_notify.h"
29 struct notify_context {
30 struct db_context *db_recursive;
31 struct db_context *db_onelevel;
32 struct server_id server;
33 struct messaging_context *messaging_ctx;
34 struct notify_list *list;
35 struct notify_array *array;
36 int seqnum;
37 struct sys_notify_context *sys_notify_ctx;
38 TDB_DATA key;
42 struct notify_list {
43 struct notify_list *next, *prev;
44 void *private_data;
45 void (*callback)(void *, const struct notify_event *);
46 void *sys_notify_handle;
47 int depth;
50 #define NOTIFY_KEY "notify array"
52 #define NOTIFY_ENABLE "notify:enable"
53 #define NOTIFY_ENABLE_DEFAULT True
55 static NTSTATUS notify_remove_all(struct notify_context *notify,
56 const struct server_id *server);
57 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
58 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
61 destroy the notify context
63 static int notify_destructor(struct notify_context *notify)
65 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
67 if (notify->list != NULL) {
68 notify_remove_all(notify, &notify->server);
71 return 0;
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
77 via internal messages
79 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
80 struct messaging_context *messaging_ctx,
81 struct event_context *ev,
82 connection_struct *conn)
84 struct notify_context *notify;
86 if (!lp_change_notify(conn->params)) {
87 return NULL;
90 notify = talloc(mem_ctx, struct notify_context);
91 if (notify == NULL) {
92 return NULL;
95 notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
96 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
97 O_RDWR|O_CREAT, 0644);
98 if (notify->db_recursive == NULL) {
99 talloc_free(notify);
100 return NULL;
103 notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
104 0, TDB_CLEAR_IF_FIRST,
105 O_RDWR|O_CREAT, 0644);
106 if (notify->db_onelevel == NULL) {
107 talloc_free(notify);
108 return NULL;
111 notify->server = server;
112 notify->messaging_ctx = messaging_ctx;
113 notify->list = NULL;
114 notify->array = NULL;
115 notify->seqnum = notify->db_recursive->get_seqnum(
116 notify->db_recursive);
117 notify->key = string_term_tdb_data(NOTIFY_KEY);
119 talloc_set_destructor(notify, notify_destructor);
121 /* register with the messaging subsystem for the notify
122 message type */
123 messaging_register(notify->messaging_ctx, notify,
124 MSG_PVFS_NOTIFY, notify_handler);
126 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
128 return notify;
131 bool notify_internal_parent_init(void)
133 struct tdb_wrap *db1, *db2;
135 if (lp_clustering()) {
136 return true;
140 * Open the tdbs in the parent process (smbd) so that our
141 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
142 * work.
145 db1 = tdb_wrap_open(talloc_autofree_context(), lock_path("notify.tdb"),
146 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
147 O_RDWR|O_CREAT, 0644);
148 if (db1 == NULL) {
149 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
150 return false;
152 db2 = tdb_wrap_open(talloc_autofree_context(),
153 lock_path("notify_onelevel.tdb"),
154 0, TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0644);
155 if (db2 == NULL) {
156 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
157 strerror(errno)));
158 TALLOC_FREE(db1);
159 return false;
161 return true;
165 lock and fetch the record
167 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
169 *rec = notify->db_recursive->fetch_locked(notify->db_recursive,
170 notify, notify->key);
171 if (*rec == NULL) {
172 return NT_STATUS_INTERNAL_DB_CORRUPTION;
174 return NT_STATUS_OK;
178 load the notify array
180 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
182 TDB_DATA dbuf;
183 DATA_BLOB blob;
184 NTSTATUS status;
185 int seqnum;
187 seqnum = notify->db_recursive->get_seqnum(notify->db_recursive);
189 if (seqnum == notify->seqnum && notify->array != NULL) {
190 return NT_STATUS_OK;
193 notify->seqnum = seqnum;
195 talloc_free(notify->array);
196 notify->array = TALLOC_ZERO_P(notify, struct notify_array);
197 NT_STATUS_HAVE_NO_MEMORY(notify->array);
199 if (!rec) {
200 if (notify->db_recursive->fetch(notify->db_recursive, notify,
201 notify->key, &dbuf) != 0) {
202 return NT_STATUS_INTERNAL_DB_CORRUPTION;
204 } else {
205 dbuf = rec->value;
208 blob.data = (uint8 *)dbuf.dptr;
209 blob.length = dbuf.dsize;
211 status = NT_STATUS_OK;
212 if (blob.length > 0) {
213 enum ndr_err_code ndr_err;
214 ndr_err = ndr_pull_struct_blob(&blob, notify->array, NULL, notify->array,
215 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
216 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
217 status = ndr_map_error2ntstatus(ndr_err);
221 if (DEBUGLEVEL >= 10) {
222 DEBUG(10, ("notify_load:\n"));
223 NDR_PRINT_DEBUG(notify_array, notify->array);
226 if (!rec) {
227 talloc_free(dbuf.dptr);
230 return status;
234 compare notify entries for sorting
236 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
238 return strcmp(e1->path, e2->path);
242 save the notify array
244 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
246 TDB_DATA dbuf;
247 DATA_BLOB blob;
248 NTSTATUS status;
249 enum ndr_err_code ndr_err;
250 TALLOC_CTX *tmp_ctx;
252 /* if possible, remove some depth arrays */
253 while (notify->array->num_depths > 0 &&
254 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
255 notify->array->num_depths--;
258 /* we might just be able to delete the record */
259 if (notify->array->num_depths == 0) {
260 return rec->delete_rec(rec);
263 tmp_ctx = talloc_new(notify);
264 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
266 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, NULL, notify->array,
267 (ndr_push_flags_fn_t)ndr_push_notify_array);
268 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
269 talloc_free(tmp_ctx);
270 return ndr_map_error2ntstatus(ndr_err);
273 if (DEBUGLEVEL >= 10) {
274 DEBUG(10, ("notify_save:\n"));
275 NDR_PRINT_DEBUG(notify_array, notify->array);
278 dbuf.dptr = blob.data;
279 dbuf.dsize = blob.length;
281 status = rec->store(rec, dbuf, TDB_REPLACE);
282 talloc_free(tmp_ctx);
284 return status;
289 handle incoming notify messages
291 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
292 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
294 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
295 enum ndr_err_code ndr_err;
296 struct notify_event ev;
297 TALLOC_CTX *tmp_ctx = talloc_new(notify);
298 struct notify_list *listel;
300 if (tmp_ctx == NULL) {
301 return;
304 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, NULL, &ev,
305 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
306 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
307 talloc_free(tmp_ctx);
308 return;
311 for (listel=notify->list;listel;listel=listel->next) {
312 if (listel->private_data == ev.private_data) {
313 listel->callback(listel->private_data, &ev);
314 break;
318 talloc_free(tmp_ctx);
322 callback from sys_notify telling us about changes from the OS
324 static void sys_notify_callback(struct sys_notify_context *ctx,
325 void *ptr, struct notify_event *ev)
327 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
328 ev->private_data = listel;
329 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
330 ev->action, ev->path));
331 listel->callback(listel->private_data, ev);
335 add an entry to the notify array
337 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
338 struct notify_entry *e,
339 void *private_data, int depth)
341 int i;
342 struct notify_depth *d;
343 struct notify_entry *ee;
345 /* possibly expand the depths array */
346 if (depth >= notify->array->num_depths) {
347 d = talloc_realloc(notify->array, notify->array->depth,
348 struct notify_depth, depth+1);
349 NT_STATUS_HAVE_NO_MEMORY(d);
350 for (i=notify->array->num_depths;i<=depth;i++) {
351 ZERO_STRUCT(d[i]);
353 notify->array->depth = d;
354 notify->array->num_depths = depth+1;
356 d = &notify->array->depth[depth];
358 /* expand the entries array */
359 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
360 d->num_entries+1);
361 NT_STATUS_HAVE_NO_MEMORY(ee);
362 d->entries = ee;
364 d->entries[d->num_entries] = *e;
365 d->entries[d->num_entries].private_data = private_data;
366 d->entries[d->num_entries].server = notify->server;
367 d->entries[d->num_entries].path_len = strlen(e->path);
368 d->num_entries++;
370 d->max_mask |= e->filter;
371 d->max_mask_subdir |= e->subdir_filter;
373 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
375 /* recalculate the maximum masks */
376 d->max_mask = 0;
377 d->max_mask_subdir = 0;
379 for (i=0;i<d->num_entries;i++) {
380 d->max_mask |= d->entries[i].filter;
381 d->max_mask_subdir |= d->entries[i].subdir_filter;
384 return notify_save(notify, rec);
388 Add a non-recursive watch
391 static void notify_add_onelevel(struct notify_context *notify,
392 struct notify_entry *e, void *private_data)
394 struct notify_entry_array *array;
395 struct db_record *rec;
396 DATA_BLOB blob;
397 TDB_DATA dbuf;
398 enum ndr_err_code ndr_err;
399 NTSTATUS status;
401 array = talloc_zero(talloc_tos(), struct notify_entry_array);
402 if (array == NULL) {
403 return;
406 rec = notify->db_onelevel->fetch_locked(
407 notify->db_onelevel, talloc_tos(),
408 make_tdb_data((uint8_t *)&e->dir_id, sizeof(e->dir_id)));
409 if (rec == NULL) {
410 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
411 "\n", file_id_string_tos(&e->dir_id)));
412 TALLOC_FREE(array);
413 return;
416 blob.data = (uint8_t *)rec->value.dptr;
417 blob.length = rec->value.dsize;
419 if (blob.length > 0) {
420 ndr_err = ndr_pull_struct_blob(
421 &blob, array, NULL, array,
422 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
423 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
424 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
425 ndr_errstr(ndr_err)));
426 TALLOC_FREE(array);
427 return;
429 if (DEBUGLEVEL >= 10) {
430 DEBUG(10, ("notify_add_onelevel:\n"));
431 NDR_PRINT_DEBUG(notify_entry_array, array);
435 array->entries = talloc_realloc(array, array->entries,
436 struct notify_entry,
437 array->num_entries+1);
438 if (array->entries == NULL) {
439 TALLOC_FREE(array);
440 return;
442 array->entries[array->num_entries] = *e;
443 array->entries[array->num_entries].private_data = private_data;
444 array->entries[array->num_entries].server = notify->server;
445 array->num_entries += 1;
447 ndr_err = ndr_push_struct_blob(
448 &blob, rec, NULL, array,
449 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
450 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
451 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
452 ndr_errstr(ndr_err)));
453 TALLOC_FREE(array);
454 return;
457 if (DEBUGLEVEL >= 10) {
458 DEBUG(10, ("notify_add_onelevel:\n"));
459 NDR_PRINT_DEBUG(notify_entry_array, array);
462 dbuf.dptr = blob.data;
463 dbuf.dsize = blob.length;
465 status = rec->store(rec, dbuf, TDB_REPLACE);
466 TALLOC_FREE(array);
467 if (!NT_STATUS_IS_OK(status)) {
468 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
469 nt_errstr(status)));
470 return;
472 e->filter = 0;
473 return;
478 add a notify watch. This is called when a notify is first setup on a open
479 directory handle.
481 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
482 void (*callback)(void *, const struct notify_event *),
483 void *private_data)
485 struct notify_entry e = *e0;
486 NTSTATUS status;
487 char *tmp_path = NULL;
488 struct notify_list *listel;
489 size_t len;
490 int depth;
491 struct db_record *rec;
493 /* see if change notify is enabled at all */
494 if (notify == NULL) {
495 return NT_STATUS_NOT_IMPLEMENTED;
498 status = notify_fetch_locked(notify, &rec);
499 NT_STATUS_NOT_OK_RETURN(status);
501 status = notify_load(notify, rec);
502 if (!NT_STATUS_IS_OK(status)) {
503 talloc_free(rec);
504 return status;
507 /* cope with /. on the end of the path */
508 len = strlen(e.path);
509 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
510 tmp_path = talloc_strndup(notify, e.path, len-2);
511 if (tmp_path == NULL) {
512 status = NT_STATUS_NO_MEMORY;
513 goto done;
515 e.path = tmp_path;
518 depth = count_chars(e.path, '/');
520 listel = TALLOC_ZERO_P(notify, struct notify_list);
521 if (listel == NULL) {
522 status = NT_STATUS_NO_MEMORY;
523 goto done;
526 listel->private_data = private_data;
527 listel->callback = callback;
528 listel->depth = depth;
529 DLIST_ADD(notify->list, listel);
531 /* ignore failures from sys_notify */
532 if (notify->sys_notify_ctx != NULL) {
534 this call will modify e.filter and e.subdir_filter
535 to remove bits handled by the backend
537 status = sys_notify_watch(notify->sys_notify_ctx, &e,
538 sys_notify_callback, listel,
539 &listel->sys_notify_handle);
540 if (NT_STATUS_IS_OK(status)) {
541 talloc_steal(listel, listel->sys_notify_handle);
545 if (e.filter != 0) {
546 notify_add_onelevel(notify, &e, private_data);
547 status = NT_STATUS_OK;
550 /* if the system notify handler couldn't handle some of the
551 filter bits, or couldn't handle a request for recursion
552 then we need to install it in the array used for the
553 intra-samba notify handling */
554 if (e.filter != 0 || e.subdir_filter != 0) {
555 status = notify_add_array(notify, rec, &e, private_data, depth);
558 done:
559 talloc_free(rec);
560 talloc_free(tmp_path);
562 return status;
565 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
566 const struct file_id *fid,
567 void *private_data)
569 struct notify_entry_array *array;
570 struct db_record *rec;
571 DATA_BLOB blob;
572 TDB_DATA dbuf;
573 enum ndr_err_code ndr_err;
574 NTSTATUS status;
575 int i;
577 if (notify == NULL) {
578 return NT_STATUS_NOT_IMPLEMENTED;
581 array = talloc_zero(talloc_tos(), struct notify_entry_array);
582 if (array == NULL) {
583 return NT_STATUS_NO_MEMORY;
586 rec = notify->db_onelevel->fetch_locked(
587 notify->db_onelevel, array,
588 make_tdb_data((uint8_t *)fid, sizeof(*fid)));
589 if (rec == NULL) {
590 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
591 "\n", file_id_string_tos(fid)));
592 TALLOC_FREE(array);
593 return NT_STATUS_INTERNAL_DB_CORRUPTION;
596 blob.data = (uint8_t *)rec->value.dptr;
597 blob.length = rec->value.dsize;
599 if (blob.length > 0) {
600 ndr_err = ndr_pull_struct_blob(
601 &blob, array, NULL, array,
602 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
603 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
604 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
605 ndr_errstr(ndr_err)));
606 TALLOC_FREE(array);
607 return ndr_map_error2ntstatus(ndr_err);
609 if (DEBUGLEVEL >= 10) {
610 DEBUG(10, ("notify_remove_onelevel:\n"));
611 NDR_PRINT_DEBUG(notify_entry_array, array);
615 for (i=0; i<array->num_entries; i++) {
616 if ((private_data == array->entries[i].private_data) &&
617 cluster_id_equal(&notify->server,
618 &array->entries[i].server)) {
619 break;
623 if (i == array->num_entries) {
624 TALLOC_FREE(array);
625 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
628 array->entries[i] = array->entries[array->num_entries-1];
629 array->num_entries -= 1;
631 if (array->num_entries == 0) {
632 rec->delete_rec(rec);
633 TALLOC_FREE(array);
634 return NT_STATUS_OK;
637 ndr_err = ndr_push_struct_blob(
638 &blob, rec, NULL, array,
639 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
640 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
641 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
642 ndr_errstr(ndr_err)));
643 TALLOC_FREE(array);
644 return ndr_map_error2ntstatus(ndr_err);
647 if (DEBUGLEVEL >= 10) {
648 DEBUG(10, ("notify_add_onelevel:\n"));
649 NDR_PRINT_DEBUG(notify_entry_array, array);
652 dbuf.dptr = blob.data;
653 dbuf.dsize = blob.length;
655 status = rec->store(rec, dbuf, TDB_REPLACE);
656 TALLOC_FREE(array);
657 if (!NT_STATUS_IS_OK(status)) {
658 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
659 nt_errstr(status)));
660 return status;
662 return NT_STATUS_OK;
666 remove a notify watch. Called when the directory handle is closed
668 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
670 NTSTATUS status;
671 struct notify_list *listel;
672 int i, depth;
673 struct notify_depth *d;
674 struct db_record *rec;
676 /* see if change notify is enabled at all */
677 if (notify == NULL) {
678 return NT_STATUS_NOT_IMPLEMENTED;
681 for (listel=notify->list;listel;listel=listel->next) {
682 if (listel->private_data == private_data) {
683 DLIST_REMOVE(notify->list, listel);
684 break;
687 if (listel == NULL) {
688 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
691 depth = listel->depth;
693 talloc_free(listel);
695 status = notify_fetch_locked(notify, &rec);
696 NT_STATUS_NOT_OK_RETURN(status);
698 status = notify_load(notify, rec);
699 if (!NT_STATUS_IS_OK(status)) {
700 talloc_free(rec);
701 return status;
704 if (depth >= notify->array->num_depths) {
705 talloc_free(rec);
706 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
709 /* we only have to search at the depth of this element */
710 d = &notify->array->depth[depth];
712 for (i=0;i<d->num_entries;i++) {
713 if (private_data == d->entries[i].private_data &&
714 cluster_id_equal(&notify->server, &d->entries[i].server)) {
715 break;
718 if (i == d->num_entries) {
719 talloc_free(rec);
720 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
723 if (i < d->num_entries-1) {
724 memmove(&d->entries[i], &d->entries[i+1],
725 sizeof(d->entries[i])*(d->num_entries-(i+1)));
727 d->num_entries--;
729 status = notify_save(notify, rec);
731 talloc_free(rec);
733 return status;
737 remove all notify watches for a messaging server
739 static NTSTATUS notify_remove_all(struct notify_context *notify,
740 const struct server_id *server)
742 NTSTATUS status;
743 int i, depth, del_count=0;
744 struct db_record *rec;
746 status = notify_fetch_locked(notify, &rec);
747 NT_STATUS_NOT_OK_RETURN(status);
749 status = notify_load(notify, rec);
750 if (!NT_STATUS_IS_OK(status)) {
751 talloc_free(rec);
752 return status;
755 /* we have to search for all entries across all depths, looking for matches
756 for the server id */
757 for (depth=0;depth<notify->array->num_depths;depth++) {
758 struct notify_depth *d = &notify->array->depth[depth];
759 for (i=0;i<d->num_entries;i++) {
760 if (cluster_id_equal(server, &d->entries[i].server)) {
761 if (i < d->num_entries-1) {
762 memmove(&d->entries[i], &d->entries[i+1],
763 sizeof(d->entries[i])*(d->num_entries-(i+1)));
765 i--;
766 d->num_entries--;
767 del_count++;
772 if (del_count > 0) {
773 status = notify_save(notify, rec);
776 talloc_free(rec);
778 return status;
783 send a notify message to another messaging server
785 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
786 const char *path, uint32_t action)
788 struct notify_event ev;
789 DATA_BLOB data;
790 NTSTATUS status;
791 enum ndr_err_code ndr_err;
792 TALLOC_CTX *tmp_ctx;
794 ev.action = action;
795 ev.path = path;
796 ev.private_data = e->private_data;
798 tmp_ctx = talloc_new(notify);
800 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, NULL, &ev,
801 (ndr_push_flags_fn_t)ndr_push_notify_event);
802 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
803 talloc_free(tmp_ctx);
804 return ndr_map_error2ntstatus(ndr_err);
807 status = messaging_send(notify->messaging_ctx, e->server,
808 MSG_PVFS_NOTIFY, &data);
809 talloc_free(tmp_ctx);
810 return status;
813 void notify_onelevel(struct notify_context *notify, uint32_t action,
814 uint32_t filter, struct file_id fid, const char *name)
816 struct notify_entry_array *array;
817 TDB_DATA dbuf;
818 DATA_BLOB blob;
819 bool have_dead_entries = false;
820 int i;
822 if (notify == NULL) {
823 return;
826 array = talloc_zero(talloc_tos(), struct notify_entry_array);
827 if (array == NULL) {
828 return;
831 if (notify->db_onelevel->fetch(
832 notify->db_onelevel, array,
833 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
834 &dbuf) == -1) {
835 TALLOC_FREE(array);
836 return;
839 blob.data = (uint8 *)dbuf.dptr;
840 blob.length = dbuf.dsize;
842 if (blob.length > 0) {
843 enum ndr_err_code ndr_err;
844 ndr_err = ndr_pull_struct_blob(
845 &blob, array, NULL, array,
846 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
847 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
848 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
849 ndr_errstr(ndr_err)));
850 TALLOC_FREE(array);
851 return;
853 if (DEBUGLEVEL >= 10) {
854 DEBUG(10, ("notify_onelevel:\n"));
855 NDR_PRINT_DEBUG(notify_entry_array, array);
859 for (i=0; i<array->num_entries; i++) {
860 struct notify_entry *e = &array->entries[i];
862 if ((e->filter & filter) != 0) {
863 NTSTATUS status;
865 status = notify_send(notify, e, name, action);
866 if (NT_STATUS_EQUAL(
867 status, NT_STATUS_INVALID_HANDLE)) {
869 * Mark the entry as dead. All entries have a
870 * path set. The marker used here is setting
871 * that to NULL.
873 e->path = NULL;
874 have_dead_entries = true;
879 if (!have_dead_entries) {
880 TALLOC_FREE(array);
881 return;
884 for (i=0; i<array->num_entries; i++) {
885 struct notify_entry *e = &array->entries[i];
886 if (e->path != NULL) {
887 continue;
889 DEBUG(10, ("Deleting notify entries for process %s because "
890 "it's gone\n", procid_str_static(&e->server)));
892 * Potential TODO: This might need optimizing,
893 * notify_remove_onelevel() does a fetch_locked() operation at
894 * every call. But this would only matter if a process with
895 * MANY notifies has died without shutting down properly.
897 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
900 TALLOC_FREE(array);
901 return;
905 trigger a notify message for anyone waiting on a matching event
907 This function is called a lot, and needs to be very fast. The unusual data structure
908 and traversal is designed to be fast in the average case, even for large numbers of
909 notifies
911 void notify_trigger(struct notify_context *notify,
912 uint32_t action, uint32_t filter, const char *path)
914 NTSTATUS status;
915 int depth;
916 const char *p, *next_p;
918 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
919 "path=%s\n", (unsigned)action, (unsigned)filter, path));
921 /* see if change notify is enabled at all */
922 if (notify == NULL) {
923 return;
926 again:
927 status = notify_load(notify, NULL);
928 if (!NT_STATUS_IS_OK(status)) {
929 return;
932 /* loop along the given path, working with each directory depth separately */
933 for (depth=0,p=path;
934 p && depth < notify->array->num_depths;
935 p=next_p,depth++) {
936 int p_len = p - path;
937 int min_i, max_i, i;
938 struct notify_depth *d = &notify->array->depth[depth];
939 next_p = strchr(p+1, '/');
941 /* see if there are any entries at this depth */
942 if (d->num_entries == 0) continue;
944 /* try to skip based on the maximum mask. If next_p is
945 NULL then we know it will be a 'this directory'
946 match, otherwise it must be a subdir match */
947 if (next_p != NULL) {
948 if (0 == (filter & d->max_mask_subdir)) {
949 continue;
951 } else {
952 if (0 == (filter & d->max_mask)) {
953 continue;
957 /* we know there is an entry here worth looking
958 for. Use a bisection search to find the first entry
959 with a matching path */
960 min_i = 0;
961 max_i = d->num_entries-1;
963 while (min_i < max_i) {
964 struct notify_entry *e;
965 int cmp;
966 i = (min_i+max_i)/2;
967 e = &d->entries[i];
968 cmp = strncmp(path, e->path, p_len);
969 if (cmp == 0) {
970 if (p_len == e->path_len) {
971 max_i = i;
972 } else {
973 max_i = i-1;
975 } else if (cmp < 0) {
976 max_i = i-1;
977 } else {
978 min_i = i+1;
982 if (min_i != max_i) {
983 /* none match */
984 continue;
987 /* we now know that the entries start at min_i */
988 for (i=min_i;i<d->num_entries;i++) {
989 struct notify_entry *e = &d->entries[i];
990 if (p_len != e->path_len ||
991 strncmp(path, e->path, p_len) != 0) break;
992 if (next_p != NULL) {
993 if (0 == (filter & e->subdir_filter)) {
994 continue;
996 } else {
997 if (0 == (filter & e->filter)) {
998 continue;
1001 status = notify_send(notify, e, path + e->path_len + 1,
1002 action);
1004 if (NT_STATUS_EQUAL(
1005 status, NT_STATUS_INVALID_HANDLE)) {
1006 struct server_id server = e->server;
1008 DEBUG(10, ("Deleting notify entries for "
1009 "process %s because it's gone\n",
1010 procid_str_static(&e->server)));
1011 notify_remove_all(notify, &server);
1012 goto again;