s3: Support shadow copy display over SMB2
[Samba.git] / source3 / smbd / notify_internal.c
blob30985022182c93bb00e64b12ff68e5b41d8a1843
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"
33 #include "util_tdb.h"
35 struct notify_context {
36 struct db_context *db_recursive;
37 struct db_context *db_onelevel;
38 struct server_id server;
39 struct messaging_context *messaging_ctx;
40 struct notify_list *list;
41 struct notify_array *array;
42 int seqnum;
43 struct sys_notify_context *sys_notify_ctx;
44 TDB_DATA key;
48 struct notify_list {
49 struct notify_list *next, *prev;
50 void *private_data;
51 void (*callback)(void *, const struct notify_event *);
52 void *sys_notify_handle;
53 int depth;
56 #define NOTIFY_KEY "notify array"
58 #define NOTIFY_ENABLE "notify:enable"
59 #define NOTIFY_ENABLE_DEFAULT True
61 static NTSTATUS notify_remove_all(struct notify_context *notify,
62 const struct server_id *server);
63 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
64 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
67 destroy the notify context
69 static int notify_destructor(struct notify_context *notify)
71 messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
73 if (notify->list != NULL) {
74 notify_remove_all(notify, &notify->server);
77 return 0;
81 Open up the notify.tdb database. You should close it down using
82 talloc_free(). We need the messaging_ctx to allow for notifications
83 via internal messages
85 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
86 struct messaging_context *messaging_ctx,
87 struct event_context *ev,
88 connection_struct *conn)
90 struct notify_context *notify;
92 if (!lp_change_notify(conn->params)) {
93 return NULL;
96 notify = talloc(mem_ctx, struct notify_context);
97 if (notify == NULL) {
98 return NULL;
101 notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
102 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
103 O_RDWR|O_CREAT, 0644);
104 if (notify->db_recursive == NULL) {
105 talloc_free(notify);
106 return NULL;
109 notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
110 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
111 O_RDWR|O_CREAT, 0644);
112 if (notify->db_onelevel == NULL) {
113 talloc_free(notify);
114 return NULL;
117 notify->server = server;
118 notify->messaging_ctx = messaging_ctx;
119 notify->list = NULL;
120 notify->array = NULL;
121 notify->seqnum = notify->db_recursive->get_seqnum(
122 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 = notify->db_recursive->fetch_locked(notify->db_recursive,
175 notify, notify->key);
176 if (*rec == NULL) {
177 return NT_STATUS_INTERNAL_DB_CORRUPTION;
179 return NT_STATUS_OK;
183 load the notify array
185 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
187 TDB_DATA dbuf;
188 DATA_BLOB blob;
189 NTSTATUS status;
190 int seqnum;
192 seqnum = notify->db_recursive->get_seqnum(notify->db_recursive);
194 if (seqnum == notify->seqnum && notify->array != NULL) {
195 return NT_STATUS_OK;
198 notify->seqnum = seqnum;
200 talloc_free(notify->array);
201 notify->array = TALLOC_ZERO_P(notify, struct notify_array);
202 NT_STATUS_HAVE_NO_MEMORY(notify->array);
204 if (!rec) {
205 if (notify->db_recursive->fetch(notify->db_recursive, notify,
206 notify->key, &dbuf) != 0) {
207 return NT_STATUS_INTERNAL_DB_CORRUPTION;
209 } else {
210 dbuf = rec->value;
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 rec->delete_rec(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 rec->delete_rec(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 = rec->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 enum ndr_err_code ndr_err;
415 NTSTATUS status;
417 array = talloc_zero(talloc_tos(), struct notify_entry_array);
418 if (array == NULL) {
419 return;
422 rec = notify->db_onelevel->fetch_locked(
423 notify->db_onelevel, talloc_tos(),
424 make_tdb_data((uint8_t *)&e->dir_id, sizeof(e->dir_id)));
425 if (rec == NULL) {
426 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
427 "\n", file_id_string_tos(&e->dir_id)));
428 TALLOC_FREE(array);
429 return;
432 blob.data = (uint8_t *)rec->value.dptr;
433 blob.length = rec->value.dsize;
435 if (blob.length > 0) {
436 ndr_err = ndr_pull_struct_blob(&blob, array, array,
437 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
438 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
439 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
440 ndr_errstr(ndr_err)));
441 TALLOC_FREE(array);
442 return;
444 if (DEBUGLEVEL >= 10) {
445 DEBUG(10, ("notify_add_onelevel:\n"));
446 NDR_PRINT_DEBUG(notify_entry_array, array);
450 array->entries = talloc_realloc(array, array->entries,
451 struct notify_entry,
452 array->num_entries+1);
453 if (array->entries == NULL) {
454 TALLOC_FREE(array);
455 return;
457 array->entries[array->num_entries] = *e;
458 array->entries[array->num_entries].private_data = private_data;
459 array->entries[array->num_entries].server = notify->server;
460 array->num_entries += 1;
462 ndr_err = ndr_push_struct_blob(&blob, rec, array,
463 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
464 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
465 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
466 ndr_errstr(ndr_err)));
467 TALLOC_FREE(array);
468 return;
471 if (DEBUGLEVEL >= 10) {
472 DEBUG(10, ("notify_add_onelevel:\n"));
473 NDR_PRINT_DEBUG(notify_entry_array, array);
476 dbuf.dptr = blob.data;
477 dbuf.dsize = blob.length;
479 status = rec->store(rec, dbuf, TDB_REPLACE);
480 TALLOC_FREE(array);
481 if (!NT_STATUS_IS_OK(status)) {
482 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
483 nt_errstr(status)));
484 return;
486 e->filter = 0;
487 return;
492 add a notify watch. This is called when a notify is first setup on a open
493 directory handle.
495 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
496 void (*callback)(void *, const struct notify_event *),
497 void *private_data)
499 struct notify_entry e = *e0;
500 NTSTATUS status;
501 char *tmp_path = NULL;
502 struct notify_list *listel;
503 size_t len;
504 int depth;
505 struct db_record *rec;
507 /* see if change notify is enabled at all */
508 if (notify == NULL) {
509 return NT_STATUS_NOT_IMPLEMENTED;
512 status = notify_fetch_locked(notify, &rec);
513 NT_STATUS_NOT_OK_RETURN(status);
515 status = notify_load(notify, rec);
516 if (!NT_STATUS_IS_OK(status)) {
517 talloc_free(rec);
518 return status;
521 /* cope with /. on the end of the path */
522 len = strlen(e.path);
523 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
524 tmp_path = talloc_strndup(notify, e.path, len-2);
525 if (tmp_path == NULL) {
526 status = NT_STATUS_NO_MEMORY;
527 goto done;
529 e.path = tmp_path;
532 depth = count_chars(e.path, '/');
534 listel = TALLOC_ZERO_P(notify, struct notify_list);
535 if (listel == NULL) {
536 status = NT_STATUS_NO_MEMORY;
537 goto done;
540 listel->private_data = private_data;
541 listel->callback = callback;
542 listel->depth = depth;
543 DLIST_ADD(notify->list, listel);
545 /* ignore failures from sys_notify */
546 if (notify->sys_notify_ctx != NULL) {
548 this call will modify e.filter and e.subdir_filter
549 to remove bits handled by the backend
551 status = sys_notify_watch(notify->sys_notify_ctx, &e,
552 sys_notify_callback, listel,
553 &listel->sys_notify_handle);
554 if (NT_STATUS_IS_OK(status)) {
555 talloc_steal(listel, listel->sys_notify_handle);
559 if (e.filter != 0) {
560 notify_add_onelevel(notify, &e, private_data);
561 status = NT_STATUS_OK;
564 /* if the system notify handler couldn't handle some of the
565 filter bits, or couldn't handle a request for recursion
566 then we need to install it in the array used for the
567 intra-samba notify handling */
568 if (e.filter != 0 || e.subdir_filter != 0) {
569 status = notify_add_array(notify, rec, &e, private_data, depth);
572 done:
573 talloc_free(rec);
574 talloc_free(tmp_path);
576 return status;
579 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
580 const struct file_id *fid,
581 void *private_data)
583 struct notify_entry_array *array;
584 struct db_record *rec;
585 DATA_BLOB blob;
586 TDB_DATA dbuf;
587 enum ndr_err_code ndr_err;
588 NTSTATUS status;
589 int i;
591 if (notify == NULL) {
592 return NT_STATUS_NOT_IMPLEMENTED;
595 array = talloc_zero(talloc_tos(), struct notify_entry_array);
596 if (array == NULL) {
597 return NT_STATUS_NO_MEMORY;
600 rec = notify->db_onelevel->fetch_locked(
601 notify->db_onelevel, array,
602 make_tdb_data((const uint8_t *)fid, sizeof(*fid)));
603 if (rec == NULL) {
604 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
605 "\n", file_id_string_tos(fid)));
606 TALLOC_FREE(array);
607 return NT_STATUS_INTERNAL_DB_CORRUPTION;
610 blob.data = (uint8_t *)rec->value.dptr;
611 blob.length = rec->value.dsize;
613 if (blob.length > 0) {
614 ndr_err = ndr_pull_struct_blob(&blob, array, array,
615 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
616 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
617 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
618 ndr_errstr(ndr_err)));
619 TALLOC_FREE(array);
620 return ndr_map_error2ntstatus(ndr_err);
622 if (DEBUGLEVEL >= 10) {
623 DEBUG(10, ("notify_remove_onelevel:\n"));
624 NDR_PRINT_DEBUG(notify_entry_array, array);
628 for (i=0; i<array->num_entries; i++) {
629 if ((private_data == array->entries[i].private_data) &&
630 cluster_id_equal(&notify->server,
631 &array->entries[i].server)) {
632 break;
636 if (i == array->num_entries) {
637 TALLOC_FREE(array);
638 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
641 array->entries[i] = array->entries[array->num_entries-1];
642 array->num_entries -= 1;
644 if (array->num_entries == 0) {
645 rec->delete_rec(rec);
646 TALLOC_FREE(array);
647 return NT_STATUS_OK;
650 ndr_err = ndr_push_struct_blob(&blob, rec, array,
651 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
652 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
653 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
654 ndr_errstr(ndr_err)));
655 TALLOC_FREE(array);
656 return ndr_map_error2ntstatus(ndr_err);
659 if (DEBUGLEVEL >= 10) {
660 DEBUG(10, ("notify_add_onelevel:\n"));
661 NDR_PRINT_DEBUG(notify_entry_array, array);
664 dbuf.dptr = blob.data;
665 dbuf.dsize = blob.length;
667 status = rec->store(rec, dbuf, TDB_REPLACE);
668 TALLOC_FREE(array);
669 if (!NT_STATUS_IS_OK(status)) {
670 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
671 nt_errstr(status)));
672 return status;
674 return NT_STATUS_OK;
678 remove a notify watch. Called when the directory handle is closed
680 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
682 NTSTATUS status;
683 struct notify_list *listel;
684 int i, depth;
685 struct notify_depth *d;
686 struct db_record *rec;
688 /* see if change notify is enabled at all */
689 if (notify == NULL) {
690 return NT_STATUS_NOT_IMPLEMENTED;
693 for (listel=notify->list;listel;listel=listel->next) {
694 if (listel->private_data == private_data) {
695 DLIST_REMOVE(notify->list, listel);
696 break;
699 if (listel == NULL) {
700 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
703 depth = listel->depth;
705 talloc_free(listel);
707 status = notify_fetch_locked(notify, &rec);
708 NT_STATUS_NOT_OK_RETURN(status);
710 status = notify_load(notify, rec);
711 if (!NT_STATUS_IS_OK(status)) {
712 talloc_free(rec);
713 return status;
716 if (depth >= notify->array->num_depths) {
717 talloc_free(rec);
718 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
721 /* we only have to search at the depth of this element */
722 d = &notify->array->depth[depth];
724 for (i=0;i<d->num_entries;i++) {
725 if (private_data == d->entries[i].private_data &&
726 cluster_id_equal(&notify->server, &d->entries[i].server)) {
727 break;
730 if (i == d->num_entries) {
731 talloc_free(rec);
732 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
735 if (i < d->num_entries-1) {
736 memmove(&d->entries[i], &d->entries[i+1],
737 sizeof(d->entries[i])*(d->num_entries-(i+1)));
739 d->num_entries--;
741 status = notify_save(notify, rec);
743 talloc_free(rec);
745 return status;
749 remove all notify watches for a messaging server
751 static NTSTATUS notify_remove_all(struct notify_context *notify,
752 const struct server_id *server)
754 NTSTATUS status;
755 int i, depth, del_count=0;
756 struct db_record *rec;
758 status = notify_fetch_locked(notify, &rec);
759 NT_STATUS_NOT_OK_RETURN(status);
761 status = notify_load(notify, rec);
762 if (!NT_STATUS_IS_OK(status)) {
763 talloc_free(rec);
764 return status;
767 /* we have to search for all entries across all depths, looking for matches
768 for the server id */
769 for (depth=0;depth<notify->array->num_depths;depth++) {
770 struct notify_depth *d = &notify->array->depth[depth];
771 for (i=0;i<d->num_entries;i++) {
772 if (cluster_id_equal(server, &d->entries[i].server)) {
773 if (i < d->num_entries-1) {
774 memmove(&d->entries[i], &d->entries[i+1],
775 sizeof(d->entries[i])*(d->num_entries-(i+1)));
777 i--;
778 d->num_entries--;
779 del_count++;
784 if (del_count > 0) {
785 status = notify_save(notify, rec);
788 talloc_free(rec);
790 return status;
795 send a notify message to another messaging server
797 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
798 const char *path, uint32_t action)
800 struct notify_event ev;
801 DATA_BLOB data;
802 NTSTATUS status;
803 enum ndr_err_code ndr_err;
804 TALLOC_CTX *tmp_ctx;
806 ev.action = action;
807 ev.path = path;
808 ev.private_data = e->private_data;
810 tmp_ctx = talloc_new(notify);
812 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
813 (ndr_push_flags_fn_t)ndr_push_notify_event);
814 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
815 talloc_free(tmp_ctx);
816 return ndr_map_error2ntstatus(ndr_err);
819 status = messaging_send(notify->messaging_ctx, e->server,
820 MSG_PVFS_NOTIFY, &data);
821 talloc_free(tmp_ctx);
822 return status;
825 void notify_onelevel(struct notify_context *notify, uint32_t action,
826 uint32_t filter, struct file_id fid, const char *name)
828 struct notify_entry_array *array;
829 TDB_DATA dbuf;
830 DATA_BLOB blob;
831 bool have_dead_entries = false;
832 int i;
834 if (notify == NULL) {
835 return;
838 array = talloc_zero(talloc_tos(), struct notify_entry_array);
839 if (array == NULL) {
840 return;
843 if (notify->db_onelevel->fetch(
844 notify->db_onelevel, array,
845 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
846 &dbuf) == -1) {
847 TALLOC_FREE(array);
848 return;
851 blob.data = (uint8 *)dbuf.dptr;
852 blob.length = dbuf.dsize;
854 if (blob.length > 0) {
855 enum ndr_err_code ndr_err;
856 ndr_err = ndr_pull_struct_blob(&blob, array, array,
857 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
858 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
859 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
860 ndr_errstr(ndr_err)));
861 TALLOC_FREE(array);
862 return;
864 if (DEBUGLEVEL >= 10) {
865 DEBUG(10, ("notify_onelevel:\n"));
866 NDR_PRINT_DEBUG(notify_entry_array, array);
870 for (i=0; i<array->num_entries; i++) {
871 struct notify_entry *e = &array->entries[i];
873 if ((e->filter & filter) != 0) {
874 NTSTATUS status;
876 status = notify_send(notify, e, name, action);
877 if (NT_STATUS_EQUAL(
878 status, NT_STATUS_INVALID_HANDLE)) {
880 * Mark the entry as dead. All entries have a
881 * path set. The marker used here is setting
882 * that to NULL.
884 e->path = NULL;
885 have_dead_entries = true;
890 if (!have_dead_entries) {
891 TALLOC_FREE(array);
892 return;
895 for (i=0; i<array->num_entries; i++) {
896 struct notify_entry *e = &array->entries[i];
897 if (e->path != NULL) {
898 continue;
900 DEBUG(10, ("Deleting notify entries for process %s because "
901 "it's gone\n", procid_str_static(&e->server)));
903 * Potential TODO: This might need optimizing,
904 * notify_remove_onelevel() does a fetch_locked() operation at
905 * every call. But this would only matter if a process with
906 * MANY notifies has died without shutting down properly.
908 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
911 TALLOC_FREE(array);
912 return;
916 trigger a notify message for anyone waiting on a matching event
918 This function is called a lot, and needs to be very fast. The unusual data structure
919 and traversal is designed to be fast in the average case, even for large numbers of
920 notifies
922 void notify_trigger(struct notify_context *notify,
923 uint32_t action, uint32_t filter, const char *path)
925 NTSTATUS status;
926 int depth;
927 const char *p, *next_p;
929 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
930 "path=%s\n", (unsigned)action, (unsigned)filter, path));
932 /* see if change notify is enabled at all */
933 if (notify == NULL) {
934 return;
937 again:
938 status = notify_load(notify, NULL);
939 if (!NT_STATUS_IS_OK(status)) {
940 return;
943 /* loop along the given path, working with each directory depth separately */
944 for (depth=0,p=path;
945 p && depth < notify->array->num_depths;
946 p=next_p,depth++) {
947 int p_len = p - path;
948 int min_i, max_i, i;
949 struct notify_depth *d = &notify->array->depth[depth];
950 next_p = strchr(p+1, '/');
952 /* see if there are any entries at this depth */
953 if (d->num_entries == 0) continue;
955 /* try to skip based on the maximum mask. If next_p is
956 NULL then we know it will be a 'this directory'
957 match, otherwise it must be a subdir match */
958 if (next_p != NULL) {
959 if (0 == (filter & d->max_mask_subdir)) {
960 continue;
962 } else {
963 if (0 == (filter & d->max_mask)) {
964 continue;
968 /* we know there is an entry here worth looking
969 for. Use a bisection search to find the first entry
970 with a matching path */
971 min_i = 0;
972 max_i = d->num_entries-1;
974 while (min_i < max_i) {
975 struct notify_entry *e;
976 int cmp;
977 i = (min_i+max_i)/2;
978 e = &d->entries[i];
979 cmp = strncmp(path, e->path, p_len);
980 if (cmp == 0) {
981 if (p_len == e->path_len) {
982 max_i = i;
983 } else {
984 max_i = i-1;
986 } else if (cmp < 0) {
987 max_i = i-1;
988 } else {
989 min_i = i+1;
993 if (min_i != max_i) {
994 /* none match */
995 continue;
998 /* we now know that the entries start at min_i */
999 for (i=min_i;i<d->num_entries;i++) {
1000 struct notify_entry *e = &d->entries[i];
1001 if (p_len != e->path_len ||
1002 strncmp(path, e->path, p_len) != 0) break;
1003 if (next_p != NULL) {
1004 if (0 == (filter & e->subdir_filter)) {
1005 continue;
1007 } else {
1008 if (0 == (filter & e->filter)) {
1009 continue;
1012 status = notify_send(notify, e, path + e->path_len + 1,
1013 action);
1015 if (NT_STATUS_EQUAL(
1016 status, NT_STATUS_INVALID_HANDLE)) {
1017 struct server_id server = e->server;
1019 DEBUG(10, ("Deleting notify entries for "
1020 "process %s because it's gone\n",
1021 procid_str_static(&e->server)));
1022 notify_remove_all(notify, &server);
1023 goto again;