s3-docs: Fix typo.
[Samba/gebeck_regimport.git] / source3 / smbd / notify_internal.c
blob2dd8557a87a73e8ce55994ba2a69f24d9791456b
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 = notify->db_recursive->get_seqnum(
123 notify->db_recursive);
124 notify->key = string_term_tdb_data(NOTIFY_KEY);
126 talloc_set_destructor(notify, notify_destructor);
128 /* register with the messaging subsystem for the notify
129 message type */
130 messaging_register(notify->messaging_ctx, notify,
131 MSG_PVFS_NOTIFY, notify_handler);
133 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
135 return notify;
138 bool notify_internal_parent_init(TALLOC_CTX *mem_ctx)
140 struct tdb_wrap *db1, *db2;
142 if (lp_clustering()) {
143 return true;
147 * Open the tdbs in the parent process (smbd) so that our
148 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
149 * work.
152 db1 = tdb_wrap_open(mem_ctx, lock_path("notify.tdb"),
153 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
154 O_RDWR|O_CREAT, 0644);
155 if (db1 == NULL) {
156 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
157 return false;
159 db2 = tdb_wrap_open(mem_ctx, lock_path("notify_onelevel.tdb"),
160 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644);
161 if (db2 == NULL) {
162 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
163 strerror(errno)));
164 TALLOC_FREE(db1);
165 return false;
167 return true;
171 lock and fetch the record
173 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
175 *rec = notify->db_recursive->fetch_locked(notify->db_recursive,
176 notify, notify->key);
177 if (*rec == NULL) {
178 return NT_STATUS_INTERNAL_DB_CORRUPTION;
180 return NT_STATUS_OK;
184 load the notify array
186 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
188 TDB_DATA dbuf;
189 DATA_BLOB blob;
190 NTSTATUS status;
191 int seqnum;
193 seqnum = notify->db_recursive->get_seqnum(notify->db_recursive);
195 if (seqnum == notify->seqnum && notify->array != NULL) {
196 return NT_STATUS_OK;
199 notify->seqnum = seqnum;
201 talloc_free(notify->array);
202 notify->array = talloc_zero(notify, struct notify_array);
203 NT_STATUS_HAVE_NO_MEMORY(notify->array);
205 if (!rec) {
206 if (notify->db_recursive->fetch(notify->db_recursive, notify,
207 notify->key, &dbuf) != 0) {
208 return NT_STATUS_INTERNAL_DB_CORRUPTION;
210 } else {
211 dbuf = rec->value;
214 blob.data = (uint8 *)dbuf.dptr;
215 blob.length = dbuf.dsize;
217 status = NT_STATUS_OK;
218 if (blob.length > 0) {
219 enum ndr_err_code ndr_err;
220 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
221 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
222 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
223 /* 1. log that we got a corrupt notify_array
224 * 2. clear the variable the garbage was stored into to not trip
225 * over it next time this method is entered with the same seqnum
226 * 3. delete it from the database */
227 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
229 ZERO_STRUCTP(notify->array);
230 if (rec != NULL) {
231 rec->delete_rec(rec);
234 } else {
235 if (DEBUGLEVEL >= 10) {
236 DEBUG(10, ("notify_load:\n"));
237 NDR_PRINT_DEBUG(notify_array, notify->array);
243 if (!rec) {
244 talloc_free(dbuf.dptr);
247 return status;
251 compare notify entries for sorting
253 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
255 return strcmp(e1->path, e2->path);
259 save the notify array
261 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
263 TDB_DATA dbuf;
264 DATA_BLOB blob;
265 NTSTATUS status;
266 enum ndr_err_code ndr_err;
267 TALLOC_CTX *tmp_ctx;
269 /* if possible, remove some depth arrays */
270 while (notify->array->num_depths > 0 &&
271 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
272 notify->array->num_depths--;
275 /* we might just be able to delete the record */
276 if (notify->array->num_depths == 0) {
277 return rec->delete_rec(rec);
280 tmp_ctx = talloc_new(notify);
281 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
283 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
284 (ndr_push_flags_fn_t)ndr_push_notify_array);
285 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
286 talloc_free(tmp_ctx);
287 return ndr_map_error2ntstatus(ndr_err);
290 if (DEBUGLEVEL >= 10) {
291 DEBUG(10, ("notify_save:\n"));
292 NDR_PRINT_DEBUG(notify_array, notify->array);
295 dbuf.dptr = blob.data;
296 dbuf.dsize = blob.length;
298 status = rec->store(rec, dbuf, TDB_REPLACE);
299 talloc_free(tmp_ctx);
301 return status;
306 handle incoming notify messages
308 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
309 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
311 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
312 enum ndr_err_code ndr_err;
313 struct notify_event ev;
314 TALLOC_CTX *tmp_ctx = talloc_new(notify);
315 struct notify_list *listel;
317 if (tmp_ctx == NULL) {
318 return;
321 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
322 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
323 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
324 talloc_free(tmp_ctx);
325 return;
328 for (listel=notify->list;listel;listel=listel->next) {
329 if (listel->private_data == ev.private_data) {
330 listel->callback(listel->private_data, &ev);
331 break;
335 talloc_free(tmp_ctx);
339 callback from sys_notify telling us about changes from the OS
341 static void sys_notify_callback(struct sys_notify_context *ctx,
342 void *ptr, struct notify_event *ev)
344 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
345 ev->private_data = listel;
346 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
347 ev->action, ev->path));
348 listel->callback(listel->private_data, ev);
352 add an entry to the notify array
354 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
355 struct notify_entry *e,
356 void *private_data, int depth)
358 int i;
359 struct notify_depth *d;
360 struct notify_entry *ee;
362 /* possibly expand the depths array */
363 if (depth >= notify->array->num_depths) {
364 d = talloc_realloc(notify->array, notify->array->depth,
365 struct notify_depth, depth+1);
366 NT_STATUS_HAVE_NO_MEMORY(d);
367 for (i=notify->array->num_depths;i<=depth;i++) {
368 ZERO_STRUCT(d[i]);
370 notify->array->depth = d;
371 notify->array->num_depths = depth+1;
373 d = &notify->array->depth[depth];
375 /* expand the entries array */
376 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
377 d->num_entries+1);
378 NT_STATUS_HAVE_NO_MEMORY(ee);
379 d->entries = ee;
381 d->entries[d->num_entries] = *e;
382 d->entries[d->num_entries].private_data = private_data;
383 d->entries[d->num_entries].server = notify->server;
384 d->entries[d->num_entries].path_len = strlen(e->path);
385 d->num_entries++;
387 d->max_mask |= e->filter;
388 d->max_mask_subdir |= e->subdir_filter;
390 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
392 /* recalculate the maximum masks */
393 d->max_mask = 0;
394 d->max_mask_subdir = 0;
396 for (i=0;i<d->num_entries;i++) {
397 d->max_mask |= d->entries[i].filter;
398 d->max_mask_subdir |= d->entries[i].subdir_filter;
401 return notify_save(notify, rec);
405 Add a non-recursive watch
408 static void notify_add_onelevel(struct notify_context *notify,
409 struct notify_entry *e, void *private_data)
411 struct notify_entry_array *array;
412 struct db_record *rec;
413 DATA_BLOB blob;
414 TDB_DATA dbuf;
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 = notify->db_onelevel->fetch_locked(
424 notify->db_onelevel, talloc_tos(),
425 make_tdb_data((uint8_t *)&e->dir_id, 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 blob.data = (uint8_t *)rec->value.dptr;
434 blob.length = rec->value.dsize;
436 if (blob.length > 0) {
437 ndr_err = ndr_pull_struct_blob(&blob, array, array,
438 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
439 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
440 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
441 ndr_errstr(ndr_err)));
442 TALLOC_FREE(array);
443 return;
445 if (DEBUGLEVEL >= 10) {
446 DEBUG(10, ("notify_add_onelevel:\n"));
447 NDR_PRINT_DEBUG(notify_entry_array, array);
451 array->entries = talloc_realloc(array, array->entries,
452 struct notify_entry,
453 array->num_entries+1);
454 if (array->entries == NULL) {
455 TALLOC_FREE(array);
456 return;
458 array->entries[array->num_entries] = *e;
459 array->entries[array->num_entries].private_data = private_data;
460 array->entries[array->num_entries].server = notify->server;
461 array->num_entries += 1;
463 ndr_err = ndr_push_struct_blob(&blob, rec, array,
464 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
465 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
466 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
467 ndr_errstr(ndr_err)));
468 TALLOC_FREE(array);
469 return;
472 if (DEBUGLEVEL >= 10) {
473 DEBUG(10, ("notify_add_onelevel:\n"));
474 NDR_PRINT_DEBUG(notify_entry_array, array);
477 dbuf.dptr = blob.data;
478 dbuf.dsize = blob.length;
480 status = rec->store(rec, dbuf, TDB_REPLACE);
481 TALLOC_FREE(array);
482 if (!NT_STATUS_IS_OK(status)) {
483 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
484 nt_errstr(status)));
485 return;
487 e->filter = 0;
488 return;
493 add a notify watch. This is called when a notify is first setup on a open
494 directory handle.
496 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
497 void (*callback)(void *, const struct notify_event *),
498 void *private_data)
500 struct notify_entry e = *e0;
501 NTSTATUS status;
502 char *tmp_path = NULL;
503 struct notify_list *listel;
504 size_t len;
505 int depth;
506 struct db_record *rec;
508 /* see if change notify is enabled at all */
509 if (notify == NULL) {
510 return NT_STATUS_NOT_IMPLEMENTED;
513 status = notify_fetch_locked(notify, &rec);
514 NT_STATUS_NOT_OK_RETURN(status);
516 status = notify_load(notify, rec);
517 if (!NT_STATUS_IS_OK(status)) {
518 talloc_free(rec);
519 return status;
522 /* cope with /. on the end of the path */
523 len = strlen(e.path);
524 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
525 tmp_path = talloc_strndup(notify, e.path, len-2);
526 if (tmp_path == NULL) {
527 status = NT_STATUS_NO_MEMORY;
528 goto done;
530 e.path = tmp_path;
533 depth = count_chars(e.path, '/');
535 listel = talloc_zero(notify, struct notify_list);
536 if (listel == NULL) {
537 status = NT_STATUS_NO_MEMORY;
538 goto done;
541 listel->private_data = private_data;
542 listel->callback = callback;
543 listel->depth = depth;
544 DLIST_ADD(notify->list, listel);
546 /* ignore failures from sys_notify */
547 if (notify->sys_notify_ctx != NULL) {
549 this call will modify e.filter and e.subdir_filter
550 to remove bits handled by the backend
552 status = sys_notify_watch(notify->sys_notify_ctx, &e,
553 sys_notify_callback, listel,
554 &listel->sys_notify_handle);
555 if (NT_STATUS_IS_OK(status)) {
556 talloc_steal(listel, listel->sys_notify_handle);
560 if (e.filter != 0) {
561 notify_add_onelevel(notify, &e, private_data);
562 status = NT_STATUS_OK;
565 /* if the system notify handler couldn't handle some of the
566 filter bits, or couldn't handle a request for recursion
567 then we need to install it in the array used for the
568 intra-samba notify handling */
569 if (e.filter != 0 || e.subdir_filter != 0) {
570 status = notify_add_array(notify, rec, &e, private_data, depth);
573 done:
574 talloc_free(rec);
575 talloc_free(tmp_path);
577 return status;
580 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
581 const struct file_id *fid,
582 void *private_data)
584 struct notify_entry_array *array;
585 struct db_record *rec;
586 DATA_BLOB blob;
587 TDB_DATA dbuf;
588 enum ndr_err_code ndr_err;
589 NTSTATUS status;
590 int i;
592 if (notify == NULL) {
593 return NT_STATUS_NOT_IMPLEMENTED;
596 array = talloc_zero(talloc_tos(), struct notify_entry_array);
597 if (array == NULL) {
598 return NT_STATUS_NO_MEMORY;
601 rec = notify->db_onelevel->fetch_locked(
602 notify->db_onelevel, array,
603 make_tdb_data((const uint8_t *)fid, sizeof(*fid)));
604 if (rec == NULL) {
605 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
606 "\n", file_id_string_tos(fid)));
607 TALLOC_FREE(array);
608 return NT_STATUS_INTERNAL_DB_CORRUPTION;
611 blob.data = (uint8_t *)rec->value.dptr;
612 blob.length = rec->value.dsize;
614 if (blob.length > 0) {
615 ndr_err = ndr_pull_struct_blob(&blob, array, array,
616 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
617 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
618 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
619 ndr_errstr(ndr_err)));
620 TALLOC_FREE(array);
621 return ndr_map_error2ntstatus(ndr_err);
623 if (DEBUGLEVEL >= 10) {
624 DEBUG(10, ("notify_remove_onelevel:\n"));
625 NDR_PRINT_DEBUG(notify_entry_array, array);
629 for (i=0; i<array->num_entries; i++) {
630 if ((private_data == array->entries[i].private_data) &&
631 cluster_id_equal(&notify->server,
632 &array->entries[i].server)) {
633 break;
637 if (i == array->num_entries) {
638 TALLOC_FREE(array);
639 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
642 array->entries[i] = array->entries[array->num_entries-1];
643 array->num_entries -= 1;
645 if (array->num_entries == 0) {
646 rec->delete_rec(rec);
647 TALLOC_FREE(array);
648 return NT_STATUS_OK;
651 ndr_err = ndr_push_struct_blob(&blob, rec, array,
652 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
653 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
654 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
655 ndr_errstr(ndr_err)));
656 TALLOC_FREE(array);
657 return ndr_map_error2ntstatus(ndr_err);
660 if (DEBUGLEVEL >= 10) {
661 DEBUG(10, ("notify_add_onelevel:\n"));
662 NDR_PRINT_DEBUG(notify_entry_array, array);
665 dbuf.dptr = blob.data;
666 dbuf.dsize = blob.length;
668 status = rec->store(rec, dbuf, TDB_REPLACE);
669 TALLOC_FREE(array);
670 if (!NT_STATUS_IS_OK(status)) {
671 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
672 nt_errstr(status)));
673 return status;
675 return NT_STATUS_OK;
679 remove a notify watch. Called when the directory handle is closed
681 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
683 NTSTATUS status;
684 struct notify_list *listel;
685 int i, depth;
686 struct notify_depth *d;
687 struct db_record *rec;
689 /* see if change notify is enabled at all */
690 if (notify == NULL) {
691 return NT_STATUS_NOT_IMPLEMENTED;
694 for (listel=notify->list;listel;listel=listel->next) {
695 if (listel->private_data == private_data) {
696 DLIST_REMOVE(notify->list, listel);
697 break;
700 if (listel == NULL) {
701 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
704 depth = listel->depth;
706 talloc_free(listel);
708 status = notify_fetch_locked(notify, &rec);
709 NT_STATUS_NOT_OK_RETURN(status);
711 status = notify_load(notify, rec);
712 if (!NT_STATUS_IS_OK(status)) {
713 talloc_free(rec);
714 return status;
717 if (depth >= notify->array->num_depths) {
718 talloc_free(rec);
719 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
722 /* we only have to search at the depth of this element */
723 d = &notify->array->depth[depth];
725 for (i=0;i<d->num_entries;i++) {
726 if (private_data == d->entries[i].private_data &&
727 cluster_id_equal(&notify->server, &d->entries[i].server)) {
728 break;
731 if (i == d->num_entries) {
732 talloc_free(rec);
733 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
736 if (i < d->num_entries-1) {
737 memmove(&d->entries[i], &d->entries[i+1],
738 sizeof(d->entries[i])*(d->num_entries-(i+1)));
740 d->num_entries--;
742 status = notify_save(notify, rec);
744 talloc_free(rec);
746 return status;
750 remove all notify watches for a messaging server
752 static NTSTATUS notify_remove_all(struct notify_context *notify,
753 const struct server_id *server)
755 NTSTATUS status;
756 int i, depth, del_count=0;
757 struct db_record *rec;
759 status = notify_fetch_locked(notify, &rec);
760 NT_STATUS_NOT_OK_RETURN(status);
762 status = notify_load(notify, rec);
763 if (!NT_STATUS_IS_OK(status)) {
764 talloc_free(rec);
765 return status;
768 /* we have to search for all entries across all depths, looking for matches
769 for the server id */
770 for (depth=0;depth<notify->array->num_depths;depth++) {
771 struct notify_depth *d = &notify->array->depth[depth];
772 for (i=0;i<d->num_entries;i++) {
773 if (cluster_id_equal(server, &d->entries[i].server)) {
774 if (i < d->num_entries-1) {
775 memmove(&d->entries[i], &d->entries[i+1],
776 sizeof(d->entries[i])*(d->num_entries-(i+1)));
778 i--;
779 d->num_entries--;
780 del_count++;
785 if (del_count > 0) {
786 status = notify_save(notify, rec);
789 talloc_free(rec);
791 return status;
796 send a notify message to another messaging server
798 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
799 const char *path, uint32_t action)
801 struct notify_event ev;
802 DATA_BLOB data;
803 NTSTATUS status;
804 enum ndr_err_code ndr_err;
805 TALLOC_CTX *tmp_ctx;
807 ev.action = action;
808 ev.path = path;
809 ev.private_data = e->private_data;
811 tmp_ctx = talloc_new(notify);
813 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
814 (ndr_push_flags_fn_t)ndr_push_notify_event);
815 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
816 talloc_free(tmp_ctx);
817 return ndr_map_error2ntstatus(ndr_err);
820 status = messaging_send(notify->messaging_ctx, e->server,
821 MSG_PVFS_NOTIFY, &data);
822 talloc_free(tmp_ctx);
823 return status;
826 void notify_onelevel(struct notify_context *notify, uint32_t action,
827 uint32_t filter, struct file_id fid, const char *name)
829 struct notify_entry_array *array;
830 TDB_DATA dbuf;
831 DATA_BLOB blob;
832 bool have_dead_entries = false;
833 int i;
835 if (notify == NULL) {
836 return;
839 array = talloc_zero(talloc_tos(), struct notify_entry_array);
840 if (array == NULL) {
841 return;
844 if (notify->db_onelevel->fetch(
845 notify->db_onelevel, array,
846 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
847 &dbuf) != 0) {
848 TALLOC_FREE(array);
849 return;
852 blob.data = (uint8 *)dbuf.dptr;
853 blob.length = dbuf.dsize;
855 if (blob.length > 0) {
856 enum ndr_err_code ndr_err;
857 ndr_err = ndr_pull_struct_blob(&blob, array, array,
858 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
859 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
860 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
861 ndr_errstr(ndr_err)));
862 TALLOC_FREE(array);
863 return;
865 if (DEBUGLEVEL >= 10) {
866 DEBUG(10, ("notify_onelevel:\n"));
867 NDR_PRINT_DEBUG(notify_entry_array, array);
871 for (i=0; i<array->num_entries; i++) {
872 struct notify_entry *e = &array->entries[i];
874 if ((e->filter & filter) != 0) {
875 NTSTATUS status;
877 status = notify_send(notify, e, name, action);
878 if (NT_STATUS_EQUAL(
879 status, NT_STATUS_INVALID_HANDLE)) {
881 * Mark the entry as dead. All entries have a
882 * path set. The marker used here is setting
883 * that to NULL.
885 e->path = NULL;
886 have_dead_entries = true;
891 if (!have_dead_entries) {
892 TALLOC_FREE(array);
893 return;
896 for (i=0; i<array->num_entries; i++) {
897 struct notify_entry *e = &array->entries[i];
898 if (e->path != NULL) {
899 continue;
901 DEBUG(10, ("Deleting notify entries for process %s because "
902 "it's gone\n", procid_str_static(&e->server)));
904 * Potential TODO: This might need optimizing,
905 * notify_remove_onelevel() does a fetch_locked() operation at
906 * every call. But this would only matter if a process with
907 * MANY notifies has died without shutting down properly.
909 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
912 TALLOC_FREE(array);
913 return;
917 trigger a notify message for anyone waiting on a matching event
919 This function is called a lot, and needs to be very fast. The unusual data structure
920 and traversal is designed to be fast in the average case, even for large numbers of
921 notifies
923 void notify_trigger(struct notify_context *notify,
924 uint32_t action, uint32_t filter, const char *path)
926 NTSTATUS status;
927 int depth;
928 const char *p, *next_p;
930 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
931 "path=%s\n", (unsigned)action, (unsigned)filter, path));
933 /* see if change notify is enabled at all */
934 if (notify == NULL) {
935 return;
938 again:
939 status = notify_load(notify, NULL);
940 if (!NT_STATUS_IS_OK(status)) {
941 return;
944 /* loop along the given path, working with each directory depth separately */
945 for (depth=0,p=path;
946 p && depth < notify->array->num_depths;
947 p=next_p,depth++) {
948 int p_len = p - path;
949 int min_i, max_i, i;
950 struct notify_depth *d = &notify->array->depth[depth];
951 next_p = strchr(p+1, '/');
953 /* see if there are any entries at this depth */
954 if (d->num_entries == 0) continue;
956 /* try to skip based on the maximum mask. If next_p is
957 NULL then we know it will be a 'this directory'
958 match, otherwise it must be a subdir match */
959 if (next_p != NULL) {
960 if (0 == (filter & d->max_mask_subdir)) {
961 continue;
963 } else {
964 if (0 == (filter & d->max_mask)) {
965 continue;
969 /* we know there is an entry here worth looking
970 for. Use a bisection search to find the first entry
971 with a matching path */
972 min_i = 0;
973 max_i = d->num_entries-1;
975 while (min_i < max_i) {
976 struct notify_entry *e;
977 int cmp;
978 i = (min_i+max_i)/2;
979 e = &d->entries[i];
980 cmp = strncmp(path, e->path, p_len);
981 if (cmp == 0) {
982 if (p_len == e->path_len) {
983 max_i = i;
984 } else {
985 max_i = i-1;
987 } else if (cmp < 0) {
988 max_i = i-1;
989 } else {
990 min_i = i+1;
994 if (min_i != max_i) {
995 /* none match */
996 continue;
999 /* we now know that the entries start at min_i */
1000 for (i=min_i;i<d->num_entries;i++) {
1001 struct notify_entry *e = &d->entries[i];
1002 if (p_len != e->path_len ||
1003 strncmp(path, e->path, p_len) != 0) break;
1004 if (next_p != NULL) {
1005 if (0 == (filter & e->subdir_filter)) {
1006 continue;
1008 } else {
1009 if (0 == (filter & e->filter)) {
1010 continue;
1013 status = notify_send(notify, e, path + e->path_len + 1,
1014 action);
1016 if (NT_STATUS_EQUAL(
1017 status, NT_STATUS_INVALID_HANDLE)) {
1018 struct server_id server = e->server;
1020 DEBUG(10, ("Deleting notify entries for "
1021 "process %s because it's gone\n",
1022 procid_str_static(&e->server)));
1023 notify_remove_all(notify, &server);
1024 goto again;