s3: Pass "path" through vfs_notify_watch
[Samba/gebeck_regimport.git] / source3 / smbd / notify_internal.c
blobb12459c9d3359dde7331dc026f628951e4ba1859
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/tdb_wrap/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,
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 = messaging_server_id(messaging_ctx);
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, array,
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 struct notify_list *listel;
515 int depth;
517 /* see if change notify is enabled at all */
518 if (notify == NULL) {
519 return NT_STATUS_NOT_IMPLEMENTED;
522 depth = count_chars(e.path, '/');
524 listel = talloc_zero(notify, struct notify_list);
525 if (listel == NULL) {
526 status = NT_STATUS_NO_MEMORY;
527 goto done;
530 listel->private_data = private_data;
531 listel->callback = callback;
532 listel->depth = depth;
533 DLIST_ADD(notify->list, listel);
535 /* ignore failures from sys_notify */
536 if (notify->sys_notify_ctx != NULL) {
538 this call will modify e.filter and e.subdir_filter
539 to remove bits handled by the backend
541 status = sys_notify_watch(notify->sys_notify_ctx, &e, e.path,
542 sys_notify_callback, listel,
543 &listel->sys_notify_handle);
544 if (NT_STATUS_IS_OK(status)) {
545 talloc_steal(listel, listel->sys_notify_handle);
549 if (e.filter != 0) {
550 notify_add_onelevel(notify, &e, private_data);
551 status = NT_STATUS_OK;
554 /* if the system notify handler couldn't handle some of the
555 filter bits, or couldn't handle a request for recursion
556 then we need to install it in the array used for the
557 intra-samba notify handling */
558 if (e.filter != 0 || e.subdir_filter != 0) {
559 struct db_record *rec;
561 status = notify_fetch_locked(notify, &rec);
562 if (!NT_STATUS_IS_OK(status)) {
563 goto done;
565 status = notify_load(notify, rec);
566 if (!NT_STATUS_IS_OK(status)) {
567 TALLOC_FREE(rec);
568 goto done;
570 status = notify_add_array(notify, rec, &e, private_data,
571 depth);
572 TALLOC_FREE(rec);
574 status = NT_STATUS_OK;
575 done:
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 TDB_DATA value;
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 = dbwrap_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 value = dbwrap_record_get_value(rec);
612 blob.data = (uint8_t *)value.dptr;
613 blob.length = value.dsize;
615 if (blob.length > 0) {
616 ndr_err = ndr_pull_struct_blob(&blob, array, array,
617 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
618 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
619 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
620 ndr_errstr(ndr_err)));
621 TALLOC_FREE(array);
622 return ndr_map_error2ntstatus(ndr_err);
624 if (DEBUGLEVEL >= 10) {
625 DEBUG(10, ("notify_remove_onelevel:\n"));
626 NDR_PRINT_DEBUG(notify_entry_array, array);
630 for (i=0; i<array->num_entries; i++) {
631 if ((private_data == array->entries[i].private_data) &&
632 procid_equal(&notify->server, &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 dbwrap_record_delete(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 = dbwrap_record_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 procid_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 (procid_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;
806 ev.action = action;
807 ev.path = path;
808 ev.private_data = e->private_data;
810 ndr_err = ndr_push_struct_blob(&data, talloc_tos(), &ev,
811 (ndr_push_flags_fn_t)ndr_push_notify_event);
812 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
813 return ndr_map_error2ntstatus(ndr_err);
816 status = messaging_send(notify->messaging_ctx, e->server,
817 MSG_PVFS_NOTIFY, &data);
818 TALLOC_FREE(data.data);
819 return status;
822 void notify_onelevel(struct notify_context *notify, uint32_t action,
823 uint32_t filter, struct file_id fid, const char *name)
825 struct notify_entry_array *array;
826 TDB_DATA dbuf;
827 DATA_BLOB blob;
828 bool have_dead_entries = false;
829 int i;
830 NTSTATUS status;
832 if (notify == NULL) {
833 return;
836 array = talloc_zero(talloc_tos(), struct notify_entry_array);
837 if (array == NULL) {
838 return;
841 status = dbwrap_fetch(notify->db_onelevel, array,
842 make_tdb_data((uint8_t *)&fid, sizeof(fid)),
843 &dbuf);
844 if (!NT_STATUS_IS_OK(status)) {
845 TALLOC_FREE(array);
846 return;
849 blob.data = (uint8 *)dbuf.dptr;
850 blob.length = dbuf.dsize;
852 if (blob.length > 0) {
853 enum ndr_err_code ndr_err;
854 ndr_err = ndr_pull_struct_blob(&blob, array, array,
855 (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
856 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
857 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
858 ndr_errstr(ndr_err)));
859 TALLOC_FREE(array);
860 return;
862 if (DEBUGLEVEL >= 10) {
863 DEBUG(10, ("notify_onelevel:\n"));
864 NDR_PRINT_DEBUG(notify_entry_array, array);
868 for (i=0; i<array->num_entries; i++) {
869 struct notify_entry *e = &array->entries[i];
871 if ((e->filter & filter) != 0) {
872 status = notify_send(notify, e, name, action);
873 if (NT_STATUS_EQUAL(
874 status, NT_STATUS_INVALID_HANDLE)) {
876 * Mark the entry as dead. All entries have a
877 * path set. The marker used here is setting
878 * that to NULL.
880 e->path = NULL;
881 have_dead_entries = true;
886 if (!have_dead_entries) {
887 TALLOC_FREE(array);
888 return;
891 for (i=0; i<array->num_entries; i++) {
892 struct notify_entry *e = &array->entries[i];
893 if (e->path != NULL) {
894 continue;
896 DEBUG(10, ("Deleting notify entries for process %s because "
897 "it's gone\n", procid_str_static(&e->server)));
899 * Potential TODO: This might need optimizing,
900 * notify_remove_onelevel() does a fetch_locked() operation at
901 * every call. But this would only matter if a process with
902 * MANY notifies has died without shutting down properly.
904 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
907 TALLOC_FREE(array);
908 return;
912 trigger a notify message for anyone waiting on a matching event
914 This function is called a lot, and needs to be very fast. The unusual data structure
915 and traversal is designed to be fast in the average case, even for large numbers of
916 notifies
918 void notify_trigger(struct notify_context *notify,
919 uint32_t action, uint32_t filter, const char *path)
921 NTSTATUS status;
922 int depth;
923 const char *p, *next_p;
925 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
926 "path=%s\n", (unsigned)action, (unsigned)filter, path));
928 /* see if change notify is enabled at all */
929 if (notify == NULL) {
930 return;
933 again:
934 status = notify_load(notify, NULL);
935 if (!NT_STATUS_IS_OK(status)) {
936 return;
939 /* loop along the given path, working with each directory depth separately */
940 for (depth=0,p=path;
941 p && depth < notify->array->num_depths;
942 p=next_p,depth++) {
943 int p_len = p - path;
944 int min_i, max_i, i;
945 struct notify_depth *d = &notify->array->depth[depth];
946 uint32_t d_max_mask;
947 next_p = strchr(p+1, '/');
949 /* see if there are any entries at this depth */
950 if (d->num_entries == 0) continue;
952 /* try to skip based on the maximum mask. If next_p is
953 NULL then we know it will be a 'this directory'
954 match, otherwise it must be a subdir match */
956 d_max_mask = next_p ? d->max_mask_subdir : d->max_mask;
958 if ((filter & d_max_mask) == 0) {
959 continue;
962 /* we know there is an entry here worth looking
963 for. Use a bisection search to find the first entry
964 with a matching path */
965 min_i = 0;
966 max_i = d->num_entries-1;
968 while (min_i < max_i) {
969 struct notify_entry *e;
970 int cmp;
971 i = (min_i+max_i)/2;
972 e = &d->entries[i];
973 cmp = strncmp(path, e->path, p_len);
974 if (cmp == 0) {
975 if (p_len == e->path_len) {
976 max_i = i;
977 } else {
978 max_i = i-1;
980 } else if (cmp < 0) {
981 max_i = i-1;
982 } else {
983 min_i = i+1;
987 if (min_i != max_i) {
988 /* none match */
989 continue;
992 /* we now know that the entries start at min_i */
993 for (i=min_i;i<d->num_entries;i++) {
994 struct notify_entry *e = &d->entries[i];
995 uint32_t e_filter;
996 if (p_len != e->path_len ||
997 strncmp(path, e->path, p_len) != 0) break;
999 e_filter = next_p ? e->subdir_filter : e->filter;
1001 if ((filter & e_filter) == 0) {
1002 continue;
1005 status = notify_send(notify, e, path + e->path_len + 1,
1006 action);
1008 if (NT_STATUS_EQUAL(
1009 status, NT_STATUS_INVALID_HANDLE)) {
1010 struct server_id server = e->server;
1012 DEBUG(10, ("Deleting notify entries for "
1013 "process %s because it's gone\n",
1014 procid_str_static(&e->server)));
1015 notify_remove_all(notify, &server);
1016 goto again;