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 "../lib/tdb_compat/tdb_compat.h"
29 #include "../lib/util/util_tdb.h"
30 #include "messaging/messaging.h"
31 #include "lib/tdb_wrap/tdb_wrap.h"
32 #include "lib/messaging/irpc.h"
33 #include "librpc/gen_ndr/ndr_notify.h"
34 #include "../lib/util/dlinklist.h"
35 #include "ntvfs/common/ntvfs_common.h"
36 #include "ntvfs/sysdep/sys_notify.h"
37 #include "cluster/cluster.h"
38 #include "param/param.h"
39 #include "lib/util/tsort.h"
41 struct notify_context
{
43 struct server_id server
;
44 struct imessaging_context
*imessaging_ctx
;
45 struct notify_list
*list
;
46 struct notify_array
*array
;
48 struct sys_notify_context
*sys_notify_ctx
;
53 struct notify_list
*next
, *prev
;
55 void (*callback
)(void *, const struct notify_event
*);
56 void *sys_notify_handle
;
60 #define NOTIFY_KEY "notify array"
62 #define NOTIFY_ENABLE "notify:enable"
63 #define NOTIFY_ENABLE_DEFAULT true
65 static NTSTATUS
notify_remove_all(struct notify_context
*notify
);
66 static void notify_handler(struct imessaging_context
*msg_ctx
, void *private_data
,
67 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
);
70 destroy the notify context
72 static int notify_destructor(struct notify_context
*notify
)
74 imessaging_deregister(notify
->imessaging_ctx
, MSG_PVFS_NOTIFY
, notify
);
75 notify_remove_all(notify
);
80 Open up the notify.tdb database. You should close it down using
81 talloc_free(). We need the imessaging_ctx to allow for notifications
84 struct notify_context
*notify_init(TALLOC_CTX
*mem_ctx
, struct server_id server
,
85 struct imessaging_context
*imessaging_ctx
,
86 struct loadparm_context
*lp_ctx
,
87 struct tevent_context
*ev
,
88 struct share_config
*scfg
)
90 struct notify_context
*notify
;
92 if (share_bool_option(scfg
, NOTIFY_ENABLE
, NOTIFY_ENABLE_DEFAULT
) != true) {
100 notify
= talloc(mem_ctx
, struct notify_context
);
101 if (notify
== NULL
) {
105 notify
->w
= cluster_tdb_tmp_open(notify
, lp_ctx
, "notify.tdb", TDB_SEQNUM
);
106 if (notify
->w
== NULL
) {
111 notify
->server
= server
;
112 notify
->imessaging_ctx
= imessaging_ctx
;
114 notify
->array
= NULL
;
115 notify
->seqnum
= tdb_get_seqnum(notify
->w
->tdb
);
117 talloc_set_destructor(notify
, notify_destructor
);
119 /* register with the messaging subsystem for the notify
121 imessaging_register(notify
->imessaging_ctx
, notify
,
122 MSG_PVFS_NOTIFY
, notify_handler
);
124 notify
->sys_notify_ctx
= sys_notify_context_create(scfg
, notify
, ev
);
133 static NTSTATUS
notify_lock(struct notify_context
*notify
)
135 if (tdb_lock_bystring(notify
->w
->tdb
, NOTIFY_KEY
) != 0) {
136 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
144 static void notify_unlock(struct notify_context
*notify
)
146 tdb_unlock_bystring(notify
->w
->tdb
, NOTIFY_KEY
);
150 load the notify array
152 static NTSTATUS
notify_load(struct notify_context
*notify
)
156 enum ndr_err_code ndr_err
;
159 seqnum
= tdb_get_seqnum(notify
->w
->tdb
);
161 if (seqnum
== notify
->seqnum
&& notify
->array
!= NULL
) {
165 notify
->seqnum
= seqnum
;
167 talloc_free(notify
->array
);
168 notify
->array
= talloc_zero(notify
, struct notify_array
);
169 NT_STATUS_HAVE_NO_MEMORY(notify
->array
);
171 dbuf
= tdb_fetch_bystring(notify
->w
->tdb
, NOTIFY_KEY
);
172 if (dbuf
.dptr
== NULL
) {
176 blob
.data
= dbuf
.dptr
;
177 blob
.length
= dbuf
.dsize
;
179 ndr_err
= ndr_pull_struct_blob(&blob
, notify
->array
, notify
->array
,
180 (ndr_pull_flags_fn_t
)ndr_pull_notify_array
);
182 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
183 return ndr_map_error2ntstatus(ndr_err
);
190 compare notify entries for sorting
192 static int notify_compare(const void *p1
, const void *p2
)
194 const struct notify_entry
*e1
= p1
, *e2
= p2
;
195 return strcmp(e1
->path
, e2
->path
);
199 save the notify array
201 static NTSTATUS
notify_save(struct notify_context
*notify
)
205 enum ndr_err_code ndr_err
;
209 /* if possible, remove some depth arrays */
210 while (notify
->array
->num_depths
> 0 &&
211 notify
->array
->depth
[notify
->array
->num_depths
-1].num_entries
== 0) {
212 notify
->array
->num_depths
--;
215 /* we might just be able to delete the record */
216 if (notify
->array
->num_depths
== 0) {
217 ret
= tdb_delete_bystring(notify
->w
->tdb
, NOTIFY_KEY
);
219 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
224 tmp_ctx
= talloc_new(notify
);
225 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx
);
227 ndr_err
= ndr_push_struct_blob(&blob
, tmp_ctx
, notify
->array
,
228 (ndr_push_flags_fn_t
)ndr_push_notify_array
);
229 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
230 talloc_free(tmp_ctx
);
231 return ndr_map_error2ntstatus(ndr_err
);
234 dbuf
.dptr
= blob
.data
;
235 dbuf
.dsize
= blob
.length
;
237 ret
= tdb_store_bystring(notify
->w
->tdb
, NOTIFY_KEY
, dbuf
, TDB_REPLACE
);
238 talloc_free(tmp_ctx
);
240 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
248 handle incoming notify messages
250 static void notify_handler(struct imessaging_context
*msg_ctx
, void *private_data
,
251 uint32_t msg_type
, struct server_id server_id
, DATA_BLOB
*data
)
253 struct notify_context
*notify
= talloc_get_type(private_data
, struct notify_context
);
254 enum ndr_err_code ndr_err
;
255 struct notify_event ev
;
256 TALLOC_CTX
*tmp_ctx
= talloc_new(notify
);
257 struct notify_list
*listel
;
259 if (tmp_ctx
== NULL
) {
263 ndr_err
= ndr_pull_struct_blob(data
, tmp_ctx
, &ev
,
264 (ndr_pull_flags_fn_t
)ndr_pull_notify_event
);
265 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
266 talloc_free(tmp_ctx
);
270 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
271 if (listel
->private_data
== ev
.private_data
) {
272 listel
->callback(listel
->private_data
, &ev
);
277 talloc_free(tmp_ctx
);
281 callback from sys_notify telling us about changes from the OS
283 static void sys_notify_callback(struct sys_notify_context
*ctx
,
284 void *ptr
, struct notify_event
*ev
)
286 struct notify_list
*listel
= talloc_get_type(ptr
, struct notify_list
);
287 ev
->private_data
= listel
;
288 listel
->callback(listel
->private_data
, ev
);
292 add an entry to the notify array
294 static NTSTATUS
notify_add_array(struct notify_context
*notify
, struct notify_entry
*e
,
295 void *private_data
, int depth
)
298 struct notify_depth
*d
;
299 struct notify_entry
*ee
;
301 /* possibly expand the depths array */
302 if (depth
>= notify
->array
->num_depths
) {
303 d
= talloc_realloc(notify
->array
, notify
->array
->depth
,
304 struct notify_depth
, depth
+1);
305 NT_STATUS_HAVE_NO_MEMORY(d
);
306 for (i
=notify
->array
->num_depths
;i
<=depth
;i
++) {
309 notify
->array
->depth
= d
;
310 notify
->array
->num_depths
= depth
+1;
312 d
= ¬ify
->array
->depth
[depth
];
314 /* expand the entries array */
315 ee
= talloc_realloc(notify
->array
->depth
, d
->entries
, struct notify_entry
,
317 NT_STATUS_HAVE_NO_MEMORY(ee
);
320 d
->entries
[d
->num_entries
] = *e
;
321 d
->entries
[d
->num_entries
].private_data
= private_data
;
322 d
->entries
[d
->num_entries
].server
= notify
->server
;
323 d
->entries
[d
->num_entries
].path_len
= strlen(e
->path
);
326 d
->max_mask
|= e
->filter
;
327 d
->max_mask_subdir
|= e
->subdir_filter
;
329 TYPESAFE_QSORT(d
->entries
, d
->num_entries
, notify_compare
);
331 /* recalculate the maximum masks */
333 d
->max_mask_subdir
= 0;
335 for (i
=0;i
<d
->num_entries
;i
++) {
336 d
->max_mask
|= d
->entries
[i
].filter
;
337 d
->max_mask_subdir
|= d
->entries
[i
].subdir_filter
;
340 return notify_save(notify
);
344 add a notify watch. This is called when a notify is first setup on a open
347 NTSTATUS
notify_add(struct notify_context
*notify
, struct notify_entry
*e0
,
348 void (*callback
)(void *, const struct notify_event
*),
351 struct notify_entry e
= *e0
;
353 char *tmp_path
= NULL
;
354 struct notify_list
*listel
;
358 /* see if change notify is enabled at all */
359 if (notify
== NULL
) {
360 return NT_STATUS_NOT_IMPLEMENTED
;
363 status
= notify_lock(notify
);
364 NT_STATUS_NOT_OK_RETURN(status
);
366 status
= notify_load(notify
);
367 if (!NT_STATUS_IS_OK(status
)) {
371 /* cope with /. on the end of the path */
372 len
= strlen(e
.path
);
373 if (len
> 1 && e
.path
[len
-1] == '.' && e
.path
[len
-2] == '/') {
374 tmp_path
= talloc_strndup(notify
, e
.path
, len
-2);
375 if (tmp_path
== NULL
) {
376 status
= NT_STATUS_NO_MEMORY
;
382 depth
= count_chars(e
.path
, '/');
384 listel
= talloc_zero(notify
, struct notify_list
);
385 if (listel
== NULL
) {
386 status
= NT_STATUS_NO_MEMORY
;
390 listel
->private_data
= private_data
;
391 listel
->callback
= callback
;
392 listel
->depth
= depth
;
393 DLIST_ADD(notify
->list
, listel
);
395 /* ignore failures from sys_notify */
396 if (notify
->sys_notify_ctx
!= NULL
) {
398 this call will modify e.filter and e.subdir_filter
399 to remove bits handled by the backend
401 status
= sys_notify_watch(notify
->sys_notify_ctx
, &e
,
402 sys_notify_callback
, listel
,
403 &listel
->sys_notify_handle
);
404 if (NT_STATUS_IS_OK(status
)) {
405 talloc_steal(listel
, listel
->sys_notify_handle
);
409 /* if the system notify handler couldn't handle some of the
410 filter bits, or couldn't handle a request for recursion
411 then we need to install it in the array used for the
412 intra-samba notify handling */
413 if (e
.filter
!= 0 || e
.subdir_filter
!= 0) {
414 status
= notify_add_array(notify
, &e
, private_data
, depth
);
418 notify_unlock(notify
);
419 talloc_free(tmp_path
);
425 remove a notify watch. Called when the directory handle is closed
427 NTSTATUS
notify_remove(struct notify_context
*notify
, void *private_data
)
430 struct notify_list
*listel
;
432 struct notify_depth
*d
;
434 /* see if change notify is enabled at all */
435 if (notify
== NULL
) {
436 return NT_STATUS_NOT_IMPLEMENTED
;
439 for (listel
=notify
->list
;listel
;listel
=listel
->next
) {
440 if (listel
->private_data
== private_data
) {
441 DLIST_REMOVE(notify
->list
, listel
);
445 if (listel
== NULL
) {
446 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
449 depth
= listel
->depth
;
453 status
= notify_lock(notify
);
454 NT_STATUS_NOT_OK_RETURN(status
);
456 status
= notify_load(notify
);
457 if (!NT_STATUS_IS_OK(status
)) {
458 notify_unlock(notify
);
462 if (depth
>= notify
->array
->num_depths
) {
463 notify_unlock(notify
);
464 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
467 /* we only have to search at the depth of this element */
468 d
= ¬ify
->array
->depth
[depth
];
470 for (i
=0;i
<d
->num_entries
;i
++) {
471 if (private_data
== d
->entries
[i
].private_data
&&
472 cluster_id_equal(¬ify
->server
, &d
->entries
[i
].server
)) {
476 if (i
== d
->num_entries
) {
477 notify_unlock(notify
);
478 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
481 if (i
< d
->num_entries
-1) {
482 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
483 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
487 status
= notify_save(notify
);
489 notify_unlock(notify
);
495 remove all notify watches for this messaging server
497 static NTSTATUS
notify_remove_all(struct notify_context
*notify
)
500 int i
, depth
, del_count
=0;
502 if (notify
->list
== NULL
) {
506 status
= notify_lock(notify
);
507 NT_STATUS_NOT_OK_RETURN(status
);
509 status
= notify_load(notify
);
510 if (!NT_STATUS_IS_OK(status
)) {
511 notify_unlock(notify
);
515 /* we have to search for all entries across all depths, looking for matches
517 for (depth
=0;depth
<notify
->array
->num_depths
;depth
++) {
518 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
519 for (i
=0;i
<d
->num_entries
;i
++) {
520 if (cluster_id_equal(¬ify
->server
, &d
->entries
[i
].server
)) {
521 if (i
< d
->num_entries
-1) {
522 memmove(&d
->entries
[i
], &d
->entries
[i
+1],
523 sizeof(d
->entries
[i
])*(d
->num_entries
-(i
+1)));
533 status
= notify_save(notify
);
536 notify_unlock(notify
);
543 send a notify message to another messaging server
545 static void notify_send(struct notify_context
*notify
, struct notify_entry
*e
,
546 const char *path
, uint32_t action
)
548 struct notify_event ev
;
551 enum ndr_err_code ndr_err
;
556 ev
.private_data
= e
->private_data
;
558 tmp_ctx
= talloc_new(notify
);
560 ndr_err
= ndr_push_struct_blob(&data
, tmp_ctx
, &ev
, (ndr_push_flags_fn_t
)ndr_push_notify_event
);
561 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
562 talloc_free(tmp_ctx
);
566 status
= imessaging_send(notify
->imessaging_ctx
, e
->server
,
567 MSG_PVFS_NOTIFY
, &data
);
568 talloc_free(tmp_ctx
);
573 trigger a notify message for anyone waiting on a matching event
575 This function is called a lot, and needs to be very fast. The unusual data structure
576 and traversal is designed to be fast in the average case, even for large numbers of
579 void notify_trigger(struct notify_context
*notify
,
580 uint32_t action
, uint32_t filter
, const char *path
)
584 const char *p
, *next_p
;
586 /* see if change notify is enabled at all */
587 if (notify
== NULL
) {
591 status
= notify_load(notify
);
592 if (!NT_STATUS_IS_OK(status
)) {
596 /* loop along the given path, working with each directory depth separately */
598 p
&& depth
< notify
->array
->num_depths
;
600 int p_len
= p
- path
;
602 struct notify_depth
*d
= ¬ify
->array
->depth
[depth
];
603 next_p
= strchr(p
+1, '/');
605 /* see if there are any entries at this depth */
606 if (d
->num_entries
== 0) continue;
608 /* try to skip based on the maximum mask. If next_p is
609 NULL then we know it will be a 'this directory'
610 match, otherwise it must be a subdir match */
611 if (next_p
!= NULL
) {
612 if (0 == (filter
& d
->max_mask_subdir
)) {
616 if (0 == (filter
& d
->max_mask
)) {
621 /* we know there is an entry here worth looking
622 for. Use a bisection search to find the first entry
623 with a matching path */
625 max_i
= d
->num_entries
-1;
627 while (min_i
< max_i
) {
628 struct notify_entry
*e
;
632 cmp
= strncmp(path
, e
->path
, p_len
);
634 if (p_len
== e
->path_len
) {
639 } else if (cmp
< 0) {
646 if (min_i
!= max_i
) {
651 /* we now know that the entries start at min_i */
652 for (i
=min_i
;i
<d
->num_entries
;i
++) {
653 struct notify_entry
*e
= &d
->entries
[i
];
654 if (p_len
!= e
->path_len
||
655 strncmp(path
, e
->path
, p_len
) != 0) break;
656 if (next_p
!= NULL
) {
657 if (0 == (filter
& e
->subdir_filter
)) {
661 if (0 == (filter
& e
->filter
)) {
665 notify_send(notify
, e
, path
+ e
->path_len
+ 1, action
);