lib/util Move source3 tdb_wrap_open() into the common code.
[Samba.git] / source3 / smbd / notify_internal.c
blob176fa863a50c3b13a80ac5f6781645b5f5361bdc
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 "librpc/gen_ndr/ndr_notify.h"
29 #include "dbwrap.h"
30 #include "smbd/smbd.h"
31 #include "messages.h"
32 #include "lib/util/tdb_wrap.h"
34 struct notify_context {
35 struct db_context *db_recursive;
36 struct db_context *db_onelevel;
37 struct server_id server;
38 struct messaging_context *messaging_ctx;
39 struct notify_list *list;
40 struct notify_array *array;
41 int seqnum;
42 struct sys_notify_context *sys_notify_ctx;
43 TDB_DATA key;
47 struct notify_list {
48 struct notify_list *next, *prev;
49 void *private_data;
50 void (*callback)(void *, const struct notify_event *);
51 void *sys_notify_handle;
52 int depth;
55 #define NOTIFY_KEY "notify array"
57 #define NOTIFY_ENABLE "notify:enable"
58 #define NOTIFY_ENABLE_DEFAULT True
60 static NTSTATUS notify_remove_all(struct notify_context *notify,
61 const struct server_id *server);
62 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
63 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
66 destroy the notify context
68 static int notify_destructor(struct notify_context *notify)
70 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
72 if (notify->list != NULL) {
73 notify_remove_all(notify, &notify->server);
76 return 0;
80 Open up the notify.tdb database. You should close it down using
81 talloc_free(). We need the messaging_ctx to allow for notifications
82 via internal messages
84 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
85 struct messaging_context *messaging_ctx,
86 struct event_context *ev,
87 connection_struct *conn)
89 struct notify_context *notify;
91 if (!lp_change_notify(conn->params)) {
92 return NULL;
95 notify = talloc(mem_ctx, struct notify_context);
96 if (notify == NULL) {
97 return NULL;
100 notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
101 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
102 O_RDWR|O_CREAT, 0644);
103 if (notify->db_recursive == NULL) {
104 talloc_free(notify);
105 return NULL;
108 notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
109 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
110 O_RDWR|O_CREAT, 0644);
111 if (notify->db_onelevel == NULL) {
112 talloc_free(notify);
113 return NULL;
116 notify->server = server;
117 notify->messaging_ctx = messaging_ctx;
118 notify->list = NULL;
119 notify->array = NULL;
120 notify->seqnum = notify->db_recursive->get_seqnum(
121 notify->db_recursive);
122 notify->key = string_term_tdb_data(NOTIFY_KEY);
124 talloc_set_destructor(notify, notify_destructor);
126 /* register with the messaging subsystem for the notify
127 message type */
128 messaging_register(notify->messaging_ctx, notify,
129 MSG_PVFS_NOTIFY, notify_handler);
131 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
133 return notify;
136 bool notify_internal_parent_init(TALLOC_CTX *mem_ctx)
138 struct tdb_wrap *db1, *db2;
140 if (lp_clustering()) {
141 return true;
145 * Open the tdbs in the parent process (smbd) so that our
146 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
147 * work.
150 db1 = tdb_wrap_open(mem_ctx, lock_path("notify.tdb"),
151 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
152 O_RDWR|O_CREAT, 0644);
153 if (db1 == NULL) {
154 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
155 return false;
157 db2 = tdb_wrap_open(mem_ctx, lock_path("notify_onelevel.tdb"),
158 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644);
159 if (db2 == NULL) {
160 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
161 strerror(errno)));
162 TALLOC_FREE(db1);
163 return false;
165 return true;
169 lock and fetch the record
171 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
173 *rec = notify->db_recursive->fetch_locked(notify->db_recursive,
174 notify, notify->key);
175 if (*rec == NULL) {
176 return NT_STATUS_INTERNAL_DB_CORRUPTION;
178 return NT_STATUS_OK;
182 load the notify array
184 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
186 TDB_DATA dbuf;
187 DATA_BLOB blob;
188 NTSTATUS status;
189 int seqnum;
191 seqnum = notify->db_recursive->get_seqnum(notify->db_recursive);
193 if (seqnum == notify->seqnum && notify->array != NULL) {
194 return NT_STATUS_OK;
197 notify->seqnum = seqnum;
199 talloc_free(notify->array);
200 notify->array = TALLOC_ZERO_P(notify, struct notify_array);
201 NT_STATUS_HAVE_NO_MEMORY(notify->array);
203 if (!rec) {
204 if (notify->db_recursive->fetch(notify->db_recursive, notify,
205 notify->key, &dbuf) != 0) {
206 return NT_STATUS_INTERNAL_DB_CORRUPTION;
208 } else {
209 dbuf = rec->value;
212 blob.data = (uint8 *)dbuf.dptr;
213 blob.length = dbuf.dsize;
215 status = NT_STATUS_OK;
216 if (blob.length > 0) {
217 enum ndr_err_code ndr_err;
218 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
219 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
220 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
221 /* 1. log that we got a corrupt notify_array
222 * 2. clear the variable the garbage was stored into to not trip
223 * over it next time this method is entered with the same seqnum
224 * 3. delete it from the database */
225 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
227 ZERO_STRUCTP(notify->array);
228 if (rec != NULL) {
229 rec->delete_rec(rec);
232 } else {
233 if (DEBUGLEVEL >= 10) {
234 DEBUG(10, ("notify_load:\n"));
235 NDR_PRINT_DEBUG(notify_array, notify->array);
241 if (!rec) {
242 talloc_free(dbuf.dptr);
245 return status;
249 compare notify entries for sorting
251 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
253 return strcmp(e1->path, e2->path);
257 save the notify array
259 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
261 TDB_DATA dbuf;
262 DATA_BLOB blob;
263 NTSTATUS status;
264 enum ndr_err_code ndr_err;
265 TALLOC_CTX *tmp_ctx;
267 /* if possible, remove some depth arrays */
268 while (notify->array->num_depths > 0 &&
269 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
270 notify->array->num_depths--;
273 /* we might just be able to delete the record */
274 if (notify->array->num_depths == 0) {
275 return rec->delete_rec(rec);
278 tmp_ctx = talloc_new(notify);
279 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
281 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
282 (ndr_push_flags_fn_t)ndr_push_notify_array);
283 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
284 talloc_free(tmp_ctx);
285 return ndr_map_error2ntstatus(ndr_err);
288 if (DEBUGLEVEL >= 10) {
289 DEBUG(10, ("notify_save:\n"));
290 NDR_PRINT_DEBUG(notify_array, notify->array);
293 dbuf.dptr = blob.data;
294 dbuf.dsize = blob.length;
296 status = rec->store(rec, dbuf, TDB_REPLACE);
297 talloc_free(tmp_ctx);
299 return status;
304 handle incoming notify messages
306 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
307 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
309 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
310 enum ndr_err_code ndr_err;
311 struct notify_event ev;
312 TALLOC_CTX *tmp_ctx = talloc_new(notify);
313 struct notify_list *listel;
315 if (tmp_ctx == NULL) {
316 return;
319 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
320 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
321 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
322 talloc_free(tmp_ctx);
323 return;
326 for (listel=notify->list;listel;listel=listel->next) {
327 if (listel->private_data == ev.private_data) {
328 listel->callback(listel->private_data, &ev);
329 break;
333 talloc_free(tmp_ctx);
337 callback from sys_notify telling us about changes from the OS
339 static void sys_notify_callback(struct sys_notify_context *ctx,
340 void *ptr, struct notify_event *ev)
342 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
343 ev->private_data = listel;
344 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
345 ev->action, ev->path));
346 listel->callback(listel->private_data, ev);
350 add an entry to the notify array
352 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
353 struct notify_entry *e,
354 void *private_data, int depth)
356 int i;
357 struct notify_depth *d;
358 struct notify_entry *ee;
360 /* possibly expand the depths array */
361 if (depth >= notify->array->num_depths) {
362 d = talloc_realloc(notify->array, notify->array->depth,
363 struct notify_depth, depth+1);
364 NT_STATUS_HAVE_NO_MEMORY(d);
365 for (i=notify->array->num_depths;i<=depth;i++) {
366 ZERO_STRUCT(d[i]);
368 notify->array->depth = d;
369 notify->array->num_depths = depth+1;
371 d = &notify->array->depth[depth];
373 /* expand the entries array */
374 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
375 d->num_entries+1);
376 NT_STATUS_HAVE_NO_MEMORY(ee);
377 d->entries = ee;
379 d->entries[d->num_entries] = *e;
380 d->entries[d->num_entries].private_data = private_data;
381 d->entries[d->num_entries].server = notify->server;
382 d->entries[d->num_entries].path_len = strlen(e->path);
383 d->num_entries++;
385 d->max_mask |= e->filter;
386 d->max_mask_subdir |= e->subdir_filter;
388 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
390 /* recalculate the maximum masks */
391 d->max_mask = 0;
392 d->max_mask_subdir = 0;
394 for (i=0;i<d->num_entries;i++) {
395 d->max_mask |= d->entries[i].filter;
396 d->max_mask_subdir |= d->entries[i].subdir_filter;
399 return notify_save(notify, rec);
403 Add a non-recursive watch
406 static void notify_add_onelevel(struct notify_context *notify,
407 struct notify_entry *e, void *private_data)
409 struct notify_entry_array *array;
410 struct db_record *rec;
411 DATA_BLOB blob;
412 TDB_DATA dbuf;
413 enum ndr_err_code ndr_err;
414 NTSTATUS status;
416 array = talloc_zero(talloc_tos(), struct notify_entry_array);
417 if (array == NULL) {
418 return;
421 rec = notify->db_onelevel->fetch_locked(
422 notify->db_onelevel, talloc_tos(),
423 make_tdb_data((uint8_t *)&e->dir_id, sizeof(e->dir_id)));
424 if (rec == NULL) {
425 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
426 "\n", file_id_string_tos(&e->dir_id)));
427 TALLOC_FREE(array);
428 return;
431 blob.data = (uint8_t *)rec->value.dptr;
432 blob.length = rec->value.dsize;
434 if (blob.length > 0) {
435 ndr_err = ndr_pull_struct_blob(&blob, array, array,
436 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
437 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
438 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
439 ndr_errstr(ndr_err)));
440 TALLOC_FREE(array);
441 return;
443 if (DEBUGLEVEL >= 10) {
444 DEBUG(10, ("notify_add_onelevel:\n"));
445 NDR_PRINT_DEBUG(notify_entry_array, array);
449 array->entries = talloc_realloc(array, array->entries,
450 struct notify_entry,
451 array->num_entries+1);
452 if (array->entries == NULL) {
453 TALLOC_FREE(array);
454 return;
456 array->entries[array->num_entries] = *e;
457 array->entries[array->num_entries].private_data = private_data;
458 array->entries[array->num_entries].server = notify->server;
459 array->num_entries += 1;
461 ndr_err = ndr_push_struct_blob(&blob, rec, array,
462 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
463 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
464 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
465 ndr_errstr(ndr_err)));
466 TALLOC_FREE(array);
467 return;
470 if (DEBUGLEVEL >= 10) {
471 DEBUG(10, ("notify_add_onelevel:\n"));
472 NDR_PRINT_DEBUG(notify_entry_array, array);
475 dbuf.dptr = blob.data;
476 dbuf.dsize = blob.length;
478 status = rec->store(rec, dbuf, TDB_REPLACE);
479 TALLOC_FREE(array);
480 if (!NT_STATUS_IS_OK(status)) {
481 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
482 nt_errstr(status)));
483 return;
485 e->filter = 0;
486 return;
491 add a notify watch. This is called when a notify is first setup on a open
492 directory handle.
494 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
495 void (*callback)(void *, const struct notify_event *),
496 void *private_data)
498 struct notify_entry e = *e0;
499 NTSTATUS status;
500 char *tmp_path = NULL;
501 struct notify_list *listel;
502 size_t len;
503 int depth;
504 struct db_record *rec;
506 /* see if change notify is enabled at all */
507 if (notify == NULL) {
508 return NT_STATUS_NOT_IMPLEMENTED;
511 status = notify_fetch_locked(notify, &rec);
512 NT_STATUS_NOT_OK_RETURN(status);
514 status = notify_load(notify, rec);
515 if (!NT_STATUS_IS_OK(status)) {
516 talloc_free(rec);
517 return status;
520 /* cope with /. on the end of the path */
521 len = strlen(e.path);
522 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
523 tmp_path = talloc_strndup(notify, e.path, len-2);
524 if (tmp_path == NULL) {
525 status = NT_STATUS_NO_MEMORY;
526 goto done;
528 e.path = tmp_path;
531 depth = count_chars(e.path, '/');
533 listel = TALLOC_ZERO_P(notify, struct notify_list);
534 if (listel == NULL) {
535 status = NT_STATUS_NO_MEMORY;
536 goto done;
539 listel->private_data = private_data;
540 listel->callback = callback;
541 listel->depth = depth;
542 DLIST_ADD(notify->list, listel);
544 /* ignore failures from sys_notify */
545 if (notify->sys_notify_ctx != NULL) {
547 this call will modify e.filter and e.subdir_filter
548 to remove bits handled by the backend
550 status = sys_notify_watch(notify->sys_notify_ctx, &e,
551 sys_notify_callback, listel,
552 &listel->sys_notify_handle);
553 if (NT_STATUS_IS_OK(status)) {
554 talloc_steal(listel, listel->sys_notify_handle);
558 if (e.filter != 0) {
559 notify_add_onelevel(notify, &e, private_data);
560 status = NT_STATUS_OK;
563 /* if the system notify handler couldn't handle some of the
564 filter bits, or couldn't handle a request for recursion
565 then we need to install it in the array used for the
566 intra-samba notify handling */
567 if (e.filter != 0 || e.subdir_filter != 0) {
568 status = notify_add_array(notify, rec, &e, private_data, depth);
571 done:
572 talloc_free(rec);
573 talloc_free(tmp_path);
575 return status;
578 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
579 const struct file_id *fid,
580 void *private_data)
582 struct notify_entry_array *array;
583 struct db_record *rec;
584 DATA_BLOB blob;
585 TDB_DATA dbuf;
586 enum ndr_err_code ndr_err;
587 NTSTATUS status;
588 int i;
590 if (notify == NULL) {
591 return NT_STATUS_NOT_IMPLEMENTED;
594 array = talloc_zero(talloc_tos(), struct notify_entry_array);
595 if (array == NULL) {
596 return NT_STATUS_NO_MEMORY;
599 rec = notify->db_onelevel->fetch_locked(
600 notify->db_onelevel, array,
601 make_tdb_data((const uint8_t *)fid, sizeof(*fid)));
602 if (rec == NULL) {
603 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
604 "\n", file_id_string_tos(fid)));
605 TALLOC_FREE(array);
606 return NT_STATUS_INTERNAL_DB_CORRUPTION;
609 blob.data = (uint8_t *)rec->value.dptr;
610 blob.length = rec->value.dsize;
612 if (blob.length > 0) {
613 ndr_err = ndr_pull_struct_blob(&blob, array, array,
614 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
615 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
616 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
617 ndr_errstr(ndr_err)));
618 TALLOC_FREE(array);
619 return ndr_map_error2ntstatus(ndr_err);
621 if (DEBUGLEVEL >= 10) {
622 DEBUG(10, ("notify_remove_onelevel:\n"));
623 NDR_PRINT_DEBUG(notify_entry_array, array);
627 for (i=0; i<array->num_entries; i++) {
628 if ((private_data == array->entries[i].private_data) &&
629 cluster_id_equal(&notify->server,
630 &array->entries[i].server)) {
631 break;
635 if (i == array->num_entries) {
636 TALLOC_FREE(array);
637 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
640 array->entries[i] = array->entries[array->num_entries-1];
641 array->num_entries -= 1;
643 if (array->num_entries == 0) {
644 rec->delete_rec(rec);
645 TALLOC_FREE(array);
646 return NT_STATUS_OK;
649 ndr_err = ndr_push_struct_blob(&blob, rec, array,
650 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
651 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
652 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
653 ndr_errstr(ndr_err)));
654 TALLOC_FREE(array);
655 return ndr_map_error2ntstatus(ndr_err);
658 if (DEBUGLEVEL >= 10) {
659 DEBUG(10, ("notify_add_onelevel:\n"));
660 NDR_PRINT_DEBUG(notify_entry_array, array);
663 dbuf.dptr = blob.data;
664 dbuf.dsize = blob.length;
666 status = rec->store(rec, dbuf, TDB_REPLACE);
667 TALLOC_FREE(array);
668 if (!NT_STATUS_IS_OK(status)) {
669 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
670 nt_errstr(status)));
671 return status;
673 return NT_STATUS_OK;
677 remove a notify watch. Called when the directory handle is closed
679 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
681 NTSTATUS status;
682 struct notify_list *listel;
683 int i, depth;
684 struct notify_depth *d;
685 struct db_record *rec;
687 /* see if change notify is enabled at all */
688 if (notify == NULL) {
689 return NT_STATUS_NOT_IMPLEMENTED;
692 for (listel=notify->list;listel;listel=listel->next) {
693 if (listel->private_data == private_data) {
694 DLIST_REMOVE(notify->list, listel);
695 break;
698 if (listel == NULL) {
699 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
702 depth = listel->depth;
704 talloc_free(listel);
706 status = notify_fetch_locked(notify, &rec);
707 NT_STATUS_NOT_OK_RETURN(status);
709 status = notify_load(notify, rec);
710 if (!NT_STATUS_IS_OK(status)) {
711 talloc_free(rec);
712 return status;
715 if (depth >= notify->array->num_depths) {
716 talloc_free(rec);
717 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
720 /* we only have to search at the depth of this element */
721 d = &notify->array->depth[depth];
723 for (i=0;i<d->num_entries;i++) {
724 if (private_data == d->entries[i].private_data &&
725 cluster_id_equal(&notify->server, &d->entries[i].server)) {
726 break;
729 if (i == d->num_entries) {
730 talloc_free(rec);
731 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
734 if (i < d->num_entries-1) {
735 memmove(&d->entries[i], &d->entries[i+1],
736 sizeof(d->entries[i])*(d->num_entries-(i+1)));
738 d->num_entries--;
740 status = notify_save(notify, rec);
742 talloc_free(rec);
744 return status;
748 remove all notify watches for a messaging server
750 static NTSTATUS notify_remove_all(struct notify_context *notify,
751 const struct server_id *server)
753 NTSTATUS status;
754 int i, depth, del_count=0;
755 struct db_record *rec;
757 status = notify_fetch_locked(notify, &rec);
758 NT_STATUS_NOT_OK_RETURN(status);
760 status = notify_load(notify, rec);
761 if (!NT_STATUS_IS_OK(status)) {
762 talloc_free(rec);
763 return status;
766 /* we have to search for all entries across all depths, looking for matches
767 for the server id */
768 for (depth=0;depth<notify->array->num_depths;depth++) {
769 struct notify_depth *d = &notify->array->depth[depth];
770 for (i=0;i<d->num_entries;i++) {
771 if (cluster_id_equal(server, &d->entries[i].server)) {
772 if (i < d->num_entries-1) {
773 memmove(&d->entries[i], &d->entries[i+1],
774 sizeof(d->entries[i])*(d->num_entries-(i+1)));
776 i--;
777 d->num_entries--;
778 del_count++;
783 if (del_count > 0) {
784 status = notify_save(notify, rec);
787 talloc_free(rec);
789 return status;
794 send a notify message to another messaging server
796 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
797 const char *path, uint32_t action)
799 struct notify_event ev;
800 DATA_BLOB data;
801 NTSTATUS status;
802 enum ndr_err_code ndr_err;
803 TALLOC_CTX *tmp_ctx;
805 ev.action = action;
806 ev.path = path;
807 ev.private_data = e->private_data;
809 tmp_ctx = talloc_new(notify);
811 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
812 (ndr_push_flags_fn_t)ndr_push_notify_event);
813 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
814 talloc_free(tmp_ctx);
815 return ndr_map_error2ntstatus(ndr_err);
818 status = messaging_send(notify->messaging_ctx, e->server,
819 MSG_PVFS_NOTIFY, &data);
820 talloc_free(tmp_ctx);
821 return status;
824 void notify_onelevel(struct notify_context *notify, uint32_t action,
825 uint32_t filter, struct file_id fid, const char *name)
827 struct notify_entry_array *array;
828 TDB_DATA dbuf;
829 DATA_BLOB blob;
830 bool have_dead_entries = false;
831 int i;
833 if (notify == NULL) {
834 return;
837 array = talloc_zero(talloc_tos(), struct notify_entry_array);
838 if (array == NULL) {
839 return;
842 if (notify->db_onelevel->fetch(
843 notify->db_onelevel, array,
844 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
845 &dbuf) == -1) {
846 TALLOC_FREE(array);
847 return;
850 blob.data = (uint8 *)dbuf.dptr;
851 blob.length = dbuf.dsize;
853 if (blob.length > 0) {
854 enum ndr_err_code ndr_err;
855 ndr_err = ndr_pull_struct_blob(&blob, array, array,
856 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
857 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
858 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
859 ndr_errstr(ndr_err)));
860 TALLOC_FREE(array);
861 return;
863 if (DEBUGLEVEL >= 10) {
864 DEBUG(10, ("notify_onelevel:\n"));
865 NDR_PRINT_DEBUG(notify_entry_array, array);
869 for (i=0; i<array->num_entries; i++) {
870 struct notify_entry *e = &array->entries[i];
872 if ((e->filter & filter) != 0) {
873 NTSTATUS status;
875 status = notify_send(notify, e, name, action);
876 if (NT_STATUS_EQUAL(
877 status, NT_STATUS_INVALID_HANDLE)) {
879 * Mark the entry as dead. All entries have a
880 * path set. The marker used here is setting
881 * that to NULL.
883 e->path = NULL;
884 have_dead_entries = true;
889 if (!have_dead_entries) {
890 TALLOC_FREE(array);
891 return;
894 for (i=0; i<array->num_entries; i++) {
895 struct notify_entry *e = &array->entries[i];
896 if (e->path != NULL) {
897 continue;
899 DEBUG(10, ("Deleting notify entries for process %s because "
900 "it's gone\n", procid_str_static(&e->server)));
902 * Potential TODO: This might need optimizing,
903 * notify_remove_onelevel() does a fetch_locked() operation at
904 * every call. But this would only matter if a process with
905 * MANY notifies has died without shutting down properly.
907 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
910 TALLOC_FREE(array);
911 return;
915 trigger a notify message for anyone waiting on a matching event
917 This function is called a lot, and needs to be very fast. The unusual data structure
918 and traversal is designed to be fast in the average case, even for large numbers of
919 notifies
921 void notify_trigger(struct notify_context *notify,
922 uint32_t action, uint32_t filter, const char *path)
924 NTSTATUS status;
925 int depth;
926 const char *p, *next_p;
928 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
929 "path=%s\n", (unsigned)action, (unsigned)filter, path));
931 /* see if change notify is enabled at all */
932 if (notify == NULL) {
933 return;
936 again:
937 status = notify_load(notify, NULL);
938 if (!NT_STATUS_IS_OK(status)) {
939 return;
942 /* loop along the given path, working with each directory depth separately */
943 for (depth=0,p=path;
944 p && depth < notify->array->num_depths;
945 p=next_p,depth++) {
946 int p_len = p - path;
947 int min_i, max_i, i;
948 struct notify_depth *d = &notify->array->depth[depth];
949 next_p = strchr(p+1, '/');
951 /* see if there are any entries at this depth */
952 if (d->num_entries == 0) continue;
954 /* try to skip based on the maximum mask. If next_p is
955 NULL then we know it will be a 'this directory'
956 match, otherwise it must be a subdir match */
957 if (next_p != NULL) {
958 if (0 == (filter & d->max_mask_subdir)) {
959 continue;
961 } else {
962 if (0 == (filter & d->max_mask)) {
963 continue;
967 /* we know there is an entry here worth looking
968 for. Use a bisection search to find the first entry
969 with a matching path */
970 min_i = 0;
971 max_i = d->num_entries-1;
973 while (min_i < max_i) {
974 struct notify_entry *e;
975 int cmp;
976 i = (min_i+max_i)/2;
977 e = &d->entries[i];
978 cmp = strncmp(path, e->path, p_len);
979 if (cmp == 0) {
980 if (p_len == e->path_len) {
981 max_i = i;
982 } else {
983 max_i = i-1;
985 } else if (cmp < 0) {
986 max_i = i-1;
987 } else {
988 min_i = i+1;
992 if (min_i != max_i) {
993 /* none match */
994 continue;
997 /* we now know that the entries start at min_i */
998 for (i=min_i;i<d->num_entries;i++) {
999 struct notify_entry *e = &d->entries[i];
1000 if (p_len != e->path_len ||
1001 strncmp(path, e->path, p_len) != 0) break;
1002 if (next_p != NULL) {
1003 if (0 == (filter & e->subdir_filter)) {
1004 continue;
1006 } else {
1007 if (0 == (filter & e->filter)) {
1008 continue;
1011 status = notify_send(notify, e, path + e->path_len + 1,
1012 action);
1014 if (NT_STATUS_EQUAL(
1015 status, NT_STATUS_INVALID_HANDLE)) {
1016 struct server_id server = e->server;
1018 DEBUG(10, ("Deleting notify entries for "
1019 "process %s because it's gone\n",
1020 procid_str_static(&e->server)));
1021 notify_remove_all(notify, &server);
1022 goto again;