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"
29 struct notify_context
{
30 struct db_context
*db_recursive
;
31 struct db_context
*db_onelevel
;
32 struct server_id server
;
33 struct messaging_context
*messaging_ctx
;
34 struct notify_list
*list
;
35 struct notify_array
*array
;
37 struct sys_notify_context
*sys_notify_ctx
;
43 struct notify_list
*next
, *prev
;
45 void (*callback
)(void *, const struct notify_event
*);
46 void *sys_notify_handle
;
50 #define NOTIFY_KEY "notify array"
52 #define NOTIFY_ENABLE "notify:enable"
53 #define NOTIFY_ENABLE_DEFAULT True
55 static NTSTATUS
notify_remove_all(struct notify_context
*notify
,
56 const struct server_id
*server
);
57 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
58 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
);
61 destroy the notify context
63 static int notify_destructor(struct notify_context
*notify
)
65 messaging_deregister(notify
->messaging_ctx
, MSG_PVFS_NOTIFY
, notify
);
67 if (notify
->list
!= NULL
) {
68 notify_remove_all(notify
, ¬ify
->server
);
75 Open up the notify.tdb database. You should close it down using
76 talloc_free(). We need the messaging_ctx to allow for notifications
79 struct notify_context
*notify_init(TALLOC_CTX
*mem_ctx
, struct server_id server
,
80 struct messaging_context
*messaging_ctx
,
81 struct event_context
*ev
,
82 connection_struct
*conn
)
84 struct notify_context
*notify
;
86 if (!lp_change_notify(conn
->params
)) {
90 notify
= talloc(mem_ctx
, struct notify_context
);
95 notify
->db_recursive
= db_open(notify
, lock_path("notify.tdb"),
96 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
,
97 O_RDWR
|O_CREAT
, 0644);
98 if (notify
->db_recursive
== NULL
) {
103 notify
->db_onelevel
= db_open(notify
, lock_path("notify_onelevel.tdb"),
104 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
,
105 O_RDWR
|O_CREAT
, 0644);
106 if (notify
->db_onelevel
== NULL
) {
111 notify
->server
= server
;
112 notify
->messaging_ctx
= messaging_ctx
;
114 notify
->array
= NULL
;
115 notify
->seqnum
= notify
->db_recursive
->get_seqnum(
116 notify
->db_recursive
);
117 notify
->key
= string_term_tdb_data(NOTIFY_KEY
);
119 talloc_set_destructor(notify
, notify_destructor
);
121 /* register with the messaging subsystem for the notify
123 messaging_register(notify
->messaging_ctx
, notify
,
124 MSG_PVFS_NOTIFY
, notify_handler
);
126 notify
->sys_notify_ctx
= sys_notify_context_create(conn
, notify
, ev
);
132 lock and fetch the record
134 static NTSTATUS
notify_fetch_locked(struct notify_context
*notify
, struct db_record
**rec
)
136 *rec
= notify
->db_recursive
->fetch_locked(notify
->db_recursive
,
137 notify
, notify
->key
);
139 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
145 load the notify array
147 static NTSTATUS
notify_load(struct notify_context
*notify
, struct db_record
*rec
)
154 seqnum
= notify
->db_recursive
->get_seqnum(notify
->db_recursive
);
156 if (seqnum
== notify
->seqnum
&& notify
->array
!= NULL
) {
160 notify
->seqnum
= seqnum
;
162 talloc_free(notify
->array
);
163 notify
->array
= TALLOC_ZERO_P(notify
, struct notify_array
);
164 NT_STATUS_HAVE_NO_MEMORY(notify
->array
);
167 if (notify
->db_recursive
->fetch(notify
->db_recursive
, notify
,
168 notify
->key
, &dbuf
) != 0) {
169 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
175 blob
.data
= (uint8
*)dbuf
.dptr
;
176 blob
.length
= dbuf
.dsize
;
178 status
= NT_STATUS_OK
;
179 if (blob
.length
> 0) {
180 enum ndr_err_code ndr_err
;
181 ndr_err
= ndr_pull_struct_blob(&blob
, notify
->array
, NULL
, notify
->array
,
182 (ndr_pull_flags_fn_t
)ndr_pull_notify_array
);
183 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
184 status
= ndr_map_error2ntstatus(ndr_err
);
188 if (DEBUGLEVEL
>= 10) {
189 DEBUG(10, ("notify_load:\n"));
190 NDR_PRINT_DEBUG(notify_array
, notify
->array
);
194 talloc_free(dbuf
.dptr
);
201 compare notify entries for sorting
203 static int notify_compare(const void *p1
, const void *p2
)
205 const struct notify_entry
*e1
= (const struct notify_entry
*)p1
;
206 const struct notify_entry
*e2
= (const struct notify_entry
*)p2
;
207 return strcmp(e1
->path
, e2
->path
);
211 save the notify array
213 static NTSTATUS
notify_save(struct notify_context
*notify
, struct db_record
*rec
)
218 enum ndr_err_code ndr_err
;
221 /* if possible, remove some depth arrays */
222 while (notify
->array
->num_depths
> 0 &&
223 notify
->array
->depth
[notify
->array
->num_depths
-1].num_entries
== 0) {
224 notify
->array
->num_depths
--;
227 /* we might just be able to delete the record */
228 if (notify
->array
->num_depths
== 0) {
229 return rec
->delete_rec(rec
);
232 tmp_ctx
= talloc_new(notify
);
233 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx
);
235 ndr_err
= ndr_push_struct_blob(&blob
, tmp_ctx
, NULL
, notify
->array
,
236 (ndr_push_flags_fn_t
)ndr_push_notify_array
);
237 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
238 talloc_free(tmp_ctx
);
239 return ndr_map_error2ntstatus(ndr_err
);
242 if (DEBUGLEVEL
>= 10) {
243 DEBUG(10, ("notify_save:\n"));
244 NDR_PRINT_DEBUG(notify_array
, notify
->array
);
247 dbuf
.dptr
= blob
.data
;
248 dbuf
.dsize
= blob
.length
;
250 status
= rec
->store(rec
, dbuf
, TDB_REPLACE
);
251 talloc_free(tmp_ctx
);
258 handle incoming notify messages
260 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
261 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
)
263 struct notify_context
*notify
= talloc_get_type(private_data
, struct notify_context
);
264 enum ndr_err_code ndr_err
;
265 struct notify_event ev
;
266 TALLOC_CTX
*tmp_ctx
= talloc_new(notify
);
267 struct notify_list
*listel
;
269 if (tmp_ctx
== NULL
) {
273 ndr_err
= ndr_pull_struct_blob(data
, tmp_ctx
, NULL
, &ev
,
274 (ndr_pull_flags_fn_t
)ndr_pull_notify_event
);
275 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
276 talloc_free(tmp_ctx
);
280 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
281 if (listel
->private_data
== ev
.private_data
) {
282 listel
->callback(listel
->private_data
, &ev
);
287 talloc_free(tmp_ctx
);
291 callback from sys_notify telling us about changes from the OS
293 static void sys_notify_callback(struct sys_notify_context
*ctx
,
294 void *ptr
, struct notify_event
*ev
)
296 struct notify_list
*listel
= talloc_get_type(ptr
, struct notify_list
);
297 ev
->private_data
= listel
;
298 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
299 ev
->action
, ev
->path
));
300 listel
->callback(listel
->private_data
, ev
);
304 add an entry to the notify array
306 static NTSTATUS
notify_add_array(struct notify_context
*notify
, struct db_record
*rec
,
307 struct notify_entry
*e
,
308 void *private_data
, int depth
)
311 struct notify_depth
*d
;
312 struct notify_entry
*ee
;
314 /* possibly expand the depths array */
315 if (depth
>= notify
->array
->num_depths
) {
316 d
= talloc_realloc(notify
->array
, notify
->array
->depth
,
317 struct notify_depth
, depth
+1);
318 NT_STATUS_HAVE_NO_MEMORY(d
);
319 for (i
=notify
->array
->num_depths
;i
<=depth
;i
++) {
322 notify
->array
->depth
= d
;
323 notify
->array
->num_depths
= depth
+1;
325 d
= ¬ify
->array
->depth
[depth
];
327 /* expand the entries array */
328 ee
= talloc_realloc(notify
->array
->depth
, d
->entries
, struct notify_entry
,
330 NT_STATUS_HAVE_NO_MEMORY(ee
);
333 d
->entries
[d
->num_entries
] = *e
;
334 d
->entries
[d
->num_entries
].private_data
= private_data
;
335 d
->entries
[d
->num_entries
].server
= notify
->server
;
336 d
->entries
[d
->num_entries
].path_len
= strlen(e
->path
);
339 d
->max_mask
|= e
->filter
;
340 d
->max_mask_subdir
|= e
->subdir_filter
;
342 if (d
->num_entries
> 1) {
343 qsort(d
->entries
, d
->num_entries
, sizeof(d
->entries
[0]), notify_compare
);
346 /* recalculate the maximum masks */
348 d
->max_mask_subdir
= 0;
350 for (i
=0;i
<d
->num_entries
;i
++) {
351 d
->max_mask
|= d
->entries
[i
].filter
;
352 d
->max_mask_subdir
|= d
->entries
[i
].subdir_filter
;
355 return notify_save(notify
, rec
);
359 Add a non-recursive watch
362 static void notify_add_onelevel(struct notify_context
*notify
,
363 struct notify_entry
*e
, void *private_data
)
365 struct notify_entry_array
*array
;
366 struct db_record
*rec
;
369 enum ndr_err_code ndr_err
;
372 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
377 rec
= notify
->db_onelevel
->fetch_locked(
378 notify
->db_onelevel
, talloc_tos(),
379 make_tdb_data((uint8_t *)&e
->dir_id
, sizeof(e
->dir_id
)));
381 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
382 "\n", file_id_string_tos(&e
->dir_id
)));
387 blob
.data
= (uint8_t *)rec
->value
.dptr
;
388 blob
.length
= rec
->value
.dsize
;
390 if (blob
.length
> 0) {
391 ndr_err
= ndr_pull_struct_blob(
392 &blob
, array
, NULL
, array
,
393 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
394 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
395 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
396 ndr_errstr(ndr_err
)));
400 if (DEBUGLEVEL
>= 10) {
401 DEBUG(10, ("notify_add_onelevel:\n"));
402 NDR_PRINT_DEBUG(notify_entry_array
, array
);
406 array
->entries
= talloc_realloc(array
, array
->entries
,
408 array
->num_entries
+1);
409 if (array
->entries
== NULL
) {
413 array
->entries
[array
->num_entries
] = *e
;
414 array
->entries
[array
->num_entries
].private_data
= private_data
;
415 array
->entries
[array
->num_entries
].server
= notify
->server
;
416 array
->num_entries
+= 1;
418 ndr_err
= ndr_push_struct_blob(
419 &blob
, rec
, NULL
, array
,
420 (ndr_push_flags_fn_t
)ndr_push_notify_entry_array
);
421 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
422 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
423 ndr_errstr(ndr_err
)));
428 if (DEBUGLEVEL
>= 10) {
429 DEBUG(10, ("notify_add_onelevel:\n"));
430 NDR_PRINT_DEBUG(notify_entry_array
, array
);
433 dbuf
.dptr
= blob
.data
;
434 dbuf
.dsize
= blob
.length
;
436 status
= rec
->store(rec
, dbuf
, TDB_REPLACE
);
438 if (!NT_STATUS_IS_OK(status
)) {
439 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
449 add a notify watch. This is called when a notify is first setup on a open
452 NTSTATUS
notify_add(struct notify_context
*notify
, struct notify_entry
*e0
,
453 void (*callback
)(void *, const struct notify_event
*),
456 struct notify_entry e
= *e0
;
458 char *tmp_path
= NULL
;
459 struct notify_list
*listel
;
462 struct db_record
*rec
;
464 /* see if change notify is enabled at all */
465 if (notify
== NULL
) {
466 return NT_STATUS_NOT_IMPLEMENTED
;
469 status
= notify_fetch_locked(notify
, &rec
);
470 NT_STATUS_NOT_OK_RETURN(status
);
472 status
= notify_load(notify
, rec
);
473 if (!NT_STATUS_IS_OK(status
)) {
478 /* cope with /. on the end of the path */
479 len
= strlen(e
.path
);
480 if (len
> 1 && e
.path
[len
-1] == '.' && e
.path
[len
-2] == '/') {
481 tmp_path
= talloc_strndup(notify
, e
.path
, len
-2);
482 if (tmp_path
== NULL
) {
483 status
= NT_STATUS_NO_MEMORY
;
489 depth
= count_chars(e
.path
, '/');
491 listel
= TALLOC_ZERO_P(notify
, struct notify_list
);
492 if (listel
== NULL
) {
493 status
= NT_STATUS_NO_MEMORY
;
497 listel
->private_data
= private_data
;
498 listel
->callback
= callback
;
499 listel
->depth
= depth
;
500 DLIST_ADD(notify
->list
, listel
);
502 /* ignore failures from sys_notify */
503 if (notify
->sys_notify_ctx
!= NULL
) {
505 this call will modify e.filter and e.subdir_filter
506 to remove bits handled by the backend
508 status
= sys_notify_watch(notify
->sys_notify_ctx
, &e
,
509 sys_notify_callback
, listel
,
510 &listel
->sys_notify_handle
);
511 if (NT_STATUS_IS_OK(status
)) {
512 talloc_steal(listel
, listel
->sys_notify_handle
);
517 notify_add_onelevel(notify
, &e
, private_data
);
518 status
= NT_STATUS_OK
;
521 /* if the system notify handler couldn't handle some of the
522 filter bits, or couldn't handle a request for recursion
523 then we need to install it in the array used for the
524 intra-samba notify handling */
525 if (e
.filter
!= 0 || e
.subdir_filter
!= 0) {
526 status
= notify_add_array(notify
, rec
, &e
, private_data
, depth
);
531 talloc_free(tmp_path
);
536 NTSTATUS
notify_remove_onelevel(struct notify_context
*notify
,
537 const struct file_id
*fid
,
540 struct notify_entry_array
*array
;
541 struct db_record
*rec
;
544 enum ndr_err_code ndr_err
;
548 if (notify
== NULL
) {
549 return NT_STATUS_NOT_IMPLEMENTED
;
552 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
554 return NT_STATUS_NO_MEMORY
;
557 rec
= notify
->db_onelevel
->fetch_locked(
558 notify
->db_onelevel
, array
,
559 make_tdb_data((uint8_t *)fid
, sizeof(*fid
)));
561 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
562 "\n", file_id_string_tos(fid
)));
564 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
567 blob
.data
= (uint8_t *)rec
->value
.dptr
;
568 blob
.length
= rec
->value
.dsize
;
570 if (blob
.length
> 0) {
571 ndr_err
= ndr_pull_struct_blob(
572 &blob
, array
, NULL
, array
,
573 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
574 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
575 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
576 ndr_errstr(ndr_err
)));
578 return ndr_map_error2ntstatus(ndr_err
);
580 if (DEBUGLEVEL
>= 10) {
581 DEBUG(10, ("notify_remove_onelevel:\n"));
582 NDR_PRINT_DEBUG(notify_entry_array
, array
);
586 for (i
=0; i
<array
->num_entries
; i
++) {
587 if ((private_data
== array
->entries
[i
].private_data
) &&
588 cluster_id_equal(¬ify
->server
,
589 &array
->entries
[i
].server
)) {
594 if (i
== array
->num_entries
) {
596 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
599 array
->entries
[i
] = array
->entries
[array
->num_entries
-1];
600 array
->num_entries
-= 1;
602 if (array
->num_entries
== 0) {
603 rec
->delete_rec(rec
);
608 ndr_err
= ndr_push_struct_blob(
609 &blob
, rec
, NULL
, array
,
610 (ndr_push_flags_fn_t
)ndr_push_notify_entry_array
);
611 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
612 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
613 ndr_errstr(ndr_err
)));
615 return ndr_map_error2ntstatus(ndr_err
);
618 if (DEBUGLEVEL
>= 10) {
619 DEBUG(10, ("notify_add_onelevel:\n"));
620 NDR_PRINT_DEBUG(notify_entry_array
, array
);
623 dbuf
.dptr
= blob
.data
;
624 dbuf
.dsize
= blob
.length
;
626 status
= rec
->store(rec
, dbuf
, TDB_REPLACE
);
628 if (!NT_STATUS_IS_OK(status
)) {
629 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
637 remove a notify watch. Called when the directory handle is closed
639 NTSTATUS
notify_remove(struct notify_context
*notify
, void *private_data
)
642 struct notify_list
*listel
;
644 struct notify_depth
*d
;
645 struct db_record
*rec
;
647 /* see if change notify is enabled at all */
648 if (notify
== NULL
) {
649 return NT_STATUS_NOT_IMPLEMENTED
;
652 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
653 if (listel
->private_data
== private_data
) {
654 DLIST_REMOVE(notify
->list
, listel
);
658 if (listel
== NULL
) {
659 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
662 depth
= listel
->depth
;
666 status
= notify_fetch_locked(notify
, &rec
);
667 NT_STATUS_NOT_OK_RETURN(status
);
669 status
= notify_load(notify
, rec
);
670 if (!NT_STATUS_IS_OK(status
)) {
675 if (depth
>= notify
->array
->num_depths
) {
677 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
680 /* we only have to search at the depth of this element */
681 d
= ¬ify
->array
->depth
[depth
];
683 for (i
=0;i
<d
->num_entries
;i
++) {
684 if (private_data
== d
->entries
[i
].private_data
&&
685 cluster_id_equal(¬ify
->server
, &d
->entries
[i
].server
)) {
689 if (i
== d
->num_entries
) {
691 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
694 if (i
< d
->num_entries
-1) {
695 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
696 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
700 status
= notify_save(notify
, rec
);
708 remove all notify watches for a messaging server
710 static NTSTATUS
notify_remove_all(struct notify_context
*notify
,
711 const struct server_id
*server
)
714 int i
, depth
, del_count
=0;
715 struct db_record
*rec
;
717 status
= notify_fetch_locked(notify
, &rec
);
718 NT_STATUS_NOT_OK_RETURN(status
);
720 status
= notify_load(notify
, rec
);
721 if (!NT_STATUS_IS_OK(status
)) {
726 /* we have to search for all entries across all depths, looking for matches
728 for (depth
=0;depth
<notify
->array
->num_depths
;depth
++) {
729 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
730 for (i
=0;i
<d
->num_entries
;i
++) {
731 if (cluster_id_equal(server
, &d
->entries
[i
].server
)) {
732 if (i
< d
->num_entries
-1) {
733 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
734 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
744 status
= notify_save(notify
, rec
);
754 send a notify message to another messaging server
756 static NTSTATUS
notify_send(struct notify_context
*notify
, struct notify_entry
*e
,
757 const char *path
, uint32_t action
)
759 struct notify_event ev
;
762 enum ndr_err_code ndr_err
;
767 ev
.private_data
= e
->private_data
;
769 tmp_ctx
= talloc_new(notify
);
771 ndr_err
= ndr_push_struct_blob(&data
, tmp_ctx
, NULL
, &ev
,
772 (ndr_push_flags_fn_t
)ndr_push_notify_event
);
773 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
774 talloc_free(tmp_ctx
);
775 return ndr_map_error2ntstatus(ndr_err
);
778 status
= messaging_send(notify
->messaging_ctx
, e
->server
,
779 MSG_PVFS_NOTIFY
, &data
);
780 talloc_free(tmp_ctx
);
784 void notify_onelevel(struct notify_context
*notify
, uint32_t action
,
785 uint32_t filter
, struct file_id fid
, const char *name
)
787 struct notify_entry_array
*array
;
790 bool have_dead_entries
= false;
793 if (notify
== NULL
) {
797 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
802 if (notify
->db_onelevel
->fetch(
803 notify
->db_onelevel
, array
,
804 make_tdb_data((uint8_t *)&fid
, sizeof(fid
)),
810 blob
.data
= (uint8
*)dbuf
.dptr
;
811 blob
.length
= dbuf
.dsize
;
813 if (blob
.length
> 0) {
814 enum ndr_err_code ndr_err
;
815 ndr_err
= ndr_pull_struct_blob(
816 &blob
, array
, NULL
, array
,
817 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
818 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
819 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
820 ndr_errstr(ndr_err
)));
824 if (DEBUGLEVEL
>= 10) {
825 DEBUG(10, ("notify_onelevel:\n"));
826 NDR_PRINT_DEBUG(notify_entry_array
, array
);
830 for (i
=0; i
<array
->num_entries
; i
++) {
831 struct notify_entry
*e
= &array
->entries
[i
];
833 if ((e
->filter
& filter
) != 0) {
836 status
= notify_send(notify
, e
, name
, action
);
838 status
, NT_STATUS_INVALID_HANDLE
)) {
840 * Mark the entry as dead. All entries have a
841 * path set. The marker used here is setting
845 have_dead_entries
= true;
850 if (!have_dead_entries
) {
855 for (i
=0; i
<array
->num_entries
; i
++) {
856 struct notify_entry
*e
= &array
->entries
[i
];
857 if (e
->path
!= NULL
) {
860 DEBUG(10, ("Deleting notify entries for process %s because "
861 "it's gone\n", procid_str_static(&e
->server
)));
863 * Potential TODO: This might need optimizing,
864 * notify_remove_onelevel() does a fetch_locked() operation at
865 * every call. But this would only matter if a process with
866 * MANY notifies has died without shutting down properly.
868 notify_remove_onelevel(notify
, &e
->dir_id
, e
->private_data
);
876 trigger a notify message for anyone waiting on a matching event
878 This function is called a lot, and needs to be very fast. The unusual data structure
879 and traversal is designed to be fast in the average case, even for large numbers of
882 void notify_trigger(struct notify_context
*notify
,
883 uint32_t action
, uint32_t filter
, const char *path
)
887 const char *p
, *next_p
;
889 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
890 "path=%s\n", (unsigned)action
, (unsigned)filter
, path
));
892 /* see if change notify is enabled at all */
893 if (notify
== NULL
) {
898 status
= notify_load(notify
, NULL
);
899 if (!NT_STATUS_IS_OK(status
)) {
903 /* loop along the given path, working with each directory depth separately */
905 p
&& depth
< notify
->array
->num_depths
;
907 int p_len
= p
- path
;
909 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
910 next_p
= strchr(p
+1, '/');
912 /* see if there are any entries at this depth */
913 if (d
->num_entries
== 0) continue;
915 /* try to skip based on the maximum mask. If next_p is
916 NULL then we know it will be a 'this directory'
917 match, otherwise it must be a subdir match */
918 if (next_p
!= NULL
) {
919 if (0 == (filter
& d
->max_mask_subdir
)) {
923 if (0 == (filter
& d
->max_mask
)) {
928 /* we know there is an entry here worth looking
929 for. Use a bisection search to find the first entry
930 with a matching path */
932 max_i
= d
->num_entries
-1;
934 while (min_i
< max_i
) {
935 struct notify_entry
*e
;
939 cmp
= strncmp(path
, e
->path
, p_len
);
941 if (p_len
== e
->path_len
) {
946 } else if (cmp
< 0) {
953 if (min_i
!= max_i
) {
958 /* we now know that the entries start at min_i */
959 for (i
=min_i
;i
<d
->num_entries
;i
++) {
960 struct notify_entry
*e
= &d
->entries
[i
];
961 if (p_len
!= e
->path_len
||
962 strncmp(path
, e
->path
, p_len
) != 0) break;
963 if (next_p
!= NULL
) {
964 if (0 == (filter
& e
->subdir_filter
)) {
968 if (0 == (filter
& e
->filter
)) {
972 status
= notify_send(notify
, e
, path
+ e
->path_len
+ 1,
976 status
, NT_STATUS_INVALID_HANDLE
)) {
977 struct server_id server
= e
->server
;
979 DEBUG(10, ("Deleting notify entries for "
980 "process %s because it's gone\n",
981 procid_str_static(&e
->server
)));
982 notify_remove_all(notify
, &server
);