s3-waf: Link errors3 only against needed subsystems.
[Samba/id10ts.git] / source3 / smbd / notify_internal.c
blobbe91cd58cdb447c165ee1af7654322a5876c8c7f
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"
35 #include "lib/param/param.h"
37 struct notify_context {
38 struct db_context *db_recursive;
39 struct db_context *db_onelevel;
40 struct server_id server;
41 struct messaging_context *messaging_ctx;
42 struct notify_list *list;
43 struct notify_array *array;
44 int seqnum;
45 struct sys_notify_context *sys_notify_ctx;
46 TDB_DATA key;
50 struct notify_list {
51 struct notify_list *next, *prev;
52 void *private_data;
53 void (*callback)(void *, const struct notify_event *);
54 void *sys_notify_handle;
55 int depth;
58 #define NOTIFY_KEY "notify array"
60 #define NOTIFY_ENABLE "notify:enable"
61 #define NOTIFY_ENABLE_DEFAULT True
63 static NTSTATUS notify_remove_all(struct notify_context *notify,
64 const struct server_id *server);
65 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
66 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
69 destroy the notify context
71 static int notify_destructor(struct notify_context *notify)
73 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
75 if (notify->list != NULL) {
76 notify_remove_all(notify, &notify->server);
79 return 0;
83 Open up the notify.tdb database. You should close it down using
84 talloc_free(). We need the messaging_ctx to allow for notifications
85 via internal messages
87 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
88 struct messaging_context *messaging_ctx,
89 struct event_context *ev,
90 connection_struct *conn)
92 struct notify_context *notify;
94 if (!lp_change_notify(conn->params)) {
95 return NULL;
98 notify = talloc(mem_ctx, struct notify_context);
99 if (notify == NULL) {
100 return NULL;
103 notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
104 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
105 O_RDWR|O_CREAT, 0644,
106 DBWRAP_LOCK_ORDER_2);
107 if (notify->db_recursive == NULL) {
108 talloc_free(notify);
109 return NULL;
112 notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
113 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
114 O_RDWR|O_CREAT, 0644,
115 DBWRAP_LOCK_ORDER_2);
116 if (notify->db_onelevel == NULL) {
117 talloc_free(notify);
118 return NULL;
121 notify->server = server;
122 notify->messaging_ctx = messaging_ctx;
123 notify->list = NULL;
124 notify->array = NULL;
125 notify->seqnum = dbwrap_get_seqnum(notify->db_recursive);
126 notify->key = string_term_tdb_data(NOTIFY_KEY);
128 talloc_set_destructor(notify, notify_destructor);
130 /* register with the messaging subsystem for the notify
131 message type */
132 messaging_register(notify->messaging_ctx, notify,
133 MSG_PVFS_NOTIFY, notify_handler);
135 notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
137 return notify;
140 bool notify_internal_parent_init(TALLOC_CTX *mem_ctx)
142 struct tdb_wrap *db1, *db2;
143 struct loadparm_context *lp_ctx;
145 if (lp_clustering()) {
146 return true;
149 lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_context());
150 if (lp_ctx == NULL) {
151 DEBUG(0, ("loadparm_init_s3 failed\n"));
152 return false;
155 * Open the tdbs in the parent process (smbd) so that our
156 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
157 * work.
160 db1 = tdb_wrap_open(mem_ctx, lock_path("notify.tdb"),
161 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
162 O_RDWR|O_CREAT, 0644, lp_ctx);
163 if (db1 == NULL) {
164 talloc_unlink(mem_ctx, lp_ctx);
165 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
166 return false;
168 db2 = tdb_wrap_open(mem_ctx, lock_path("notify_onelevel.tdb"),
169 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644, lp_ctx);
170 talloc_unlink(mem_ctx, lp_ctx);
171 if (db2 == NULL) {
172 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
173 strerror(errno)));
174 TALLOC_FREE(db1);
175 return false;
177 return true;
181 lock and fetch the record
183 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
185 *rec = dbwrap_fetch_locked(notify->db_recursive, notify, notify->key);
186 if (*rec == NULL) {
187 return NT_STATUS_INTERNAL_DB_CORRUPTION;
189 return NT_STATUS_OK;
193 load the notify array
195 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
197 TDB_DATA dbuf;
198 DATA_BLOB blob;
199 NTSTATUS status;
200 int seqnum;
202 seqnum = dbwrap_get_seqnum(notify->db_recursive);
204 if (seqnum == notify->seqnum && notify->array != NULL) {
205 return NT_STATUS_OK;
208 notify->seqnum = seqnum;
210 talloc_free(notify->array);
211 notify->array = talloc_zero(notify, struct notify_array);
212 NT_STATUS_HAVE_NO_MEMORY(notify->array);
214 if (!rec) {
215 status = dbwrap_fetch(notify->db_recursive, notify,
216 notify->key, &dbuf);
217 if (!NT_STATUS_IS_OK(status)) {
218 return NT_STATUS_INTERNAL_DB_CORRUPTION;
220 } else {
221 dbuf = dbwrap_record_get_value(rec);
224 blob.data = (uint8 *)dbuf.dptr;
225 blob.length = dbuf.dsize;
227 status = NT_STATUS_OK;
228 if (blob.length > 0) {
229 enum ndr_err_code ndr_err;
230 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
231 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
232 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
233 /* 1. log that we got a corrupt notify_array
234 * 2. clear the variable the garbage was stored into to not trip
235 * over it next time this method is entered with the same seqnum
236 * 3. delete it from the database */
237 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
239 ZERO_STRUCTP(notify->array);
240 if (rec != NULL) {
241 dbwrap_record_delete(rec);
244 } else {
245 if (DEBUGLEVEL >= 10) {
246 DEBUG(10, ("notify_load:\n"));
247 NDR_PRINT_DEBUG(notify_array, notify->array);
253 if (!rec) {
254 talloc_free(dbuf.dptr);
257 return status;
261 compare notify entries for sorting
263 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
265 return strcmp(e1->path, e2->path);
269 save the notify array
271 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
273 TDB_DATA dbuf;
274 DATA_BLOB blob;
275 NTSTATUS status;
276 enum ndr_err_code ndr_err;
277 TALLOC_CTX *tmp_ctx;
279 /* if possible, remove some depth arrays */
280 while (notify->array->num_depths > 0 &&
281 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
282 notify->array->num_depths--;
285 /* we might just be able to delete the record */
286 if (notify->array->num_depths == 0) {
287 return dbwrap_record_delete(rec);
290 tmp_ctx = talloc_new(notify);
291 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
293 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
294 (ndr_push_flags_fn_t)ndr_push_notify_array);
295 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
296 talloc_free(tmp_ctx);
297 return ndr_map_error2ntstatus(ndr_err);
300 if (DEBUGLEVEL >= 10) {
301 DEBUG(10, ("notify_save:\n"));
302 NDR_PRINT_DEBUG(notify_array, notify->array);
305 dbuf.dptr = blob.data;
306 dbuf.dsize = blob.length;
308 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
309 talloc_free(tmp_ctx);
311 return status;
316 handle incoming notify messages
318 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
319 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
321 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
322 enum ndr_err_code ndr_err;
323 struct notify_event ev;
324 TALLOC_CTX *tmp_ctx = talloc_new(notify);
325 struct notify_list *listel;
327 if (tmp_ctx == NULL) {
328 return;
331 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
332 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
333 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
334 talloc_free(tmp_ctx);
335 return;
338 for (listel=notify->list;listel;listel=listel->next) {
339 if (listel->private_data == ev.private_data) {
340 listel->callback(listel->private_data, &ev);
341 break;
345 talloc_free(tmp_ctx);
349 callback from sys_notify telling us about changes from the OS
351 static void sys_notify_callback(struct sys_notify_context *ctx,
352 void *ptr, struct notify_event *ev)
354 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
355 ev->private_data = listel;
356 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
357 ev->action, ev->path));
358 listel->callback(listel->private_data, ev);
362 add an entry to the notify array
364 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
365 struct notify_entry *e,
366 void *private_data, int depth)
368 int i;
369 struct notify_depth *d;
370 struct notify_entry *ee;
372 /* possibly expand the depths array */
373 if (depth >= notify->array->num_depths) {
374 d = talloc_realloc(notify->array, notify->array->depth,
375 struct notify_depth, depth+1);
376 NT_STATUS_HAVE_NO_MEMORY(d);
377 for (i=notify->array->num_depths;i<=depth;i++) {
378 ZERO_STRUCT(d[i]);
380 notify->array->depth = d;
381 notify->array->num_depths = depth+1;
383 d = &notify->array->depth[depth];
385 /* expand the entries array */
386 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
387 d->num_entries+1);
388 NT_STATUS_HAVE_NO_MEMORY(ee);
389 d->entries = ee;
391 d->entries[d->num_entries] = *e;
392 d->entries[d->num_entries].private_data = private_data;
393 d->entries[d->num_entries].server = notify->server;
394 d->entries[d->num_entries].path_len = strlen(e->path);
395 d->num_entries++;
397 d->max_mask |= e->filter;
398 d->max_mask_subdir |= e->subdir_filter;
400 TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
402 /* recalculate the maximum masks */
403 d->max_mask = 0;
404 d->max_mask_subdir = 0;
406 for (i=0;i<d->num_entries;i++) {
407 d->max_mask |= d->entries[i].filter;
408 d->max_mask_subdir |= d->entries[i].subdir_filter;
411 return notify_save(notify, rec);
415 Add a non-recursive watch
418 static void notify_add_onelevel(struct notify_context *notify,
419 struct notify_entry *e, void *private_data)
421 struct notify_entry_array *array;
422 struct db_record *rec;
423 DATA_BLOB blob;
424 TDB_DATA dbuf;
425 TDB_DATA value;
426 enum ndr_err_code ndr_err;
427 NTSTATUS status;
429 array = talloc_zero(talloc_tos(), struct notify_entry_array);
430 if (array == NULL) {
431 return;
434 rec = dbwrap_fetch_locked(notify->db_onelevel, talloc_tos(),
435 make_tdb_data((uint8_t *)&e->dir_id,
436 sizeof(e->dir_id)));
437 if (rec == NULL) {
438 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
439 "\n", file_id_string_tos(&e->dir_id)));
440 TALLOC_FREE(array);
441 return;
444 value = dbwrap_record_get_value(rec);
445 blob.data = (uint8_t *)value.dptr;
446 blob.length = value.dsize;
448 if (blob.length > 0) {
449 ndr_err = ndr_pull_struct_blob(&blob, array, array,
450 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
451 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
452 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
453 ndr_errstr(ndr_err)));
454 TALLOC_FREE(array);
455 return;
457 if (DEBUGLEVEL >= 10) {
458 DEBUG(10, ("notify_add_onelevel:\n"));
459 NDR_PRINT_DEBUG(notify_entry_array, array);
463 array->entries = talloc_realloc(array, array->entries,
464 struct notify_entry,
465 array->num_entries+1);
466 if (array->entries == NULL) {
467 TALLOC_FREE(array);
468 return;
470 array->entries[array->num_entries] = *e;
471 array->entries[array->num_entries].private_data = private_data;
472 array->entries[array->num_entries].server = notify->server;
473 array->num_entries += 1;
475 ndr_err = ndr_push_struct_blob(&blob, rec, array,
476 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
477 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
478 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
479 ndr_errstr(ndr_err)));
480 TALLOC_FREE(array);
481 return;
484 if (DEBUGLEVEL >= 10) {
485 DEBUG(10, ("notify_add_onelevel:\n"));
486 NDR_PRINT_DEBUG(notify_entry_array, array);
489 dbuf.dptr = blob.data;
490 dbuf.dsize = blob.length;
492 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
493 TALLOC_FREE(array);
494 if (!NT_STATUS_IS_OK(status)) {
495 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
496 nt_errstr(status)));
497 return;
499 e->filter = 0;
500 return;
505 add a notify watch. This is called when a notify is first setup on a open
506 directory handle.
508 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
509 void (*callback)(void *, const struct notify_event *),
510 void *private_data)
512 struct notify_entry e = *e0;
513 NTSTATUS status;
514 char *tmp_path = NULL;
515 struct notify_list *listel;
516 size_t len;
517 int depth;
518 struct db_record *rec;
520 /* see if change notify is enabled at all */
521 if (notify == NULL) {
522 return NT_STATUS_NOT_IMPLEMENTED;
525 status = notify_fetch_locked(notify, &rec);
526 NT_STATUS_NOT_OK_RETURN(status);
528 status = notify_load(notify, rec);
529 if (!NT_STATUS_IS_OK(status)) {
530 talloc_free(rec);
531 return status;
534 /* cope with /. on the end of the path */
535 len = strlen(e.path);
536 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
537 tmp_path = talloc_strndup(notify, e.path, len-2);
538 if (tmp_path == NULL) {
539 status = NT_STATUS_NO_MEMORY;
540 goto done;
542 e.path = tmp_path;
545 depth = count_chars(e.path, '/');
547 listel = talloc_zero(notify, struct notify_list);
548 if (listel == NULL) {
549 status = NT_STATUS_NO_MEMORY;
550 goto done;
553 listel->private_data = private_data;
554 listel->callback = callback;
555 listel->depth = depth;
556 DLIST_ADD(notify->list, listel);
558 /* ignore failures from sys_notify */
559 if (notify->sys_notify_ctx != NULL) {
561 this call will modify e.filter and e.subdir_filter
562 to remove bits handled by the backend
564 status = sys_notify_watch(notify->sys_notify_ctx, &e,
565 sys_notify_callback, listel,
566 &listel->sys_notify_handle);
567 if (NT_STATUS_IS_OK(status)) {
568 talloc_steal(listel, listel->sys_notify_handle);
572 if (e.filter != 0) {
573 notify_add_onelevel(notify, &e, private_data);
574 status = NT_STATUS_OK;
577 /* if the system notify handler couldn't handle some of the
578 filter bits, or couldn't handle a request for recursion
579 then we need to install it in the array used for the
580 intra-samba notify handling */
581 if (e.filter != 0 || e.subdir_filter != 0) {
582 status = notify_add_array(notify, rec, &e, private_data, depth);
585 done:
586 talloc_free(rec);
587 talloc_free(tmp_path);
589 return status;
592 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
593 const struct file_id *fid,
594 void *private_data)
596 struct notify_entry_array *array;
597 struct db_record *rec;
598 DATA_BLOB blob;
599 TDB_DATA dbuf;
600 TDB_DATA value;
601 enum ndr_err_code ndr_err;
602 NTSTATUS status;
603 int i;
605 if (notify == NULL) {
606 return NT_STATUS_NOT_IMPLEMENTED;
609 array = talloc_zero(talloc_tos(), struct notify_entry_array);
610 if (array == NULL) {
611 return NT_STATUS_NO_MEMORY;
614 rec = dbwrap_fetch_locked(
615 notify->db_onelevel, array,
616 make_tdb_data((const uint8_t *)fid, sizeof(*fid)));
617 if (rec == NULL) {
618 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
619 "\n", file_id_string_tos(fid)));
620 TALLOC_FREE(array);
621 return NT_STATUS_INTERNAL_DB_CORRUPTION;
624 value = dbwrap_record_get_value(rec);
625 blob.data = (uint8_t *)value.dptr;
626 blob.length = value.dsize;
628 if (blob.length > 0) {
629 ndr_err = ndr_pull_struct_blob(&blob, array, array,
630 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
631 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
632 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
633 ndr_errstr(ndr_err)));
634 TALLOC_FREE(array);
635 return ndr_map_error2ntstatus(ndr_err);
637 if (DEBUGLEVEL >= 10) {
638 DEBUG(10, ("notify_remove_onelevel:\n"));
639 NDR_PRINT_DEBUG(notify_entry_array, array);
643 for (i=0; i<array->num_entries; i++) {
644 if ((private_data == array->entries[i].private_data) &&
645 cluster_id_equal(&notify->server,
646 &array->entries[i].server)) {
647 break;
651 if (i == array->num_entries) {
652 TALLOC_FREE(array);
653 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
656 array->entries[i] = array->entries[array->num_entries-1];
657 array->num_entries -= 1;
659 if (array->num_entries == 0) {
660 dbwrap_record_delete(rec);
661 TALLOC_FREE(array);
662 return NT_STATUS_OK;
665 ndr_err = ndr_push_struct_blob(&blob, rec, array,
666 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
667 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
668 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
669 ndr_errstr(ndr_err)));
670 TALLOC_FREE(array);
671 return ndr_map_error2ntstatus(ndr_err);
674 if (DEBUGLEVEL >= 10) {
675 DEBUG(10, ("notify_add_onelevel:\n"));
676 NDR_PRINT_DEBUG(notify_entry_array, array);
679 dbuf.dptr = blob.data;
680 dbuf.dsize = blob.length;
682 status = dbwrap_record_store(rec, dbuf, TDB_REPLACE);
683 TALLOC_FREE(array);
684 if (!NT_STATUS_IS_OK(status)) {
685 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
686 nt_errstr(status)));
687 return status;
689 return NT_STATUS_OK;
693 remove a notify watch. Called when the directory handle is closed
695 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
697 NTSTATUS status;
698 struct notify_list *listel;
699 int i, depth;
700 struct notify_depth *d;
701 struct db_record *rec;
703 /* see if change notify is enabled at all */
704 if (notify == NULL) {
705 return NT_STATUS_NOT_IMPLEMENTED;
708 for (listel=notify->list;listel;listel=listel->next) {
709 if (listel->private_data == private_data) {
710 DLIST_REMOVE(notify->list, listel);
711 break;
714 if (listel == NULL) {
715 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
718 depth = listel->depth;
720 talloc_free(listel);
722 status = notify_fetch_locked(notify, &rec);
723 NT_STATUS_NOT_OK_RETURN(status);
725 status = notify_load(notify, rec);
726 if (!NT_STATUS_IS_OK(status)) {
727 talloc_free(rec);
728 return status;
731 if (depth >= notify->array->num_depths) {
732 talloc_free(rec);
733 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
736 /* we only have to search at the depth of this element */
737 d = &notify->array->depth[depth];
739 for (i=0;i<d->num_entries;i++) {
740 if (private_data == d->entries[i].private_data &&
741 cluster_id_equal(&notify->server, &d->entries[i].server)) {
742 break;
745 if (i == d->num_entries) {
746 talloc_free(rec);
747 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
750 if (i < d->num_entries-1) {
751 memmove(&d->entries[i], &d->entries[i+1],
752 sizeof(d->entries[i])*(d->num_entries-(i+1)));
754 d->num_entries--;
756 status = notify_save(notify, rec);
758 talloc_free(rec);
760 return status;
764 remove all notify watches for a messaging server
766 static NTSTATUS notify_remove_all(struct notify_context *notify,
767 const struct server_id *server)
769 NTSTATUS status;
770 int i, depth, del_count=0;
771 struct db_record *rec;
773 status = notify_fetch_locked(notify, &rec);
774 NT_STATUS_NOT_OK_RETURN(status);
776 status = notify_load(notify, rec);
777 if (!NT_STATUS_IS_OK(status)) {
778 talloc_free(rec);
779 return status;
782 /* we have to search for all entries across all depths, looking for matches
783 for the server id */
784 for (depth=0;depth<notify->array->num_depths;depth++) {
785 struct notify_depth *d = &notify->array->depth[depth];
786 for (i=0;i<d->num_entries;i++) {
787 if (cluster_id_equal(server, &d->entries[i].server)) {
788 if (i < d->num_entries-1) {
789 memmove(&d->entries[i], &d->entries[i+1],
790 sizeof(d->entries[i])*(d->num_entries-(i+1)));
792 i--;
793 d->num_entries--;
794 del_count++;
799 if (del_count > 0) {
800 status = notify_save(notify, rec);
803 talloc_free(rec);
805 return status;
810 send a notify message to another messaging server
812 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
813 const char *path, uint32_t action)
815 struct notify_event ev;
816 DATA_BLOB data;
817 NTSTATUS status;
818 enum ndr_err_code ndr_err;
819 TALLOC_CTX *tmp_ctx;
821 ev.action = action;
822 ev.path = path;
823 ev.private_data = e->private_data;
825 tmp_ctx = talloc_new(notify);
827 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
828 (ndr_push_flags_fn_t)ndr_push_notify_event);
829 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
830 talloc_free(tmp_ctx);
831 return ndr_map_error2ntstatus(ndr_err);
834 status = messaging_send(notify->messaging_ctx, e->server,
835 MSG_PVFS_NOTIFY, &data);
836 talloc_free(tmp_ctx);
837 return status;
840 void notify_onelevel(struct notify_context *notify, uint32_t action,
841 uint32_t filter, struct file_id fid, const char *name)
843 struct notify_entry_array *array;
844 TDB_DATA dbuf;
845 DATA_BLOB blob;
846 bool have_dead_entries = false;
847 int i;
848 NTSTATUS status;
850 if (notify == NULL) {
851 return;
854 array = talloc_zero(talloc_tos(), struct notify_entry_array);
855 if (array == NULL) {
856 return;
859 status = dbwrap_fetch(notify->db_onelevel, array,
860 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
861 &dbuf);
862 if (!NT_STATUS_IS_OK(status)) {
863 TALLOC_FREE(array);
864 return;
867 blob.data = (uint8 *)dbuf.dptr;
868 blob.length = dbuf.dsize;
870 if (blob.length > 0) {
871 enum ndr_err_code ndr_err;
872 ndr_err = ndr_pull_struct_blob(&blob, array, array,
873 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
874 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
875 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
876 ndr_errstr(ndr_err)));
877 TALLOC_FREE(array);
878 return;
880 if (DEBUGLEVEL >= 10) {
881 DEBUG(10, ("notify_onelevel:\n"));
882 NDR_PRINT_DEBUG(notify_entry_array, array);
886 for (i=0; i<array->num_entries; i++) {
887 struct notify_entry *e = &array->entries[i];
889 if ((e->filter & filter) != 0) {
890 status = notify_send(notify, e, name, action);
891 if (NT_STATUS_EQUAL(
892 status, NT_STATUS_INVALID_HANDLE)) {
894 * Mark the entry as dead. All entries have a
895 * path set. The marker used here is setting
896 * that to NULL.
898 e->path = NULL;
899 have_dead_entries = true;
904 if (!have_dead_entries) {
905 TALLOC_FREE(array);
906 return;
909 for (i=0; i<array->num_entries; i++) {
910 struct notify_entry *e = &array->entries[i];
911 if (e->path != NULL) {
912 continue;
914 DEBUG(10, ("Deleting notify entries for process %s because "
915 "it's gone\n", procid_str_static(&e->server)));
917 * Potential TODO: This might need optimizing,
918 * notify_remove_onelevel() does a fetch_locked() operation at
919 * every call. But this would only matter if a process with
920 * MANY notifies has died without shutting down properly.
922 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
925 TALLOC_FREE(array);
926 return;
930 trigger a notify message for anyone waiting on a matching event
932 This function is called a lot, and needs to be very fast. The unusual data structure
933 and traversal is designed to be fast in the average case, even for large numbers of
934 notifies
936 void notify_trigger(struct notify_context *notify,
937 uint32_t action, uint32_t filter, const char *path)
939 NTSTATUS status;
940 int depth;
941 const char *p, *next_p;
943 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
944 "path=%s\n", (unsigned)action, (unsigned)filter, path));
946 /* see if change notify is enabled at all */
947 if (notify == NULL) {
948 return;
951 again:
952 status = notify_load(notify, NULL);
953 if (!NT_STATUS_IS_OK(status)) {
954 return;
957 /* loop along the given path, working with each directory depth separately */
958 for (depth=0,p=path;
959 p && depth < notify->array->num_depths;
960 p=next_p,depth++) {
961 int p_len = p - path;
962 int min_i, max_i, i;
963 struct notify_depth *d = &notify->array->depth[depth];
964 next_p = strchr(p+1, '/');
966 /* see if there are any entries at this depth */
967 if (d->num_entries == 0) continue;
969 /* try to skip based on the maximum mask. If next_p is
970 NULL then we know it will be a 'this directory'
971 match, otherwise it must be a subdir match */
972 if (next_p != NULL) {
973 if (0 == (filter & d->max_mask_subdir)) {
974 continue;
976 } else {
977 if (0 == (filter & d->max_mask)) {
978 continue;
982 /* we know there is an entry here worth looking
983 for. Use a bisection search to find the first entry
984 with a matching path */
985 min_i = 0;
986 max_i = d->num_entries-1;
988 while (min_i < max_i) {
989 struct notify_entry *e;
990 int cmp;
991 i = (min_i+max_i)/2;
992 e = &d->entries[i];
993 cmp = strncmp(path, e->path, p_len);
994 if (cmp == 0) {
995 if (p_len == e->path_len) {
996 max_i = i;
997 } else {
998 max_i = i-1;
1000 } else if (cmp < 0) {
1001 max_i = i-1;
1002 } else {
1003 min_i = i+1;
1007 if (min_i != max_i) {
1008 /* none match */
1009 continue;
1012 /* we now know that the entries start at min_i */
1013 for (i=min_i;i<d->num_entries;i++) {
1014 struct notify_entry *e = &d->entries[i];
1015 if (p_len != e->path_len ||
1016 strncmp(path, e->path, p_len) != 0) break;
1017 if (next_p != NULL) {
1018 if (0 == (filter & e->subdir_filter)) {
1019 continue;
1021 } else {
1022 if (0 == (filter & e->filter)) {
1023 continue;
1026 status = notify_send(notify, e, path + e->path_len + 1,
1027 action);
1029 if (NT_STATUS_EQUAL(
1030 status, NT_STATUS_INVALID_HANDLE)) {
1031 struct server_id server = e->server;
1033 DEBUG(10, ("Deleting notify entries for "
1034 "process %s because it's gone\n",
1035 procid_str_static(&e->server)));
1036 notify_remove_all(notify, &server);
1037 goto again;