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/util/tdb_wrap.h"
36 struct notify_context
{
37 struct db_context
*db_recursive
;
38 struct db_context
*db_onelevel
;
39 struct server_id server
;
40 struct messaging_context
*messaging_ctx
;
41 struct notify_list
*list
;
42 struct notify_array
*array
;
44 struct sys_notify_context
*sys_notify_ctx
;
50 struct notify_list
*next
, *prev
;
52 void (*callback
)(void *, const struct notify_event
*);
53 void *sys_notify_handle
;
57 #define NOTIFY_KEY "notify array"
59 #define NOTIFY_ENABLE "notify:enable"
60 #define NOTIFY_ENABLE_DEFAULT True
62 static NTSTATUS
notify_remove_all(struct notify_context
*notify
,
63 const struct server_id
*server
);
64 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
65 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
);
68 destroy the notify context
70 static int notify_destructor(struct notify_context
*notify
)
72 messaging_deregister(notify
->messaging_ctx
, MSG_PVFS_NOTIFY
, notify
);
74 if (notify
->list
!= NULL
) {
75 notify_remove_all(notify
, ¬ify
->server
);
82 Open up the notify.tdb database. You should close it down using
83 talloc_free(). We need the messaging_ctx to allow for notifications
86 struct notify_context
*notify_init(TALLOC_CTX
*mem_ctx
, struct server_id server
,
87 struct messaging_context
*messaging_ctx
,
88 struct event_context
*ev
,
89 connection_struct
*conn
)
91 struct notify_context
*notify
;
93 if (!lp_change_notify(conn
->params
)) {
97 notify
= talloc(mem_ctx
, struct notify_context
);
102 notify
->db_recursive
= db_open(notify
, lock_path("notify.tdb"),
103 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
,
104 O_RDWR
|O_CREAT
, 0644);
105 if (notify
->db_recursive
== NULL
) {
110 notify
->db_onelevel
= db_open(notify
, lock_path("notify_onelevel.tdb"),
111 0, TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
,
112 O_RDWR
|O_CREAT
, 0644);
113 if (notify
->db_onelevel
== NULL
) {
118 notify
->server
= server
;
119 notify
->messaging_ctx
= messaging_ctx
;
121 notify
->array
= NULL
;
122 notify
->seqnum
= dbwrap_get_seqnum(notify
->db_recursive
);
123 notify
->key
= string_term_tdb_data(NOTIFY_KEY
);
125 talloc_set_destructor(notify
, notify_destructor
);
127 /* register with the messaging subsystem for the notify
129 messaging_register(notify
->messaging_ctx
, notify
,
130 MSG_PVFS_NOTIFY
, notify_handler
);
132 notify
->sys_notify_ctx
= sys_notify_context_create(conn
, notify
, ev
);
137 bool notify_internal_parent_init(TALLOC_CTX
*mem_ctx
)
139 struct tdb_wrap
*db1
, *db2
;
141 if (lp_clustering()) {
146 * Open the tdbs in the parent process (smbd) so that our
147 * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
151 db1
= tdb_wrap_open(mem_ctx
, lock_path("notify.tdb"),
152 0, TDB_SEQNUM
|TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
,
153 O_RDWR
|O_CREAT
, 0644);
155 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno
)));
158 db2
= tdb_wrap_open(mem_ctx
, lock_path("notify_onelevel.tdb"),
159 0, TDB_CLEAR_IF_FIRST
|TDB_INCOMPATIBLE_HASH
, O_RDWR
|O_CREAT
, 0644);
161 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
170 lock and fetch the record
172 static NTSTATUS
notify_fetch_locked(struct notify_context
*notify
, struct db_record
**rec
)
174 *rec
= dbwrap_fetch_locked(notify
->db_recursive
, notify
, notify
->key
);
176 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
182 load the notify array
184 static NTSTATUS
notify_load(struct notify_context
*notify
, struct db_record
*rec
)
191 seqnum
= dbwrap_get_seqnum(notify
->db_recursive
);
193 if (seqnum
== notify
->seqnum
&& notify
->array
!= NULL
) {
197 notify
->seqnum
= seqnum
;
199 talloc_free(notify
->array
);
200 notify
->array
= talloc_zero(notify
, struct notify_array
);
201 NT_STATUS_HAVE_NO_MEMORY(notify
->array
);
204 status
= dbwrap_fetch(notify
->db_recursive
, notify
,
206 if (!NT_STATUS_IS_OK(status
)) {
207 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
210 dbuf
= dbwrap_record_get_value(rec
);
213 blob
.data
= (uint8
*)dbuf
.dptr
;
214 blob
.length
= dbuf
.dsize
;
216 status
= NT_STATUS_OK
;
217 if (blob
.length
> 0) {
218 enum ndr_err_code ndr_err
;
219 ndr_err
= ndr_pull_struct_blob(&blob
, notify
->array
, notify
->array
,
220 (ndr_pull_flags_fn_t
)ndr_pull_notify_array
);
221 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
222 /* 1. log that we got a corrupt notify_array
223 * 2. clear the variable the garbage was stored into to not trip
224 * over it next time this method is entered with the same seqnum
225 * 3. delete it from the database */
226 DEBUG(2, ("notify_array is corrupt, discarding it\n"));
228 ZERO_STRUCTP(notify
->array
);
230 dbwrap_record_delete(rec
);
234 if (DEBUGLEVEL
>= 10) {
235 DEBUG(10, ("notify_load:\n"));
236 NDR_PRINT_DEBUG(notify_array
, notify
->array
);
243 talloc_free(dbuf
.dptr
);
250 compare notify entries for sorting
252 static int notify_compare(const struct notify_entry
*e1
, const struct notify_entry
*e2
)
254 return strcmp(e1
->path
, e2
->path
);
258 save the notify array
260 static NTSTATUS
notify_save(struct notify_context
*notify
, struct db_record
*rec
)
265 enum ndr_err_code ndr_err
;
268 /* if possible, remove some depth arrays */
269 while (notify
->array
->num_depths
> 0 &&
270 notify
->array
->depth
[notify
->array
->num_depths
-1].num_entries
== 0) {
271 notify
->array
->num_depths
--;
274 /* we might just be able to delete the record */
275 if (notify
->array
->num_depths
== 0) {
276 return dbwrap_record_delete(rec
);
279 tmp_ctx
= talloc_new(notify
);
280 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx
);
282 ndr_err
= ndr_push_struct_blob(&blob
, tmp_ctx
, notify
->array
,
283 (ndr_push_flags_fn_t
)ndr_push_notify_array
);
284 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
285 talloc_free(tmp_ctx
);
286 return ndr_map_error2ntstatus(ndr_err
);
289 if (DEBUGLEVEL
>= 10) {
290 DEBUG(10, ("notify_save:\n"));
291 NDR_PRINT_DEBUG(notify_array
, notify
->array
);
294 dbuf
.dptr
= blob
.data
;
295 dbuf
.dsize
= blob
.length
;
297 status
= dbwrap_record_store(rec
, dbuf
, TDB_REPLACE
);
298 talloc_free(tmp_ctx
);
305 handle incoming notify messages
307 static void notify_handler(struct messaging_context
*msg_ctx
, void *private_data
,
308 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
)
310 struct notify_context
*notify
= talloc_get_type(private_data
, struct notify_context
);
311 enum ndr_err_code ndr_err
;
312 struct notify_event ev
;
313 TALLOC_CTX
*tmp_ctx
= talloc_new(notify
);
314 struct notify_list
*listel
;
316 if (tmp_ctx
== NULL
) {
320 ndr_err
= ndr_pull_struct_blob(data
, tmp_ctx
, &ev
,
321 (ndr_pull_flags_fn_t
)ndr_pull_notify_event
);
322 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
323 talloc_free(tmp_ctx
);
327 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
328 if (listel
->private_data
== ev
.private_data
) {
329 listel
->callback(listel
->private_data
, &ev
);
334 talloc_free(tmp_ctx
);
338 callback from sys_notify telling us about changes from the OS
340 static void sys_notify_callback(struct sys_notify_context
*ctx
,
341 void *ptr
, struct notify_event
*ev
)
343 struct notify_list
*listel
= talloc_get_type(ptr
, struct notify_list
);
344 ev
->private_data
= listel
;
345 DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
346 ev
->action
, ev
->path
));
347 listel
->callback(listel
->private_data
, ev
);
351 add an entry to the notify array
353 static NTSTATUS
notify_add_array(struct notify_context
*notify
, struct db_record
*rec
,
354 struct notify_entry
*e
,
355 void *private_data
, int depth
)
358 struct notify_depth
*d
;
359 struct notify_entry
*ee
;
361 /* possibly expand the depths array */
362 if (depth
>= notify
->array
->num_depths
) {
363 d
= talloc_realloc(notify
->array
, notify
->array
->depth
,
364 struct notify_depth
, depth
+1);
365 NT_STATUS_HAVE_NO_MEMORY(d
);
366 for (i
=notify
->array
->num_depths
;i
<=depth
;i
++) {
369 notify
->array
->depth
= d
;
370 notify
->array
->num_depths
= depth
+1;
372 d
= ¬ify
->array
->depth
[depth
];
374 /* expand the entries array */
375 ee
= talloc_realloc(notify
->array
->depth
, d
->entries
, struct notify_entry
,
377 NT_STATUS_HAVE_NO_MEMORY(ee
);
380 d
->entries
[d
->num_entries
] = *e
;
381 d
->entries
[d
->num_entries
].private_data
= private_data
;
382 d
->entries
[d
->num_entries
].server
= notify
->server
;
383 d
->entries
[d
->num_entries
].path_len
= strlen(e
->path
);
386 d
->max_mask
|= e
->filter
;
387 d
->max_mask_subdir
|= e
->subdir_filter
;
389 TYPESAFE_QSORT(d
->entries
, d
->num_entries
, notify_compare
);
391 /* recalculate the maximum masks */
393 d
->max_mask_subdir
= 0;
395 for (i
=0;i
<d
->num_entries
;i
++) {
396 d
->max_mask
|= d
->entries
[i
].filter
;
397 d
->max_mask_subdir
|= d
->entries
[i
].subdir_filter
;
400 return notify_save(notify
, rec
);
404 Add a non-recursive watch
407 static void notify_add_onelevel(struct notify_context
*notify
,
408 struct notify_entry
*e
, void *private_data
)
410 struct notify_entry_array
*array
;
411 struct db_record
*rec
;
415 enum ndr_err_code ndr_err
;
418 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
423 rec
= dbwrap_fetch_locked(notify
->db_onelevel
, talloc_tos(),
424 make_tdb_data((uint8_t *)&e
->dir_id
,
427 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
428 "\n", file_id_string_tos(&e
->dir_id
)));
433 value
= dbwrap_record_get_value(rec
);
434 blob
.data
= (uint8_t *)value
.dptr
;
435 blob
.length
= value
.dsize
;
437 if (blob
.length
> 0) {
438 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
439 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
440 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
441 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
442 ndr_errstr(ndr_err
)));
446 if (DEBUGLEVEL
>= 10) {
447 DEBUG(10, ("notify_add_onelevel:\n"));
448 NDR_PRINT_DEBUG(notify_entry_array
, array
);
452 array
->entries
= talloc_realloc(array
, array
->entries
,
454 array
->num_entries
+1);
455 if (array
->entries
== NULL
) {
459 array
->entries
[array
->num_entries
] = *e
;
460 array
->entries
[array
->num_entries
].private_data
= private_data
;
461 array
->entries
[array
->num_entries
].server
= notify
->server
;
462 array
->num_entries
+= 1;
464 ndr_err
= ndr_push_struct_blob(&blob
, rec
, array
,
465 (ndr_push_flags_fn_t
)ndr_push_notify_entry_array
);
466 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
467 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
468 ndr_errstr(ndr_err
)));
473 if (DEBUGLEVEL
>= 10) {
474 DEBUG(10, ("notify_add_onelevel:\n"));
475 NDR_PRINT_DEBUG(notify_entry_array
, array
);
478 dbuf
.dptr
= blob
.data
;
479 dbuf
.dsize
= blob
.length
;
481 status
= dbwrap_record_store(rec
, dbuf
, TDB_REPLACE
);
483 if (!NT_STATUS_IS_OK(status
)) {
484 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
494 add a notify watch. This is called when a notify is first setup on a open
497 NTSTATUS
notify_add(struct notify_context
*notify
, struct notify_entry
*e0
,
498 void (*callback
)(void *, const struct notify_event
*),
501 struct notify_entry e
= *e0
;
503 char *tmp_path
= NULL
;
504 struct notify_list
*listel
;
507 struct db_record
*rec
;
509 /* see if change notify is enabled at all */
510 if (notify
== NULL
) {
511 return NT_STATUS_NOT_IMPLEMENTED
;
514 status
= notify_fetch_locked(notify
, &rec
);
515 NT_STATUS_NOT_OK_RETURN(status
);
517 status
= notify_load(notify
, rec
);
518 if (!NT_STATUS_IS_OK(status
)) {
523 /* cope with /. on the end of the path */
524 len
= strlen(e
.path
);
525 if (len
> 1 && e
.path
[len
-1] == '.' && e
.path
[len
-2] == '/') {
526 tmp_path
= talloc_strndup(notify
, e
.path
, len
-2);
527 if (tmp_path
== NULL
) {
528 status
= NT_STATUS_NO_MEMORY
;
534 depth
= count_chars(e
.path
, '/');
536 listel
= talloc_zero(notify
, struct notify_list
);
537 if (listel
== NULL
) {
538 status
= NT_STATUS_NO_MEMORY
;
542 listel
->private_data
= private_data
;
543 listel
->callback
= callback
;
544 listel
->depth
= depth
;
545 DLIST_ADD(notify
->list
, listel
);
547 /* ignore failures from sys_notify */
548 if (notify
->sys_notify_ctx
!= NULL
) {
550 this call will modify e.filter and e.subdir_filter
551 to remove bits handled by the backend
553 status
= sys_notify_watch(notify
->sys_notify_ctx
, &e
,
554 sys_notify_callback
, listel
,
555 &listel
->sys_notify_handle
);
556 if (NT_STATUS_IS_OK(status
)) {
557 talloc_steal(listel
, listel
->sys_notify_handle
);
562 notify_add_onelevel(notify
, &e
, private_data
);
563 status
= NT_STATUS_OK
;
566 /* if the system notify handler couldn't handle some of the
567 filter bits, or couldn't handle a request for recursion
568 then we need to install it in the array used for the
569 intra-samba notify handling */
570 if (e
.filter
!= 0 || e
.subdir_filter
!= 0) {
571 status
= notify_add_array(notify
, rec
, &e
, private_data
, depth
);
576 talloc_free(tmp_path
);
581 NTSTATUS
notify_remove_onelevel(struct notify_context
*notify
,
582 const struct file_id
*fid
,
585 struct notify_entry_array
*array
;
586 struct db_record
*rec
;
590 enum ndr_err_code ndr_err
;
594 if (notify
== NULL
) {
595 return NT_STATUS_NOT_IMPLEMENTED
;
598 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
600 return NT_STATUS_NO_MEMORY
;
603 rec
= dbwrap_fetch_locked(
604 notify
->db_onelevel
, array
,
605 make_tdb_data((const uint8_t *)fid
, sizeof(*fid
)));
607 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
608 "\n", file_id_string_tos(fid
)));
610 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
613 value
= dbwrap_record_get_value(rec
);
614 blob
.data
= (uint8_t *)value
.dptr
;
615 blob
.length
= value
.dsize
;
617 if (blob
.length
> 0) {
618 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
619 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
620 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
621 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
622 ndr_errstr(ndr_err
)));
624 return ndr_map_error2ntstatus(ndr_err
);
626 if (DEBUGLEVEL
>= 10) {
627 DEBUG(10, ("notify_remove_onelevel:\n"));
628 NDR_PRINT_DEBUG(notify_entry_array
, array
);
632 for (i
=0; i
<array
->num_entries
; i
++) {
633 if ((private_data
== array
->entries
[i
].private_data
) &&
634 cluster_id_equal(¬ify
->server
,
635 &array
->entries
[i
].server
)) {
640 if (i
== array
->num_entries
) {
642 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
645 array
->entries
[i
] = array
->entries
[array
->num_entries
-1];
646 array
->num_entries
-= 1;
648 if (array
->num_entries
== 0) {
649 dbwrap_record_delete(rec
);
654 ndr_err
= ndr_push_struct_blob(&blob
, rec
, array
,
655 (ndr_push_flags_fn_t
)ndr_push_notify_entry_array
);
656 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
657 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
658 ndr_errstr(ndr_err
)));
660 return ndr_map_error2ntstatus(ndr_err
);
663 if (DEBUGLEVEL
>= 10) {
664 DEBUG(10, ("notify_add_onelevel:\n"));
665 NDR_PRINT_DEBUG(notify_entry_array
, array
);
668 dbuf
.dptr
= blob
.data
;
669 dbuf
.dsize
= blob
.length
;
671 status
= dbwrap_record_store(rec
, dbuf
, TDB_REPLACE
);
673 if (!NT_STATUS_IS_OK(status
)) {
674 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
682 remove a notify watch. Called when the directory handle is closed
684 NTSTATUS
notify_remove(struct notify_context
*notify
, void *private_data
)
687 struct notify_list
*listel
;
689 struct notify_depth
*d
;
690 struct db_record
*rec
;
692 /* see if change notify is enabled at all */
693 if (notify
== NULL
) {
694 return NT_STATUS_NOT_IMPLEMENTED
;
697 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
698 if (listel
->private_data
== private_data
) {
699 DLIST_REMOVE(notify
->list
, listel
);
703 if (listel
== NULL
) {
704 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
707 depth
= listel
->depth
;
711 status
= notify_fetch_locked(notify
, &rec
);
712 NT_STATUS_NOT_OK_RETURN(status
);
714 status
= notify_load(notify
, rec
);
715 if (!NT_STATUS_IS_OK(status
)) {
720 if (depth
>= notify
->array
->num_depths
) {
722 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
725 /* we only have to search at the depth of this element */
726 d
= ¬ify
->array
->depth
[depth
];
728 for (i
=0;i
<d
->num_entries
;i
++) {
729 if (private_data
== d
->entries
[i
].private_data
&&
730 cluster_id_equal(¬ify
->server
, &d
->entries
[i
].server
)) {
734 if (i
== d
->num_entries
) {
736 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
739 if (i
< d
->num_entries
-1) {
740 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
741 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
745 status
= notify_save(notify
, rec
);
753 remove all notify watches for a messaging server
755 static NTSTATUS
notify_remove_all(struct notify_context
*notify
,
756 const struct server_id
*server
)
759 int i
, depth
, del_count
=0;
760 struct db_record
*rec
;
762 status
= notify_fetch_locked(notify
, &rec
);
763 NT_STATUS_NOT_OK_RETURN(status
);
765 status
= notify_load(notify
, rec
);
766 if (!NT_STATUS_IS_OK(status
)) {
771 /* we have to search for all entries across all depths, looking for matches
773 for (depth
=0;depth
<notify
->array
->num_depths
;depth
++) {
774 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
775 for (i
=0;i
<d
->num_entries
;i
++) {
776 if (cluster_id_equal(server
, &d
->entries
[i
].server
)) {
777 if (i
< d
->num_entries
-1) {
778 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
779 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
789 status
= notify_save(notify
, rec
);
799 send a notify message to another messaging server
801 static NTSTATUS
notify_send(struct notify_context
*notify
, struct notify_entry
*e
,
802 const char *path
, uint32_t action
)
804 struct notify_event ev
;
807 enum ndr_err_code ndr_err
;
812 ev
.private_data
= e
->private_data
;
814 tmp_ctx
= talloc_new(notify
);
816 ndr_err
= ndr_push_struct_blob(&data
, tmp_ctx
, &ev
,
817 (ndr_push_flags_fn_t
)ndr_push_notify_event
);
818 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
819 talloc_free(tmp_ctx
);
820 return ndr_map_error2ntstatus(ndr_err
);
823 status
= messaging_send(notify
->messaging_ctx
, e
->server
,
824 MSG_PVFS_NOTIFY
, &data
);
825 talloc_free(tmp_ctx
);
829 void notify_onelevel(struct notify_context
*notify
, uint32_t action
,
830 uint32_t filter
, struct file_id fid
, const char *name
)
832 struct notify_entry_array
*array
;
835 bool have_dead_entries
= false;
839 if (notify
== NULL
) {
843 array
= talloc_zero(talloc_tos(), struct notify_entry_array
);
848 status
= dbwrap_fetch(notify
->db_onelevel
, array
,
849 make_tdb_data((uint8_t *)&fid
, sizeof(fid
)),
851 if (!NT_STATUS_IS_OK(status
)) {
856 blob
.data
= (uint8
*)dbuf
.dptr
;
857 blob
.length
= dbuf
.dsize
;
859 if (blob
.length
> 0) {
860 enum ndr_err_code ndr_err
;
861 ndr_err
= ndr_pull_struct_blob(&blob
, array
, array
,
862 (ndr_pull_flags_fn_t
)ndr_pull_notify_entry_array
);
863 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
864 DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
865 ndr_errstr(ndr_err
)));
869 if (DEBUGLEVEL
>= 10) {
870 DEBUG(10, ("notify_onelevel:\n"));
871 NDR_PRINT_DEBUG(notify_entry_array
, array
);
875 for (i
=0; i
<array
->num_entries
; i
++) {
876 struct notify_entry
*e
= &array
->entries
[i
];
878 if ((e
->filter
& filter
) != 0) {
879 status
= notify_send(notify
, e
, name
, action
);
881 status
, NT_STATUS_INVALID_HANDLE
)) {
883 * Mark the entry as dead. All entries have a
884 * path set. The marker used here is setting
888 have_dead_entries
= true;
893 if (!have_dead_entries
) {
898 for (i
=0; i
<array
->num_entries
; i
++) {
899 struct notify_entry
*e
= &array
->entries
[i
];
900 if (e
->path
!= NULL
) {
903 DEBUG(10, ("Deleting notify entries for process %s because "
904 "it's gone\n", procid_str_static(&e
->server
)));
906 * Potential TODO: This might need optimizing,
907 * notify_remove_onelevel() does a fetch_locked() operation at
908 * every call. But this would only matter if a process with
909 * MANY notifies has died without shutting down properly.
911 notify_remove_onelevel(notify
, &e
->dir_id
, e
->private_data
);
919 trigger a notify message for anyone waiting on a matching event
921 This function is called a lot, and needs to be very fast. The unusual data structure
922 and traversal is designed to be fast in the average case, even for large numbers of
925 void notify_trigger(struct notify_context
*notify
,
926 uint32_t action
, uint32_t filter
, const char *path
)
930 const char *p
, *next_p
;
932 DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
933 "path=%s\n", (unsigned)action
, (unsigned)filter
, path
));
935 /* see if change notify is enabled at all */
936 if (notify
== NULL
) {
941 status
= notify_load(notify
, NULL
);
942 if (!NT_STATUS_IS_OK(status
)) {
946 /* loop along the given path, working with each directory depth separately */
948 p
&& depth
< notify
->array
->num_depths
;
950 int p_len
= p
- path
;
952 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
953 next_p
= strchr(p
+1, '/');
955 /* see if there are any entries at this depth */
956 if (d
->num_entries
== 0) continue;
958 /* try to skip based on the maximum mask. If next_p is
959 NULL then we know it will be a 'this directory'
960 match, otherwise it must be a subdir match */
961 if (next_p
!= NULL
) {
962 if (0 == (filter
& d
->max_mask_subdir
)) {
966 if (0 == (filter
& d
->max_mask
)) {
971 /* we know there is an entry here worth looking
972 for. Use a bisection search to find the first entry
973 with a matching path */
975 max_i
= d
->num_entries
-1;
977 while (min_i
< max_i
) {
978 struct notify_entry
*e
;
982 cmp
= strncmp(path
, e
->path
, p_len
);
984 if (p_len
== e
->path_len
) {
989 } else if (cmp
< 0) {
996 if (min_i
!= max_i
) {
1001 /* we now know that the entries start at min_i */
1002 for (i
=min_i
;i
<d
->num_entries
;i
++) {
1003 struct notify_entry
*e
= &d
->entries
[i
];
1004 if (p_len
!= e
->path_len
||
1005 strncmp(path
, e
->path
, p_len
) != 0) break;
1006 if (next_p
!= NULL
) {
1007 if (0 == (filter
& e
->subdir_filter
)) {
1011 if (0 == (filter
& e
->filter
)) {
1015 status
= notify_send(notify
, e
, path
+ e
->path_len
+ 1,
1018 if (NT_STATUS_EQUAL(
1019 status
, NT_STATUS_INVALID_HANDLE
)) {
1020 struct server_id server
= e
->server
;
1022 DEBUG(10, ("Deleting notify entries for "
1023 "process %s because it's gone\n",
1024 procid_str_static(&e
->server
)));
1025 notify_remove_all(notify
, &server
);