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.
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"
33 #include "lib/tdb_wrap/tdb_wrap.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
;
45 struct sys_notify_context
*sys_notify_ctx
;
51 struct notify_list
*next
, *prev
;
53 void (*callback
)(void *, const struct notify_event
*);
54 void *sys_notify_handle
;
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
, ¬ify
->server
);
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
87 struct notify_context
*notify_init(TALLOC_CTX
*mem_ctx
,
88 struct messaging_context
*messaging_ctx
,
89 struct event_context
*ev
)
91 struct notify_context
*notify
;
93 notify
= talloc(mem_ctx
, struct notify_context
);
98 notify
->db_recursive
= db_open(notify
, lock_path("notify.tdb"),
99 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
,
100 O_RDWR
|O_CREAT
, 0644,
101 DBWRAP_LOCK_ORDER_2
);
102 if (notify
->db_recursive
== NULL
) {
107 notify
->db_onelevel
= db_open(notify
, lock_path("notify_onelevel.tdb"),
108 0, TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
,
109 O_RDWR
|O_CREAT
, 0644,
110 DBWRAP_LOCK_ORDER_2
);
111 if (notify
->db_onelevel
== NULL
) {
116 notify
->server
= messaging_server_id(messaging_ctx
);
117 notify
->messaging_ctx
= messaging_ctx
;
119 notify
->array
= NULL
;
120 notify
->seqnum
= dbwrap_get_seqnum(notify
->db_recursive
);
121 notify
->key
= string_term_tdb_data(NOTIFY_KEY
);
123 talloc_set_destructor(notify
, notify_destructor
);
125 /* register with the messaging subsystem for the notify
127 messaging_register(notify
->messaging_ctx
, notify
,
128 MSG_PVFS_NOTIFY
, notify_handler
);
130 notify
->sys_notify_ctx
= sys_notify_context_create(notify
, ev
);
135 bool notify_internal_parent_init(TALLOC_CTX
*mem_ctx
)
137 struct tdb_wrap
*db1
, *db2
;
138 struct loadparm_context
*lp_ctx
;
140 if (lp_clustering()) {
144 lp_ctx
= loadparm_init_s3(mem_ctx
, loadparm_s3_context());
145 if (lp_ctx
== NULL
) {
146 DEBUG(0, ("loadparm_init_s3 failed\n"));
150 * Open the tdbs in the parent process (smbd) so that our
151 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
155 db1
= tdb_wrap_open(mem_ctx
, lock_path("notify.tdb"),
156 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
,
157 O_RDWR
|O_CREAT
, 0644, lp_ctx
);
159 talloc_unlink(mem_ctx
, lp_ctx
);
160 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno
)));
163 db2
= tdb_wrap_open(mem_ctx
, lock_path("notify_onelevel.tdb"),
164 0, TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
, O_RDWR
|O_CREAT
, 0644, lp_ctx
);
165 talloc_unlink(mem_ctx
, lp_ctx
);
167 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
176 lock and fetch the record
178 static NTSTATUS
notify_fetch_locked(struct notify_context
*notify
, struct db_record
**rec
)
180 *rec
= dbwrap_fetch_locked(notify
->db_recursive
, notify
, notify
->key
);
182 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
188 load the notify array
190 static NTSTATUS
notify_load(struct notify_context
*notify
, struct db_record
*rec
)
197 seqnum
= dbwrap_get_seqnum(notify
->db_recursive
);
199 if (seqnum
== notify
->seqnum
&& notify
->array
!= NULL
) {
203 notify
->seqnum
= seqnum
;
205 talloc_free(notify
->array
);
206 notify
->array
= talloc_zero(notify
, struct notify_array
);
207 NT_STATUS_HAVE_NO_MEMORY(notify
->array
);
210 status
= dbwrap_fetch(notify
->db_recursive
, notify
,
212 if (!NT_STATUS_IS_OK(status
)) {
213 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
216 dbuf
= dbwrap_record_get_value(rec
);
219 blob
.data
= (uint8
*)dbuf
.dptr
;
220 blob
.length
= dbuf
.dsize
;
222 status
= NT_STATUS_OK
;
223 if (blob
.length
> 0) {
224 enum ndr_err_code ndr_err
;
225 ndr_err
= ndr_pull_struct_blob(&blob
, notify
->array
, notify
->array
,
226 (ndr_pull_flags_fn_t
)ndr_pull_notify_array
);
227 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
228 /* 1. log that we got a corrupt notify_array
229 * 2. clear the variable the garbage was stored into to not trip
230 * over it next time this method is entered with the same seqnum
231 * 3. delete it from the database */
232 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
234 ZERO_STRUCTP(notify
->array
);
236 dbwrap_record_delete(rec
);
240 if (DEBUGLEVEL
>= 10) {
241 DEBUG(10, ("notify_load:\n"));
242 NDR_PRINT_DEBUG(notify_array
, notify
->array
);
249 talloc_free(dbuf
.dptr
);
256 compare notify entries for sorting
258 static int notify_compare(const struct notify_entry
*e1
, const struct notify_entry
*e2
)
260 return strcmp(e1
->path
, e2
->path
);
264 save the notify array
266 static NTSTATUS
notify_save(struct notify_context
*notify
, struct db_record
*rec
)
271 enum ndr_err_code ndr_err
;
274 /* if possible, remove some depth arrays */
275 while (notify
->array
->num_depths
> 0 &&
276 notify
->array
->depth
[notify
->array
->num_depths
-1].num_entries
== 0) {
277 notify
->array
->num_depths
--;
280 /* we might just be able to delete the record */
281 if (notify
->array
->num_depths
== 0) {
282 return dbwrap_record_delete(rec
);
285 tmp_ctx
= talloc_new(notify
);
286 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx
);
288 ndr_err
= ndr_push_struct_blob(&blob
, tmp_ctx
, notify
->array
,
289 (ndr_push_flags_fn_t
)ndr_push_notify_array
);
290 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
291 talloc_free(tmp_ctx
);
292 return ndr_map_error2ntstatus(ndr_err
);
295 if (DEBUGLEVEL
>= 10) {
296 DEBUG(10, ("notify_save:\n"));
297 NDR_PRINT_DEBUG(notify_array
, notify
->array
);
300 dbuf
.dptr
= blob
.data
;
301 dbuf
.dsize
= blob
.length
;
303 status
= dbwrap_record_store(rec
, dbuf
, TDB_REPLACE
);
304 talloc_free(tmp_ctx
);
311 handle incoming notify messages
313 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
314 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
)
316 struct notify_context
*notify
= talloc_get_type(private_data
, struct notify_context
);
317 enum ndr_err_code ndr_err
;
318 struct notify_event ev
;
319 TALLOC_CTX
*tmp_ctx
= talloc_new(notify
);
320 struct notify_list
*listel
;
322 if (tmp_ctx
== NULL
) {
326 ndr_err
= ndr_pull_struct_blob(data
, tmp_ctx
, &ev
,
327 (ndr_pull_flags_fn_t
)ndr_pull_notify_event
);
328 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
329 talloc_free(tmp_ctx
);
333 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
334 if (listel
->private_data
== ev
.private_data
) {
335 listel
->callback(listel
->private_data
, &ev
);
340 talloc_free(tmp_ctx
);
344 callback from sys_notify telling us about changes from the OS
346 static void sys_notify_callback(struct sys_notify_context
*ctx
,
347 void *ptr
, struct notify_event
*ev
)
349 struct notify_list
*listel
= talloc_get_type(ptr
, struct notify_list
);
350 ev
->private_data
= listel
;
351 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
352 ev
->action
, ev
->path
));
353 listel
->callback(listel
->private_data
, ev
);
357 add an entry to the notify array
359 static NTSTATUS
notify_add_array(struct notify_context
*notify
, struct db_record
*rec
,
360 struct notify_entry
*e
,
361 void *private_data
, int depth
)
364 struct notify_depth
*d
;
365 struct notify_entry
*ee
;
367 /* possibly expand the depths array */
368 if (depth
>= notify
->array
->num_depths
) {
369 d
= talloc_realloc(notify
->array
, notify
->array
->depth
,
370 struct notify_depth
, depth
+1);
371 NT_STATUS_HAVE_NO_MEMORY(d
);
372 for (i
=notify
->array
->num_depths
;i
<=depth
;i
++) {
375 notify
->array
->depth
= d
;
376 notify
->array
->num_depths
= depth
+1;
378 d
= ¬ify
->array
->depth
[depth
];
380 /* expand the entries array */
381 ee
= talloc_realloc(notify
->array
->depth
, d
->entries
, struct notify_entry
,
383 NT_STATUS_HAVE_NO_MEMORY(ee
);
386 d
->entries
[d
->num_entries
] = *e
;
387 d
->entries
[d
->num_entries
].private_data
= private_data
;
388 d
->entries
[d
->num_entries
].server
= notify
->server
;
389 d
->entries
[d
->num_entries
].path_len
= strlen(e
->path
);
392 d
->max_mask
|= e
->filter
;
393 d
->max_mask_subdir
|= e
->subdir_filter
;
395 TYPESAFE_QSORT(d
->entries
, d
->num_entries
, notify_compare
);
397 /* recalculate the maximum masks */
399 d
->max_mask_subdir
= 0;
401 for (i
=0;i
<d
->num_entries
;i
++) {
402 d
->max_mask
|= d
->entries
[i
].filter
;
403 d
->max_mask_subdir
|= d
->entries
[i
].subdir_filter
;
406 return notify_save(notify
, rec
);
410 Add a non-recursive watch
413 static void notify_add_onelevel(struct notify_context
*notify
,
414 struct notify_entry
*e
, void *private_data
)
416 struct notify_entry_array
*array
;
417 struct db_record
*rec
;
421 enum ndr_err_code ndr_err
;
424 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
429 rec
= dbwrap_fetch_locked(notify
->db_onelevel
, array
,
430 make_tdb_data((uint8_t *)&e
->dir_id
,
433 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
434 "\n", file_id_string_tos(&e
->dir_id
)));
439 value
= dbwrap_record_get_value(rec
);
440 blob
.data
= (uint8_t *)value
.dptr
;
441 blob
.length
= value
.dsize
;
443 if (blob
.length
> 0) {
444 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
445 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
446 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
447 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
448 ndr_errstr(ndr_err
)));
452 if (DEBUGLEVEL
>= 10) {
453 DEBUG(10, ("notify_add_onelevel:\n"));
454 NDR_PRINT_DEBUG(notify_entry_array
, array
);
458 array
->entries
= talloc_realloc(array
, array
->entries
,
460 array
->num_entries
+1);
461 if (array
->entries
== NULL
) {
465 array
->entries
[array
->num_entries
] = *e
;
466 array
->entries
[array
->num_entries
].private_data
= private_data
;
467 array
->entries
[array
->num_entries
].server
= notify
->server
;
468 array
->num_entries
+= 1;
470 ndr_err
= ndr_push_struct_blob(&blob
, rec
, array
,
471 (ndr_push_flags_fn_t
)ndr_push_notify_entry_array
);
472 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
473 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
474 ndr_errstr(ndr_err
)));
479 if (DEBUGLEVEL
>= 10) {
480 DEBUG(10, ("notify_add_onelevel:\n"));
481 NDR_PRINT_DEBUG(notify_entry_array
, array
);
484 dbuf
.dptr
= blob
.data
;
485 dbuf
.dsize
= blob
.length
;
487 status
= dbwrap_record_store(rec
, dbuf
, TDB_REPLACE
);
489 if (!NT_STATUS_IS_OK(status
)) {
490 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
500 add a notify watch. This is called when a notify is first setup on a open
503 NTSTATUS
notify_add(struct notify_context
*notify
, connection_struct
*conn
,
504 struct notify_entry
*e0
,
505 void (*callback
)(void *, const struct notify_event
*),
508 struct notify_entry e
= *e0
;
510 struct notify_list
*listel
;
513 /* see if change notify is enabled at all */
514 if (notify
== NULL
) {
515 return NT_STATUS_NOT_IMPLEMENTED
;
518 depth
= count_chars(e
.path
, '/');
520 listel
= talloc_zero(notify
, struct notify_list
);
521 if (listel
== NULL
) {
522 status
= NT_STATUS_NO_MEMORY
;
526 listel
->private_data
= private_data
;
527 listel
->callback
= callback
;
528 listel
->depth
= depth
;
529 DLIST_ADD(notify
->list
, listel
);
531 /* ignore failures from sys_notify */
532 if (notify
->sys_notify_ctx
!= NULL
) {
534 this call will modify e.filter and e.subdir_filter
535 to remove bits handled by the backend
537 status
= sys_notify_watch(notify
->sys_notify_ctx
, conn
,
539 sys_notify_callback
, listel
,
540 &listel
->sys_notify_handle
);
541 if (NT_STATUS_IS_OK(status
)) {
542 talloc_steal(listel
, listel
->sys_notify_handle
);
547 notify_add_onelevel(notify
, &e
, private_data
);
548 status
= NT_STATUS_OK
;
551 /* if the system notify handler couldn't handle some of the
552 filter bits, or couldn't handle a request for recursion
553 then we need to install it in the array used for the
554 intra-samba notify handling */
555 if (e
.filter
!= 0 || e
.subdir_filter
!= 0) {
556 struct db_record
*rec
;
558 status
= notify_fetch_locked(notify
, &rec
);
559 if (!NT_STATUS_IS_OK(status
)) {
562 status
= notify_load(notify
, rec
);
563 if (!NT_STATUS_IS_OK(status
)) {
567 status
= notify_add_array(notify
, rec
, &e
, private_data
,
571 status
= NT_STATUS_OK
;
576 NTSTATUS
notify_remove_onelevel(struct notify_context
*notify
,
577 const struct file_id
*fid
,
580 struct notify_entry_array
*array
;
581 struct db_record
*rec
;
585 enum ndr_err_code ndr_err
;
589 if (notify
== NULL
) {
590 return NT_STATUS_NOT_IMPLEMENTED
;
593 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
595 return NT_STATUS_NO_MEMORY
;
598 rec
= dbwrap_fetch_locked(
599 notify
->db_onelevel
, array
,
600 make_tdb_data((const uint8_t *)fid
, sizeof(*fid
)));
602 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
603 "\n", file_id_string_tos(fid
)));
605 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
608 value
= dbwrap_record_get_value(rec
);
609 blob
.data
= (uint8_t *)value
.dptr
;
610 blob
.length
= value
.dsize
;
612 if (blob
.length
> 0) {
613 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
614 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
615 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
616 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
617 ndr_errstr(ndr_err
)));
619 return ndr_map_error2ntstatus(ndr_err
);
621 if (DEBUGLEVEL
>= 10) {
622 DEBUG(10, ("notify_remove_onelevel:\n"));
623 NDR_PRINT_DEBUG(notify_entry_array
, array
);
627 for (i
=0; i
<array
->num_entries
; i
++) {
628 if ((private_data
== array
->entries
[i
].private_data
) &&
629 procid_equal(¬ify
->server
, &array
->entries
[i
].server
)) {
634 if (i
== array
->num_entries
) {
636 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
639 array
->entries
[i
] = array
->entries
[array
->num_entries
-1];
640 array
->num_entries
-= 1;
642 if (array
->num_entries
== 0) {
643 dbwrap_record_delete(rec
);
648 ndr_err
= ndr_push_struct_blob(&blob
, rec
, array
,
649 (ndr_push_flags_fn_t
)ndr_push_notify_entry_array
);
650 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
651 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
652 ndr_errstr(ndr_err
)));
654 return ndr_map_error2ntstatus(ndr_err
);
657 if (DEBUGLEVEL
>= 10) {
658 DEBUG(10, ("notify_add_onelevel:\n"));
659 NDR_PRINT_DEBUG(notify_entry_array
, array
);
662 dbuf
.dptr
= blob
.data
;
663 dbuf
.dsize
= blob
.length
;
665 status
= dbwrap_record_store(rec
, dbuf
, TDB_REPLACE
);
667 if (!NT_STATUS_IS_OK(status
)) {
668 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
676 remove a notify watch. Called when the directory handle is closed
678 NTSTATUS
notify_remove(struct notify_context
*notify
, void *private_data
)
681 struct notify_list
*listel
;
683 struct notify_depth
*d
;
684 struct db_record
*rec
;
686 /* see if change notify is enabled at all */
687 if (notify
== NULL
) {
688 return NT_STATUS_NOT_IMPLEMENTED
;
691 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
692 if (listel
->private_data
== private_data
) {
693 DLIST_REMOVE(notify
->list
, listel
);
697 if (listel
== NULL
) {
698 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
701 depth
= listel
->depth
;
705 status
= notify_fetch_locked(notify
, &rec
);
706 NT_STATUS_NOT_OK_RETURN(status
);
708 status
= notify_load(notify
, rec
);
709 if (!NT_STATUS_IS_OK(status
)) {
714 if (depth
>= notify
->array
->num_depths
) {
716 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
719 /* we only have to search at the depth of this element */
720 d
= ¬ify
->array
->depth
[depth
];
722 for (i
=0;i
<d
->num_entries
;i
++) {
723 if (private_data
== d
->entries
[i
].private_data
&&
724 procid_equal(¬ify
->server
, &d
->entries
[i
].server
)) {
728 if (i
== d
->num_entries
) {
730 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
733 if (i
< d
->num_entries
-1) {
734 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
735 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
739 status
= notify_save(notify
, rec
);
747 remove all notify watches for a messaging server
749 static NTSTATUS
notify_remove_all(struct notify_context
*notify
,
750 const struct server_id
*server
)
753 int i
, depth
, del_count
=0;
754 struct db_record
*rec
;
756 status
= notify_fetch_locked(notify
, &rec
);
757 NT_STATUS_NOT_OK_RETURN(status
);
759 status
= notify_load(notify
, rec
);
760 if (!NT_STATUS_IS_OK(status
)) {
765 /* we have to search for all entries across all depths, looking for matches
767 for (depth
=0;depth
<notify
->array
->num_depths
;depth
++) {
768 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
769 for (i
=0;i
<d
->num_entries
;i
++) {
770 if (procid_equal(server
, &d
->entries
[i
].server
)) {
771 if (i
< d
->num_entries
-1) {
772 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
773 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
783 status
= notify_save(notify
, rec
);
793 send a notify message to another messaging server
795 static NTSTATUS
notify_send(struct notify_context
*notify
, struct notify_entry
*e
,
796 const char *path
, uint32_t action
)
798 struct notify_event ev
;
801 enum ndr_err_code ndr_err
;
805 ev
.private_data
= e
->private_data
;
807 ndr_err
= ndr_push_struct_blob(&data
, talloc_tos(), &ev
,
808 (ndr_push_flags_fn_t
)ndr_push_notify_event
);
809 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
810 return ndr_map_error2ntstatus(ndr_err
);
813 status
= messaging_send(notify
->messaging_ctx
, e
->server
,
814 MSG_PVFS_NOTIFY
, &data
);
815 TALLOC_FREE(data
.data
);
819 void notify_onelevel(struct notify_context
*notify
, uint32_t action
,
820 uint32_t filter
, struct file_id fid
, const char *name
)
822 struct notify_entry_array
*array
;
825 bool have_dead_entries
= false;
829 if (notify
== NULL
) {
833 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
838 status
= dbwrap_fetch(notify
->db_onelevel
, array
,
839 make_tdb_data((uint8_t *)&fid
, sizeof(fid
)),
841 if (!NT_STATUS_IS_OK(status
)) {
846 blob
.data
= (uint8
*)dbuf
.dptr
;
847 blob
.length
= dbuf
.dsize
;
849 if (blob
.length
> 0) {
850 enum ndr_err_code ndr_err
;
851 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
852 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
853 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
854 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
855 ndr_errstr(ndr_err
)));
859 if (DEBUGLEVEL
>= 10) {
860 DEBUG(10, ("notify_onelevel:\n"));
861 NDR_PRINT_DEBUG(notify_entry_array
, array
);
865 for (i
=0; i
<array
->num_entries
; i
++) {
866 struct notify_entry
*e
= &array
->entries
[i
];
868 if ((e
->filter
& filter
) != 0) {
869 status
= notify_send(notify
, e
, name
, action
);
871 status
, NT_STATUS_INVALID_HANDLE
)) {
873 * Mark the entry as dead. All entries have a
874 * path set. The marker used here is setting
878 have_dead_entries
= true;
883 if (!have_dead_entries
) {
888 for (i
=0; i
<array
->num_entries
; i
++) {
889 struct notify_entry
*e
= &array
->entries
[i
];
890 if (e
->path
!= NULL
) {
893 DEBUG(10, ("Deleting notify entries for process %s because "
894 "it's gone\n", procid_str_static(&e
->server
)));
896 * Potential TODO: This might need optimizing,
897 * notify_remove_onelevel() does a fetch_locked() operation at
898 * every call. But this would only matter if a process with
899 * MANY notifies has died without shutting down properly.
901 notify_remove_onelevel(notify
, &e
->dir_id
, e
->private_data
);
909 trigger a notify message for anyone waiting on a matching event
911 This function is called a lot, and needs to be very fast. The unusual data structure
912 and traversal is designed to be fast in the average case, even for large numbers of
915 void notify_trigger(struct notify_context
*notify
,
916 uint32_t action
, uint32_t filter
, const char *path
)
920 const char *p
, *next_p
;
922 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
923 "path=%s\n", (unsigned)action
, (unsigned)filter
, path
));
925 /* see if change notify is enabled at all */
926 if (notify
== NULL
) {
931 status
= notify_load(notify
, NULL
);
932 if (!NT_STATUS_IS_OK(status
)) {
936 /* loop along the given path, working with each directory depth separately */
938 p
&& depth
< notify
->array
->num_depths
;
940 int p_len
= p
- path
;
942 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
944 next_p
= strchr(p
+1, '/');
946 /* see if there are any entries at this depth */
947 if (d
->num_entries
== 0) continue;
949 /* try to skip based on the maximum mask. If next_p is
950 NULL then we know it will be a 'this directory'
951 match, otherwise it must be a subdir match */
953 d_max_mask
= next_p
? d
->max_mask_subdir
: d
->max_mask
;
955 if ((filter
& d_max_mask
) == 0) {
959 /* we know there is an entry here worth looking
960 for. Use a bisection search to find the first entry
961 with a matching path */
963 max_i
= d
->num_entries
-1;
965 while (min_i
< max_i
) {
966 struct notify_entry
*e
;
970 cmp
= strncmp(path
, e
->path
, p_len
);
972 if (p_len
== e
->path_len
) {
977 } else if (cmp
< 0) {
984 if (min_i
!= max_i
) {
989 /* we now know that the entries start at min_i */
990 for (i
=min_i
;i
<d
->num_entries
;i
++) {
991 struct notify_entry
*e
= &d
->entries
[i
];
993 if (p_len
!= e
->path_len
||
994 strncmp(path
, e
->path
, p_len
) != 0) break;
996 e_filter
= next_p
? e
->subdir_filter
: e
->filter
;
998 if ((filter
& e_filter
) == 0) {
1002 status
= notify_send(notify
, e
, path
+ e
->path_len
+ 1,
1005 if (NT_STATUS_EQUAL(
1006 status
, NT_STATUS_INVALID_HANDLE
)) {
1007 struct server_id server
= e
->server
;
1009 DEBUG(10, ("Deleting notify entries for "
1010 "process %s because it's gone\n",
1011 procid_str_static(&e
->server
)));
1012 notify_remove_all(notify
, &server
);