s3:smbd: convert notify db to use dbwrap wrapper functions
[Samba/vl.git] / source3 / smbd / notify_internal.c
blob4f2749e17d0000815224fd6b1d8f9a2ed68cf8ac
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/dbwrap.h"
30 #include "dbwrap/dbwrap_open.h"
31 #include "smbd/smbd.h"
32 #include "messages.h"
33 #include "lib/util/tdb_wrap.h"
34 #include "util_tdb.h"
36 struct notify_context {
37 struct db_context *db_recursive;
38 struct db_context *db_onelevel;
39 struct server_id server;
40 struct messaging_context *messaging_ctx;
41 struct notify_list *list;
42 struct notify_array *array;
43 int seqnum;
44 struct sys_notify_context *sys_notify_ctx;
45 TDB_DATA key;
49 struct notify_list {
50 struct notify_list *next, *prev;
51 void *private_data;
52 void (*callback)(void *, const struct notify_event *);
53 void *sys_notify_handle;
54 int depth;
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 const struct server_id *server);
64 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
65 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
68 destroy the notify context
70 static int notify_destructor(struct notify_context *notify)
72 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
74 if (notify->list != NULL) {
75 notify_remove_all(notify, &notify->server);
78 return 0;
82 Open up the notify.tdb database. You should close it down using
83 talloc_free(). We need the messaging_ctx to allow for notifications
84 via internal messages
86 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
87 struct messaging_context *messaging_ctx,
88 struct event_context *ev,
89 connection_struct *conn)
91 struct notify_context *notify;
93 if (!lp_change_notify(conn->params)) {
94 return NULL;
97 notify = talloc(mem_ctx, struct notify_context);
98 if (notify == NULL) {
99 return NULL;
102 notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
103 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
104 O_RDWR|O_CREAT, 0644);
105 if (notify->db_recursive == NULL) {
106 talloc_free(notify);
107 return NULL;
110 notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
111 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
112 O_RDWR|O_CREAT, 0644);
113 if (notify->db_onelevel == NULL) {
114 talloc_free(notify);
115 return NULL;
118 notify->server = server;
119 notify->messaging_ctx = messaging_ctx;
120 notify->list = NULL;
121 notify->array = NULL;
122 notify->seqnum = dbwrap_get_seqnum(notify->db_recursive);
123 notify->key = string_term_tdb_data(NOTIFY_KEY);
125 talloc_set_destructor(notify, notify_destructor);
127 /* register with the messaging subsystem for the notify
128 message type */
129 messaging_register(notify->messaging_ctx, notify,
130 MSG_PVFS_NOTIFY, notify_handler);
132 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
134 return notify;
137 bool notify_internal_parent_init(TALLOC_CTX *mem_ctx)
139 struct tdb_wrap *db1, *db2;
141 if (lp_clustering()) {
142 return true;
146 * Open the tdbs in the parent process (smbd) so that our
147 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
148 * work.
151 db1 = tdb_wrap_open(mem_ctx, lock_path("notify.tdb"),
152 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
153 O_RDWR|O_CREAT, 0644);
154 if (db1 == NULL) {
155 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
156 return false;
158 db2 = tdb_wrap_open(mem_ctx, lock_path("notify_onelevel.tdb"),
159 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644);
160 if (db2 == NULL) {
161 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
162 strerror(errno)));
163 TALLOC_FREE(db1);
164 return false;
166 return true;
170 lock and fetch the record
172 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
174 *rec = dbwrap_fetch_locked(notify->db_recursive, 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 = dbwrap_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(notify, struct notify_array);
201 NT_STATUS_HAVE_NO_MEMORY(notify->array);
203 if (!rec) {
204 status = dbwrap_fetch(notify->db_recursive, notify,
205 notify->key, &dbuf);
206 if (!NT_STATUS_IS_OK(status)) {
207 return NT_STATUS_INTERNAL_DB_CORRUPTION;
209 } else {
210 dbuf = dbwrap_record_get_value(rec);
213 blob.data = (uint8 *)dbuf.dptr;
214 blob.length = dbuf.dsize;
216 status = NT_STATUS_OK;
217 if (blob.length > 0) {
218 enum ndr_err_code ndr_err;
219 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
220 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
221 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
222 /* 1. log that we got a corrupt notify_array
223 * 2. clear the variable the garbage was stored into to not trip
224 * over it next time this method is entered with the same seqnum
225 * 3. delete it from the database */
226 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
228 ZERO_STRUCTP(notify->array);
229 if (rec != NULL) {
230 dbwrap_record_delete(rec);
233 } else {
234 if (DEBUGLEVEL >= 10) {
235 DEBUG(10, ("notify_load:\n"));
236 NDR_PRINT_DEBUG(notify_array, notify->array);
242 if (!rec) {
243 talloc_free(dbuf.dptr);
246 return status;
250 compare notify entries for sorting
252 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
254 return strcmp(e1->path, e2->path);
258 save the notify array
260 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
262 TDB_DATA dbuf;
263 DATA_BLOB blob;
264 NTSTATUS status;
265 enum ndr_err_code ndr_err;
266 TALLOC_CTX *tmp_ctx;
268 /* if possible, remove some depth arrays */
269 while (notify->array->num_depths > 0 &&
270 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
271 notify->array->num_depths--;
274 /* we might just be able to delete the record */
275 if (notify->array->num_depths == 0) {
276 return dbwrap_record_delete(rec);
279 tmp_ctx = talloc_new(notify);
280 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
282 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
283 (ndr_push_flags_fn_t)ndr_push_notify_array);
284 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
285 talloc_free(tmp_ctx);
286 return ndr_map_error2ntstatus(ndr_err);
289 if (DEBUGLEVEL >= 10) {
290 DEBUG(10, ("notify_save:\n"));
291 NDR_PRINT_DEBUG(notify_array, notify->array);
294 dbuf.dptr = blob.data;
295 dbuf.dsize = blob.length;
297 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
298 talloc_free(tmp_ctx);
300 return status;
305 handle incoming notify messages
307 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
308 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
310 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
311 enum ndr_err_code ndr_err;
312 struct notify_event ev;
313 TALLOC_CTX *tmp_ctx = talloc_new(notify);
314 struct notify_list *listel;
316 if (tmp_ctx == NULL) {
317 return;
320 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
321 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
322 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
323 talloc_free(tmp_ctx);
324 return;
327 for (listel=notify->list;listel;listel=listel->next) {
328 if (listel->private_data == ev.private_data) {
329 listel->callback(listel->private_data, &ev);
330 break;
334 talloc_free(tmp_ctx);
338 callback from sys_notify telling us about changes from the OS
340 static void sys_notify_callback(struct sys_notify_context *ctx,
341 void *ptr, struct notify_event *ev)
343 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
344 ev->private_data = listel;
345 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
346 ev->action, ev->path));
347 listel->callback(listel->private_data, ev);
351 add an entry to the notify array
353 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
354 struct notify_entry *e,
355 void *private_data, int depth)
357 int i;
358 struct notify_depth *d;
359 struct notify_entry *ee;
361 /* possibly expand the depths array */
362 if (depth >= notify->array->num_depths) {
363 d = talloc_realloc(notify->array, notify->array->depth,
364 struct notify_depth, depth+1);
365 NT_STATUS_HAVE_NO_MEMORY(d);
366 for (i=notify->array->num_depths;i<=depth;i++) {
367 ZERO_STRUCT(d[i]);
369 notify->array->depth = d;
370 notify->array->num_depths = depth+1;
372 d = &notify->array->depth[depth];
374 /* expand the entries array */
375 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
376 d->num_entries+1);
377 NT_STATUS_HAVE_NO_MEMORY(ee);
378 d->entries = ee;
380 d->entries[d->num_entries] = *e;
381 d->entries[d->num_entries].private_data = private_data;
382 d->entries[d->num_entries].server = notify->server;
383 d->entries[d->num_entries].path_len = strlen(e->path);
384 d->num_entries++;
386 d->max_mask |= e->filter;
387 d->max_mask_subdir |= e->subdir_filter;
389 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
391 /* recalculate the maximum masks */
392 d->max_mask = 0;
393 d->max_mask_subdir = 0;
395 for (i=0;i<d->num_entries;i++) {
396 d->max_mask |= d->entries[i].filter;
397 d->max_mask_subdir |= d->entries[i].subdir_filter;
400 return notify_save(notify, rec);
404 Add a non-recursive watch
407 static void notify_add_onelevel(struct notify_context *notify,
408 struct notify_entry *e, void *private_data)
410 struct notify_entry_array *array;
411 struct db_record *rec;
412 DATA_BLOB blob;
413 TDB_DATA dbuf;
414 TDB_DATA value;
415 enum ndr_err_code ndr_err;
416 NTSTATUS status;
418 array = talloc_zero(talloc_tos(), struct notify_entry_array);
419 if (array == NULL) {
420 return;
423 rec = dbwrap_fetch_locked(notify->db_onelevel, talloc_tos(),
424 make_tdb_data((uint8_t *)&e->dir_id,
425 sizeof(e->dir_id)));
426 if (rec == NULL) {
427 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
428 "\n", file_id_string_tos(&e->dir_id)));
429 TALLOC_FREE(array);
430 return;
433 value = dbwrap_record_get_value(rec);
434 blob.data = (uint8_t *)value.dptr;
435 blob.length = value.dsize;
437 if (blob.length > 0) {
438 ndr_err = ndr_pull_struct_blob(&blob, array, array,
439 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
440 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
441 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
442 ndr_errstr(ndr_err)));
443 TALLOC_FREE(array);
444 return;
446 if (DEBUGLEVEL >= 10) {
447 DEBUG(10, ("notify_add_onelevel:\n"));
448 NDR_PRINT_DEBUG(notify_entry_array, array);
452 array->entries = talloc_realloc(array, array->entries,
453 struct notify_entry,
454 array->num_entries+1);
455 if (array->entries == NULL) {
456 TALLOC_FREE(array);
457 return;
459 array->entries[array->num_entries] = *e;
460 array->entries[array->num_entries].private_data = private_data;
461 array->entries[array->num_entries].server = notify->server;
462 array->num_entries += 1;
464 ndr_err = ndr_push_struct_blob(&blob, rec, array,
465 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
466 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
467 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
468 ndr_errstr(ndr_err)));
469 TALLOC_FREE(array);
470 return;
473 if (DEBUGLEVEL >= 10) {
474 DEBUG(10, ("notify_add_onelevel:\n"));
475 NDR_PRINT_DEBUG(notify_entry_array, array);
478 dbuf.dptr = blob.data;
479 dbuf.dsize = blob.length;
481 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
482 TALLOC_FREE(array);
483 if (!NT_STATUS_IS_OK(status)) {
484 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
485 nt_errstr(status)));
486 return;
488 e->filter = 0;
489 return;
494 add a notify watch. This is called when a notify is first setup on a open
495 directory handle.
497 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
498 void (*callback)(void *, const struct notify_event *),
499 void *private_data)
501 struct notify_entry e = *e0;
502 NTSTATUS status;
503 char *tmp_path = NULL;
504 struct notify_list *listel;
505 size_t len;
506 int depth;
507 struct db_record *rec;
509 /* see if change notify is enabled at all */
510 if (notify == NULL) {
511 return NT_STATUS_NOT_IMPLEMENTED;
514 status = notify_fetch_locked(notify, &rec);
515 NT_STATUS_NOT_OK_RETURN(status);
517 status = notify_load(notify, rec);
518 if (!NT_STATUS_IS_OK(status)) {
519 talloc_free(rec);
520 return status;
523 /* cope with /. on the end of the path */
524 len = strlen(e.path);
525 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
526 tmp_path = talloc_strndup(notify, e.path, len-2);
527 if (tmp_path == NULL) {
528 status = NT_STATUS_NO_MEMORY;
529 goto done;
531 e.path = tmp_path;
534 depth = count_chars(e.path, '/');
536 listel = talloc_zero(notify, struct notify_list);
537 if (listel == NULL) {
538 status = NT_STATUS_NO_MEMORY;
539 goto done;
542 listel->private_data = private_data;
543 listel->callback = callback;
544 listel->depth = depth;
545 DLIST_ADD(notify->list, listel);
547 /* ignore failures from sys_notify */
548 if (notify->sys_notify_ctx != NULL) {
550 this call will modify e.filter and e.subdir_filter
551 to remove bits handled by the backend
553 status = sys_notify_watch(notify->sys_notify_ctx, &e,
554 sys_notify_callback, listel,
555 &listel->sys_notify_handle);
556 if (NT_STATUS_IS_OK(status)) {
557 talloc_steal(listel, listel->sys_notify_handle);
561 if (e.filter != 0) {
562 notify_add_onelevel(notify, &e, private_data);
563 status = NT_STATUS_OK;
566 /* if the system notify handler couldn't handle some of the
567 filter bits, or couldn't handle a request for recursion
568 then we need to install it in the array used for the
569 intra-samba notify handling */
570 if (e.filter != 0 || e.subdir_filter != 0) {
571 status = notify_add_array(notify, rec, &e, private_data, depth);
574 done:
575 talloc_free(rec);
576 talloc_free(tmp_path);
578 return status;
581 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
582 const struct file_id *fid,
583 void *private_data)
585 struct notify_entry_array *array;
586 struct db_record *rec;
587 DATA_BLOB blob;
588 TDB_DATA dbuf;
589 TDB_DATA value;
590 enum ndr_err_code ndr_err;
591 NTSTATUS status;
592 int i;
594 if (notify == NULL) {
595 return NT_STATUS_NOT_IMPLEMENTED;
598 array = talloc_zero(talloc_tos(), struct notify_entry_array);
599 if (array == NULL) {
600 return NT_STATUS_NO_MEMORY;
603 rec = dbwrap_fetch_locked(
604 notify->db_onelevel, array,
605 make_tdb_data((const uint8_t *)fid, sizeof(*fid)));
606 if (rec == NULL) {
607 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
608 "\n", file_id_string_tos(fid)));
609 TALLOC_FREE(array);
610 return NT_STATUS_INTERNAL_DB_CORRUPTION;
613 value = dbwrap_record_get_value(rec);
614 blob.data = (uint8_t *)value.dptr;
615 blob.length = value.dsize;
617 if (blob.length > 0) {
618 ndr_err = ndr_pull_struct_blob(&blob, array, array,
619 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
620 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
621 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
622 ndr_errstr(ndr_err)));
623 TALLOC_FREE(array);
624 return ndr_map_error2ntstatus(ndr_err);
626 if (DEBUGLEVEL >= 10) {
627 DEBUG(10, ("notify_remove_onelevel:\n"));
628 NDR_PRINT_DEBUG(notify_entry_array, array);
632 for (i=0; i<array->num_entries; i++) {
633 if ((private_data == array->entries[i].private_data) &&
634 cluster_id_equal(&notify->server,
635 &array->entries[i].server)) {
636 break;
640 if (i == array->num_entries) {
641 TALLOC_FREE(array);
642 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
645 array->entries[i] = array->entries[array->num_entries-1];
646 array->num_entries -= 1;
648 if (array->num_entries == 0) {
649 dbwrap_record_delete(rec);
650 TALLOC_FREE(array);
651 return NT_STATUS_OK;
654 ndr_err = ndr_push_struct_blob(&blob, rec, array,
655 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
656 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
657 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
658 ndr_errstr(ndr_err)));
659 TALLOC_FREE(array);
660 return ndr_map_error2ntstatus(ndr_err);
663 if (DEBUGLEVEL >= 10) {
664 DEBUG(10, ("notify_add_onelevel:\n"));
665 NDR_PRINT_DEBUG(notify_entry_array, array);
668 dbuf.dptr = blob.data;
669 dbuf.dsize = blob.length;
671 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
672 TALLOC_FREE(array);
673 if (!NT_STATUS_IS_OK(status)) {
674 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
675 nt_errstr(status)));
676 return status;
678 return NT_STATUS_OK;
682 remove a notify watch. Called when the directory handle is closed
684 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
686 NTSTATUS status;
687 struct notify_list *listel;
688 int i, depth;
689 struct notify_depth *d;
690 struct db_record *rec;
692 /* see if change notify is enabled at all */
693 if (notify == NULL) {
694 return NT_STATUS_NOT_IMPLEMENTED;
697 for (listel=notify->list;listel;listel=listel->next) {
698 if (listel->private_data == private_data) {
699 DLIST_REMOVE(notify->list, listel);
700 break;
703 if (listel == NULL) {
704 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
707 depth = listel->depth;
709 talloc_free(listel);
711 status = notify_fetch_locked(notify, &rec);
712 NT_STATUS_NOT_OK_RETURN(status);
714 status = notify_load(notify, rec);
715 if (!NT_STATUS_IS_OK(status)) {
716 talloc_free(rec);
717 return status;
720 if (depth >= notify->array->num_depths) {
721 talloc_free(rec);
722 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
725 /* we only have to search at the depth of this element */
726 d = &notify->array->depth[depth];
728 for (i=0;i<d->num_entries;i++) {
729 if (private_data == d->entries[i].private_data &&
730 cluster_id_equal(&notify->server, &d->entries[i].server)) {
731 break;
734 if (i == d->num_entries) {
735 talloc_free(rec);
736 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
739 if (i < d->num_entries-1) {
740 memmove(&d->entries[i], &d->entries[i+1],
741 sizeof(d->entries[i])*(d->num_entries-(i+1)));
743 d->num_entries--;
745 status = notify_save(notify, rec);
747 talloc_free(rec);
749 return status;
753 remove all notify watches for a messaging server
755 static NTSTATUS notify_remove_all(struct notify_context *notify,
756 const struct server_id *server)
758 NTSTATUS status;
759 int i, depth, del_count=0;
760 struct db_record *rec;
762 status = notify_fetch_locked(notify, &rec);
763 NT_STATUS_NOT_OK_RETURN(status);
765 status = notify_load(notify, rec);
766 if (!NT_STATUS_IS_OK(status)) {
767 talloc_free(rec);
768 return status;
771 /* we have to search for all entries across all depths, looking for matches
772 for the server id */
773 for (depth=0;depth<notify->array->num_depths;depth++) {
774 struct notify_depth *d = &notify->array->depth[depth];
775 for (i=0;i<d->num_entries;i++) {
776 if (cluster_id_equal(server, &d->entries[i].server)) {
777 if (i < d->num_entries-1) {
778 memmove(&d->entries[i], &d->entries[i+1],
779 sizeof(d->entries[i])*(d->num_entries-(i+1)));
781 i--;
782 d->num_entries--;
783 del_count++;
788 if (del_count > 0) {
789 status = notify_save(notify, rec);
792 talloc_free(rec);
794 return status;
799 send a notify message to another messaging server
801 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
802 const char *path, uint32_t action)
804 struct notify_event ev;
805 DATA_BLOB data;
806 NTSTATUS status;
807 enum ndr_err_code ndr_err;
808 TALLOC_CTX *tmp_ctx;
810 ev.action = action;
811 ev.path = path;
812 ev.private_data = e->private_data;
814 tmp_ctx = talloc_new(notify);
816 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
817 (ndr_push_flags_fn_t)ndr_push_notify_event);
818 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
819 talloc_free(tmp_ctx);
820 return ndr_map_error2ntstatus(ndr_err);
823 status = messaging_send(notify->messaging_ctx, e->server,
824 MSG_PVFS_NOTIFY, &data);
825 talloc_free(tmp_ctx);
826 return status;
829 void notify_onelevel(struct notify_context *notify, uint32_t action,
830 uint32_t filter, struct file_id fid, const char *name)
832 struct notify_entry_array *array;
833 TDB_DATA dbuf;
834 DATA_BLOB blob;
835 bool have_dead_entries = false;
836 int i;
837 NTSTATUS status;
839 if (notify == NULL) {
840 return;
843 array = talloc_zero(talloc_tos(), struct notify_entry_array);
844 if (array == NULL) {
845 return;
848 status = dbwrap_fetch(notify->db_onelevel, array,
849 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
850 &dbuf);
851 if (!NT_STATUS_IS_OK(status)) {
852 TALLOC_FREE(array);
853 return;
856 blob.data = (uint8 *)dbuf.dptr;
857 blob.length = dbuf.dsize;
859 if (blob.length > 0) {
860 enum ndr_err_code ndr_err;
861 ndr_err = ndr_pull_struct_blob(&blob, array, array,
862 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
863 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
864 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
865 ndr_errstr(ndr_err)));
866 TALLOC_FREE(array);
867 return;
869 if (DEBUGLEVEL >= 10) {
870 DEBUG(10, ("notify_onelevel:\n"));
871 NDR_PRINT_DEBUG(notify_entry_array, array);
875 for (i=0; i<array->num_entries; i++) {
876 struct notify_entry *e = &array->entries[i];
878 if ((e->filter & filter) != 0) {
879 status = notify_send(notify, e, name, action);
880 if (NT_STATUS_EQUAL(
881 status, NT_STATUS_INVALID_HANDLE)) {
883 * Mark the entry as dead. All entries have a
884 * path set. The marker used here is setting
885 * that to NULL.
887 e->path = NULL;
888 have_dead_entries = true;
893 if (!have_dead_entries) {
894 TALLOC_FREE(array);
895 return;
898 for (i=0; i<array->num_entries; i++) {
899 struct notify_entry *e = &array->entries[i];
900 if (e->path != NULL) {
901 continue;
903 DEBUG(10, ("Deleting notify entries for process %s because "
904 "it's gone\n", procid_str_static(&e->server)));
906 * Potential TODO: This might need optimizing,
907 * notify_remove_onelevel() does a fetch_locked() operation at
908 * every call. But this would only matter if a process with
909 * MANY notifies has died without shutting down properly.
911 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
914 TALLOC_FREE(array);
915 return;
919 trigger a notify message for anyone waiting on a matching event
921 This function is called a lot, and needs to be very fast. The unusual data structure
922 and traversal is designed to be fast in the average case, even for large numbers of
923 notifies
925 void notify_trigger(struct notify_context *notify,
926 uint32_t action, uint32_t filter, const char *path)
928 NTSTATUS status;
929 int depth;
930 const char *p, *next_p;
932 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
933 "path=%s\n", (unsigned)action, (unsigned)filter, path));
935 /* see if change notify is enabled at all */
936 if (notify == NULL) {
937 return;
940 again:
941 status = notify_load(notify, NULL);
942 if (!NT_STATUS_IS_OK(status)) {
943 return;
946 /* loop along the given path, working with each directory depth separately */
947 for (depth=0,p=path;
948 p && depth < notify->array->num_depths;
949 p=next_p,depth++) {
950 int p_len = p - path;
951 int min_i, max_i, i;
952 struct notify_depth *d = &notify->array->depth[depth];
953 next_p = strchr(p+1, '/');
955 /* see if there are any entries at this depth */
956 if (d->num_entries == 0) continue;
958 /* try to skip based on the maximum mask. If next_p is
959 NULL then we know it will be a 'this directory'
960 match, otherwise it must be a subdir match */
961 if (next_p != NULL) {
962 if (0 == (filter & d->max_mask_subdir)) {
963 continue;
965 } else {
966 if (0 == (filter & d->max_mask)) {
967 continue;
971 /* we know there is an entry here worth looking
972 for. Use a bisection search to find the first entry
973 with a matching path */
974 min_i = 0;
975 max_i = d->num_entries-1;
977 while (min_i < max_i) {
978 struct notify_entry *e;
979 int cmp;
980 i = (min_i+max_i)/2;
981 e = &d->entries[i];
982 cmp = strncmp(path, e->path, p_len);
983 if (cmp == 0) {
984 if (p_len == e->path_len) {
985 max_i = i;
986 } else {
987 max_i = i-1;
989 } else if (cmp < 0) {
990 max_i = i-1;
991 } else {
992 min_i = i+1;
996 if (min_i != max_i) {
997 /* none match */
998 continue;
1001 /* we now know that the entries start at min_i */
1002 for (i=min_i;i<d->num_entries;i++) {
1003 struct notify_entry *e = &d->entries[i];
1004 if (p_len != e->path_len ||
1005 strncmp(path, e->path, p_len) != 0) break;
1006 if (next_p != NULL) {
1007 if (0 == (filter & e->subdir_filter)) {
1008 continue;
1010 } else {
1011 if (0 == (filter & e->filter)) {
1012 continue;
1015 status = notify_send(notify, e, path + e->path_len + 1,
1016 action);
1018 if (NT_STATUS_EQUAL(
1019 status, NT_STATUS_INVALID_HANDLE)) {
1020 struct server_id server = e->server;
1022 DEBUG(10, ("Deleting notify entries for "
1023 "process %s because it's gone\n",
1024 procid_str_static(&e->server)));
1025 notify_remove_all(notify, &server);
1026 goto again;