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 "librpc/gen_ndr/ndr_notify.h"
28 #include "librpc/gen_ndr/messaging.h"
30 struct notify_context
{
31 struct db_context
*db_recursive
;
32 struct db_context
*db_onelevel
;
33 struct server_id server
;
34 struct messaging_context
*messaging_ctx
;
35 struct notify_list
*list
;
36 struct notify_array
*array
;
38 struct sys_notify_context
*sys_notify_ctx
;
44 struct notify_list
*next
, *prev
;
46 void (*callback
)(void *, const struct notify_event
*);
47 void *sys_notify_handle
;
51 #define NOTIFY_KEY "notify array"
53 #define NOTIFY_ENABLE "notify:enable"
54 #define NOTIFY_ENABLE_DEFAULT True
56 static NTSTATUS
notify_remove_all(struct notify_context
*notify
,
57 const struct server_id
*server
);
58 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
59 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
);
62 destroy the notify context
64 static int notify_destructor(struct notify_context
*notify
)
66 messaging_deregister(notify
->messaging_ctx
, MSG_PVFS_NOTIFY
, notify
);
68 if (notify
->list
!= NULL
) {
69 notify_remove_all(notify
, ¬ify
->server
);
76 Open up the notify.tdb database. You should close it down using
77 talloc_free(). We need the messaging_ctx to allow for notifications
80 struct notify_context
*notify_init(TALLOC_CTX
*mem_ctx
, struct server_id server
,
81 struct messaging_context
*messaging_ctx
,
82 struct event_context
*ev
,
83 connection_struct
*conn
)
85 struct notify_context
*notify
;
87 if (!lp_change_notify(conn
->params
)) {
91 notify
= talloc(mem_ctx
, struct notify_context
);
96 notify
->db_recursive
= db_open(notify
, lock_path("notify.tdb"),
97 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
,
98 O_RDWR
|O_CREAT
, 0644);
99 if (notify
->db_recursive
== NULL
) {
104 notify
->db_onelevel
= db_open(notify
, lock_path("notify_onelevel.tdb"),
105 0, TDB_CLEAR_IF_FIRST
,
106 O_RDWR
|O_CREAT
, 0644);
107 if (notify
->db_onelevel
== NULL
) {
112 notify
->server
= server
;
113 notify
->messaging_ctx
= messaging_ctx
;
115 notify
->array
= NULL
;
116 notify
->seqnum
= notify
->db_recursive
->get_seqnum(
117 notify
->db_recursive
);
118 notify
->key
= string_term_tdb_data(NOTIFY_KEY
);
120 talloc_set_destructor(notify
, notify_destructor
);
122 /* register with the messaging subsystem for the notify
124 messaging_register(notify
->messaging_ctx
, notify
,
125 MSG_PVFS_NOTIFY
, notify_handler
);
127 notify
->sys_notify_ctx
= sys_notify_context_create(conn
, notify
, ev
);
132 bool notify_internal_parent_init(void)
134 struct tdb_wrap
*db1
, *db2
;
136 if (lp_clustering()) {
141 * Open the tdbs in the parent process (smbd) so that our
142 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
146 db1
= tdb_wrap_open(talloc_autofree_context(), lock_path("notify.tdb"),
147 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
,
148 O_RDWR
|O_CREAT
, 0644);
150 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno
)));
153 db2
= tdb_wrap_open(talloc_autofree_context(),
154 lock_path("notify_onelevel.tdb"),
155 0, TDB_CLEAR_IF_FIRST
, O_RDWR
|O_CREAT
, 0644);
157 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
166 lock and fetch the record
168 static NTSTATUS
notify_fetch_locked(struct notify_context
*notify
, struct db_record
**rec
)
170 *rec
= notify
->db_recursive
->fetch_locked(notify
->db_recursive
,
171 notify
, notify
->key
);
173 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
179 load the notify array
181 static NTSTATUS
notify_load(struct notify_context
*notify
, struct db_record
*rec
)
188 seqnum
= notify
->db_recursive
->get_seqnum(notify
->db_recursive
);
190 if (seqnum
== notify
->seqnum
&& notify
->array
!= NULL
) {
194 notify
->seqnum
= seqnum
;
196 talloc_free(notify
->array
);
197 notify
->array
= TALLOC_ZERO_P(notify
, struct notify_array
);
198 NT_STATUS_HAVE_NO_MEMORY(notify
->array
);
201 if (notify
->db_recursive
->fetch(notify
->db_recursive
, notify
,
202 notify
->key
, &dbuf
) != 0) {
203 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
209 blob
.data
= (uint8
*)dbuf
.dptr
;
210 blob
.length
= dbuf
.dsize
;
212 status
= NT_STATUS_OK
;
213 if (blob
.length
> 0) {
214 enum ndr_err_code ndr_err
;
215 ndr_err
= ndr_pull_struct_blob(&blob
, notify
->array
, notify
->array
,
216 (ndr_pull_flags_fn_t
)ndr_pull_notify_array
);
217 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
218 /* 1. log that we got a corrupt notify_array
219 * 2. clear the variable the garbage was stored into to not trip
220 * over it next time this method is entered with the same seqnum
221 * 3. delete it from the database */
222 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
224 ZERO_STRUCTP(notify
->array
);
226 rec
->delete_rec(rec
);
230 if (DEBUGLEVEL
>= 10) {
231 DEBUG(10, ("notify_load:\n"));
232 NDR_PRINT_DEBUG(notify_array
, notify
->array
);
239 talloc_free(dbuf
.dptr
);
246 compare notify entries for sorting
248 static int notify_compare(const struct notify_entry
*e1
, const struct notify_entry
*e2
)
250 return strcmp(e1
->path
, e2
->path
);
254 save the notify array
256 static NTSTATUS
notify_save(struct notify_context
*notify
, struct db_record
*rec
)
261 enum ndr_err_code ndr_err
;
264 /* if possible, remove some depth arrays */
265 while (notify
->array
->num_depths
> 0 &&
266 notify
->array
->depth
[notify
->array
->num_depths
-1].num_entries
== 0) {
267 notify
->array
->num_depths
--;
270 /* we might just be able to delete the record */
271 if (notify
->array
->num_depths
== 0) {
272 return rec
->delete_rec(rec
);
275 tmp_ctx
= talloc_new(notify
);
276 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx
);
278 ndr_err
= ndr_push_struct_blob(&blob
, tmp_ctx
, notify
->array
,
279 (ndr_push_flags_fn_t
)ndr_push_notify_array
);
280 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
281 talloc_free(tmp_ctx
);
282 return ndr_map_error2ntstatus(ndr_err
);
285 if (DEBUGLEVEL
>= 10) {
286 DEBUG(10, ("notify_save:\n"));
287 NDR_PRINT_DEBUG(notify_array
, notify
->array
);
290 dbuf
.dptr
= blob
.data
;
291 dbuf
.dsize
= blob
.length
;
293 status
= rec
->store(rec
, dbuf
, TDB_REPLACE
);
294 talloc_free(tmp_ctx
);
301 handle incoming notify messages
303 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
304 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
)
306 struct notify_context
*notify
= talloc_get_type(private_data
, struct notify_context
);
307 enum ndr_err_code ndr_err
;
308 struct notify_event ev
;
309 TALLOC_CTX
*tmp_ctx
= talloc_new(notify
);
310 struct notify_list
*listel
;
312 if (tmp_ctx
== NULL
) {
316 ndr_err
= ndr_pull_struct_blob(data
, tmp_ctx
, &ev
,
317 (ndr_pull_flags_fn_t
)ndr_pull_notify_event
);
318 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
319 talloc_free(tmp_ctx
);
323 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
324 if (listel
->private_data
== ev
.private_data
) {
325 listel
->callback(listel
->private_data
, &ev
);
330 talloc_free(tmp_ctx
);
334 callback from sys_notify telling us about changes from the OS
336 static void sys_notify_callback(struct sys_notify_context
*ctx
,
337 void *ptr
, struct notify_event
*ev
)
339 struct notify_list
*listel
= talloc_get_type(ptr
, struct notify_list
);
340 ev
->private_data
= listel
;
341 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
342 ev
->action
, ev
->path
));
343 listel
->callback(listel
->private_data
, ev
);
347 add an entry to the notify array
349 static NTSTATUS
notify_add_array(struct notify_context
*notify
, struct db_record
*rec
,
350 struct notify_entry
*e
,
351 void *private_data
, int depth
)
354 struct notify_depth
*d
;
355 struct notify_entry
*ee
;
357 /* possibly expand the depths array */
358 if (depth
>= notify
->array
->num_depths
) {
359 d
= talloc_realloc(notify
->array
, notify
->array
->depth
,
360 struct notify_depth
, depth
+1);
361 NT_STATUS_HAVE_NO_MEMORY(d
);
362 for (i
=notify
->array
->num_depths
;i
<=depth
;i
++) {
365 notify
->array
->depth
= d
;
366 notify
->array
->num_depths
= depth
+1;
368 d
= ¬ify
->array
->depth
[depth
];
370 /* expand the entries array */
371 ee
= talloc_realloc(notify
->array
->depth
, d
->entries
, struct notify_entry
,
373 NT_STATUS_HAVE_NO_MEMORY(ee
);
376 d
->entries
[d
->num_entries
] = *e
;
377 d
->entries
[d
->num_entries
].private_data
= private_data
;
378 d
->entries
[d
->num_entries
].server
= notify
->server
;
379 d
->entries
[d
->num_entries
].path_len
= strlen(e
->path
);
382 d
->max_mask
|= e
->filter
;
383 d
->max_mask_subdir
|= e
->subdir_filter
;
385 TYPESAFE_QSORT(d
->entries
, d
->num_entries
, notify_compare
);
387 /* recalculate the maximum masks */
389 d
->max_mask_subdir
= 0;
391 for (i
=0;i
<d
->num_entries
;i
++) {
392 d
->max_mask
|= d
->entries
[i
].filter
;
393 d
->max_mask_subdir
|= d
->entries
[i
].subdir_filter
;
396 return notify_save(notify
, rec
);
400 Add a non-recursive watch
403 static void notify_add_onelevel(struct notify_context
*notify
,
404 struct notify_entry
*e
, void *private_data
)
406 struct notify_entry_array
*array
;
407 struct db_record
*rec
;
410 enum ndr_err_code ndr_err
;
413 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
418 rec
= notify
->db_onelevel
->fetch_locked(
419 notify
->db_onelevel
, talloc_tos(),
420 make_tdb_data((uint8_t *)&e
->dir_id
, sizeof(e
->dir_id
)));
422 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
423 "\n", file_id_string_tos(&e
->dir_id
)));
428 blob
.data
= (uint8_t *)rec
->value
.dptr
;
429 blob
.length
= rec
->value
.dsize
;
431 if (blob
.length
> 0) {
432 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
433 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
434 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
435 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
436 ndr_errstr(ndr_err
)));
440 if (DEBUGLEVEL
>= 10) {
441 DEBUG(10, ("notify_add_onelevel:\n"));
442 NDR_PRINT_DEBUG(notify_entry_array
, array
);
446 array
->entries
= talloc_realloc(array
, array
->entries
,
448 array
->num_entries
+1);
449 if (array
->entries
== NULL
) {
453 array
->entries
[array
->num_entries
] = *e
;
454 array
->entries
[array
->num_entries
].private_data
= private_data
;
455 array
->entries
[array
->num_entries
].server
= notify
->server
;
456 array
->num_entries
+= 1;
458 ndr_err
= ndr_push_struct_blob(&blob
, rec
, array
,
459 (ndr_push_flags_fn_t
)ndr_push_notify_entry_array
);
460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
461 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
462 ndr_errstr(ndr_err
)));
467 if (DEBUGLEVEL
>= 10) {
468 DEBUG(10, ("notify_add_onelevel:\n"));
469 NDR_PRINT_DEBUG(notify_entry_array
, array
);
472 dbuf
.dptr
= blob
.data
;
473 dbuf
.dsize
= blob
.length
;
475 status
= rec
->store(rec
, dbuf
, TDB_REPLACE
);
477 if (!NT_STATUS_IS_OK(status
)) {
478 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
488 add a notify watch. This is called when a notify is first setup on a open
491 NTSTATUS
notify_add(struct notify_context
*notify
, struct notify_entry
*e0
,
492 void (*callback
)(void *, const struct notify_event
*),
495 struct notify_entry e
= *e0
;
497 char *tmp_path
= NULL
;
498 struct notify_list
*listel
;
501 struct db_record
*rec
;
503 /* see if change notify is enabled at all */
504 if (notify
== NULL
) {
505 return NT_STATUS_NOT_IMPLEMENTED
;
508 status
= notify_fetch_locked(notify
, &rec
);
509 NT_STATUS_NOT_OK_RETURN(status
);
511 status
= notify_load(notify
, rec
);
512 if (!NT_STATUS_IS_OK(status
)) {
517 /* cope with /. on the end of the path */
518 len
= strlen(e
.path
);
519 if (len
> 1 && e
.path
[len
-1] == '.' && e
.path
[len
-2] == '/') {
520 tmp_path
= talloc_strndup(notify
, e
.path
, len
-2);
521 if (tmp_path
== NULL
) {
522 status
= NT_STATUS_NO_MEMORY
;
528 depth
= count_chars(e
.path
, '/');
530 listel
= TALLOC_ZERO_P(notify
, struct notify_list
);
531 if (listel
== NULL
) {
532 status
= NT_STATUS_NO_MEMORY
;
536 listel
->private_data
= private_data
;
537 listel
->callback
= callback
;
538 listel
->depth
= depth
;
539 DLIST_ADD(notify
->list
, listel
);
541 /* ignore failures from sys_notify */
542 if (notify
->sys_notify_ctx
!= NULL
) {
544 this call will modify e.filter and e.subdir_filter
545 to remove bits handled by the backend
547 status
= sys_notify_watch(notify
->sys_notify_ctx
, &e
,
548 sys_notify_callback
, listel
,
549 &listel
->sys_notify_handle
);
550 if (NT_STATUS_IS_OK(status
)) {
551 talloc_steal(listel
, listel
->sys_notify_handle
);
556 notify_add_onelevel(notify
, &e
, private_data
);
557 status
= NT_STATUS_OK
;
560 /* if the system notify handler couldn't handle some of the
561 filter bits, or couldn't handle a request for recursion
562 then we need to install it in the array used for the
563 intra-samba notify handling */
564 if (e
.filter
!= 0 || e
.subdir_filter
!= 0) {
565 status
= notify_add_array(notify
, rec
, &e
, private_data
, depth
);
570 talloc_free(tmp_path
);
575 NTSTATUS
notify_remove_onelevel(struct notify_context
*notify
,
576 const struct file_id
*fid
,
579 struct notify_entry_array
*array
;
580 struct db_record
*rec
;
583 enum ndr_err_code ndr_err
;
587 if (notify
== NULL
) {
588 return NT_STATUS_NOT_IMPLEMENTED
;
591 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
593 return NT_STATUS_NO_MEMORY
;
596 rec
= notify
->db_onelevel
->fetch_locked(
597 notify
->db_onelevel
, array
,
598 make_tdb_data((uint8_t *)fid
, sizeof(*fid
)));
600 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
601 "\n", file_id_string_tos(fid
)));
603 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
606 blob
.data
= (uint8_t *)rec
->value
.dptr
;
607 blob
.length
= rec
->value
.dsize
;
609 if (blob
.length
> 0) {
610 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
611 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
612 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
613 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
614 ndr_errstr(ndr_err
)));
616 return ndr_map_error2ntstatus(ndr_err
);
618 if (DEBUGLEVEL
>= 10) {
619 DEBUG(10, ("notify_remove_onelevel:\n"));
620 NDR_PRINT_DEBUG(notify_entry_array
, array
);
624 for (i
=0; i
<array
->num_entries
; i
++) {
625 if ((private_data
== array
->entries
[i
].private_data
) &&
626 cluster_id_equal(¬ify
->server
,
627 &array
->entries
[i
].server
)) {
632 if (i
== array
->num_entries
) {
634 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
637 array
->entries
[i
] = array
->entries
[array
->num_entries
-1];
638 array
->num_entries
-= 1;
640 if (array
->num_entries
== 0) {
641 rec
->delete_rec(rec
);
646 ndr_err
= ndr_push_struct_blob(&blob
, rec
, array
,
647 (ndr_push_flags_fn_t
)ndr_push_notify_entry_array
);
648 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
649 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
650 ndr_errstr(ndr_err
)));
652 return ndr_map_error2ntstatus(ndr_err
);
655 if (DEBUGLEVEL
>= 10) {
656 DEBUG(10, ("notify_add_onelevel:\n"));
657 NDR_PRINT_DEBUG(notify_entry_array
, array
);
660 dbuf
.dptr
= blob
.data
;
661 dbuf
.dsize
= blob
.length
;
663 status
= rec
->store(rec
, dbuf
, TDB_REPLACE
);
665 if (!NT_STATUS_IS_OK(status
)) {
666 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
674 remove a notify watch. Called when the directory handle is closed
676 NTSTATUS
notify_remove(struct notify_context
*notify
, void *private_data
)
679 struct notify_list
*listel
;
681 struct notify_depth
*d
;
682 struct db_record
*rec
;
684 /* see if change notify is enabled at all */
685 if (notify
== NULL
) {
686 return NT_STATUS_NOT_IMPLEMENTED
;
689 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
690 if (listel
->private_data
== private_data
) {
691 DLIST_REMOVE(notify
->list
, listel
);
695 if (listel
== NULL
) {
696 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
699 depth
= listel
->depth
;
703 status
= notify_fetch_locked(notify
, &rec
);
704 NT_STATUS_NOT_OK_RETURN(status
);
706 status
= notify_load(notify
, rec
);
707 if (!NT_STATUS_IS_OK(status
)) {
712 if (depth
>= notify
->array
->num_depths
) {
714 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
717 /* we only have to search at the depth of this element */
718 d
= ¬ify
->array
->depth
[depth
];
720 for (i
=0;i
<d
->num_entries
;i
++) {
721 if (private_data
== d
->entries
[i
].private_data
&&
722 cluster_id_equal(¬ify
->server
, &d
->entries
[i
].server
)) {
726 if (i
== d
->num_entries
) {
728 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
731 if (i
< d
->num_entries
-1) {
732 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
733 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
737 status
= notify_save(notify
, rec
);
745 remove all notify watches for a messaging server
747 static NTSTATUS
notify_remove_all(struct notify_context
*notify
,
748 const struct server_id
*server
)
751 int i
, depth
, del_count
=0;
752 struct db_record
*rec
;
754 status
= notify_fetch_locked(notify
, &rec
);
755 NT_STATUS_NOT_OK_RETURN(status
);
757 status
= notify_load(notify
, rec
);
758 if (!NT_STATUS_IS_OK(status
)) {
763 /* we have to search for all entries across all depths, looking for matches
765 for (depth
=0;depth
<notify
->array
->num_depths
;depth
++) {
766 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
767 for (i
=0;i
<d
->num_entries
;i
++) {
768 if (cluster_id_equal(server
, &d
->entries
[i
].server
)) {
769 if (i
< d
->num_entries
-1) {
770 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
771 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
781 status
= notify_save(notify
, rec
);
791 send a notify message to another messaging server
793 static NTSTATUS
notify_send(struct notify_context
*notify
, struct notify_entry
*e
,
794 const char *path
, uint32_t action
)
796 struct notify_event ev
;
799 enum ndr_err_code ndr_err
;
804 ev
.private_data
= e
->private_data
;
806 tmp_ctx
= talloc_new(notify
);
808 ndr_err
= ndr_push_struct_blob(&data
, tmp_ctx
, &ev
,
809 (ndr_push_flags_fn_t
)ndr_push_notify_event
);
810 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
811 talloc_free(tmp_ctx
);
812 return ndr_map_error2ntstatus(ndr_err
);
815 status
= messaging_send(notify
->messaging_ctx
, e
->server
,
816 MSG_PVFS_NOTIFY
, &data
);
817 talloc_free(tmp_ctx
);
821 void notify_onelevel(struct notify_context
*notify
, uint32_t action
,
822 uint32_t filter
, struct file_id fid
, const char *name
)
824 struct notify_entry_array
*array
;
827 bool have_dead_entries
= false;
830 if (notify
== NULL
) {
834 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
839 if (notify
->db_onelevel
->fetch(
840 notify
->db_onelevel
, array
,
841 make_tdb_data((uint8_t *)&fid
, sizeof(fid
)),
847 blob
.data
= (uint8
*)dbuf
.dptr
;
848 blob
.length
= dbuf
.dsize
;
850 if (blob
.length
> 0) {
851 enum ndr_err_code ndr_err
;
852 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
853 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
854 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
855 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
856 ndr_errstr(ndr_err
)));
860 if (DEBUGLEVEL
>= 10) {
861 DEBUG(10, ("notify_onelevel:\n"));
862 NDR_PRINT_DEBUG(notify_entry_array
, array
);
866 for (i
=0; i
<array
->num_entries
; i
++) {
867 struct notify_entry
*e
= &array
->entries
[i
];
869 if ((e
->filter
& filter
) != 0) {
872 status
= notify_send(notify
, e
, name
, action
);
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
881 have_dead_entries
= true;
886 if (!have_dead_entries
) {
891 for (i
=0; i
<array
->num_entries
; i
++) {
892 struct notify_entry
*e
= &array
->entries
[i
];
893 if (e
->path
!= NULL
) {
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
);
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
918 void notify_trigger(struct notify_context
*notify
,
919 uint32_t action
, uint32_t filter
, const char *path
)
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
) {
934 status
= notify_load(notify
, NULL
);
935 if (!NT_STATUS_IS_OK(status
)) {
939 /* loop along the given path, working with each directory depth separately */
941 p
&& depth
< notify
->array
->num_depths
;
943 int p_len
= p
- path
;
945 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
946 next_p
= strchr(p
+1, '/');
948 /* see if there are any entries at this depth */
949 if (d
->num_entries
== 0) continue;
951 /* try to skip based on the maximum mask. If next_p is
952 NULL then we know it will be a 'this directory'
953 match, otherwise it must be a subdir match */
954 if (next_p
!= NULL
) {
955 if (0 == (filter
& d
->max_mask_subdir
)) {
959 if (0 == (filter
& d
->max_mask
)) {
964 /* we know there is an entry here worth looking
965 for. Use a bisection search to find the first entry
966 with a matching path */
968 max_i
= d
->num_entries
-1;
970 while (min_i
< max_i
) {
971 struct notify_entry
*e
;
975 cmp
= strncmp(path
, e
->path
, p_len
);
977 if (p_len
== e
->path_len
) {
982 } else if (cmp
< 0) {
989 if (min_i
!= max_i
) {
994 /* we now know that the entries start at min_i */
995 for (i
=min_i
;i
<d
->num_entries
;i
++) {
996 struct notify_entry
*e
= &d
->entries
[i
];
997 if (p_len
!= e
->path_len
||
998 strncmp(path
, e
->path
, p_len
) != 0) break;
999 if (next_p
!= NULL
) {
1000 if (0 == (filter
& e
->subdir_filter
)) {
1004 if (0 == (filter
& e
->filter
)) {
1008 status
= notify_send(notify
, e
, path
+ e
->path_len
+ 1,
1011 if (NT_STATUS_EQUAL(
1012 status
, NT_STATUS_INVALID_HANDLE
)) {
1013 struct server_id server
= e
->server
;
1015 DEBUG(10, ("Deleting notify entries for "
1016 "process %s because it's gone\n",
1017 procid_str_static(&e
->server
)));
1018 notify_remove_all(notify
, &server
);