s3: Slightly simplify is_stat_open
[Samba/gebeck_regimport.git] / source4 / ntvfs / common / notify.c
blob6b5ece9e4ec6c855b5dfb0dc799caf2aee38eac1
1 /*
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.
26 #include "includes.h"
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 {
42 struct tdb_wrap *w;
43 struct server_id server;
44 struct imessaging_context *imessaging_ctx;
45 struct notify_list *list;
46 struct notify_array *array;
47 int seqnum;
48 struct sys_notify_context *sys_notify_ctx;
52 struct notify_list {
53 struct notify_list *next, *prev;
54 void *private_data;
55 void (*callback)(void *, const struct notify_event *);
56 void *sys_notify_handle;
57 int depth;
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);
76 return 0;
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
82 via internal messages
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) {
93 return NULL;
96 if (ev == NULL) {
97 return NULL;
100 notify = talloc(mem_ctx, struct notify_context);
101 if (notify == NULL) {
102 return NULL;
105 notify->w = cluster_tdb_tmp_open(notify, lp_ctx, "notify.tdb", TDB_SEQNUM);
106 if (notify->w == NULL) {
107 talloc_free(notify);
108 return NULL;
111 notify->server = server;
112 notify->imessaging_ctx = imessaging_ctx;
113 notify->list = NULL;
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
120 message type */
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);
126 return notify;
131 lock the notify db
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;
138 return NT_STATUS_OK;
142 unlock the notify db
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)
154 TDB_DATA dbuf;
155 DATA_BLOB blob;
156 enum ndr_err_code ndr_err;
157 int seqnum;
159 seqnum = tdb_get_seqnum(notify->w->tdb);
161 if (seqnum == notify->seqnum && notify->array != NULL) {
162 return NT_STATUS_OK;
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) {
173 return NT_STATUS_OK;
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);
181 free(dbuf.dptr);
182 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
183 return ndr_map_error2ntstatus(ndr_err);
186 return NT_STATUS_OK;
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)
203 TDB_DATA dbuf;
204 DATA_BLOB blob;
205 enum ndr_err_code ndr_err;
206 int ret;
207 TALLOC_CTX *tmp_ctx;
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);
218 if (ret != 0) {
219 return NT_STATUS_INTERNAL_DB_CORRUPTION;
221 return NT_STATUS_OK;
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);
239 if (ret != 0) {
240 return NT_STATUS_INTERNAL_DB_CORRUPTION;
243 return NT_STATUS_OK;
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) {
260 return;
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);
267 return;
270 for (listel=notify->list;listel;listel=listel->next) {
271 if (listel->private_data == ev.private_data) {
272 listel->callback(listel->private_data, &ev);
273 break;
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)
297 int i;
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++) {
307 ZERO_STRUCT(d[i]);
309 notify->array->depth = d;
310 notify->array->num_depths = depth+1;
312 d = &notify->array->depth[depth];
314 /* expand the entries array */
315 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
316 d->num_entries+1);
317 NT_STATUS_HAVE_NO_MEMORY(ee);
318 d->entries = 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);
324 d->num_entries++;
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 */
332 d->max_mask = 0;
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
345 directory handle.
347 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
348 void (*callback)(void *, const struct notify_event *),
349 void *private_data)
351 struct notify_entry e = *e0;
352 NTSTATUS status;
353 char *tmp_path = NULL;
354 struct notify_list *listel;
355 size_t len;
356 int depth;
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)) {
368 goto done;
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;
377 goto done;
379 e.path = tmp_path;
382 depth = count_chars(e.path, '/');
384 listel = talloc_zero(notify, struct notify_list);
385 if (listel == NULL) {
386 status = NT_STATUS_NO_MEMORY;
387 goto done;
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);
417 done:
418 notify_unlock(notify);
419 talloc_free(tmp_path);
421 return status;
425 remove a notify watch. Called when the directory handle is closed
427 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
429 NTSTATUS status;
430 struct notify_list *listel;
431 int i, depth;
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);
442 break;
445 if (listel == NULL) {
446 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
449 depth = listel->depth;
451 talloc_free(listel);
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);
459 return status;
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 = &notify->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(&notify->server, &d->entries[i].server)) {
473 break;
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)));
485 d->num_entries--;
487 status = notify_save(notify);
489 notify_unlock(notify);
491 return status;
495 remove all notify watches for this messaging server
497 static NTSTATUS notify_remove_all(struct notify_context *notify)
499 NTSTATUS status;
500 int i, depth, del_count=0;
502 if (notify->list == NULL) {
503 return NT_STATUS_OK;
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);
512 return status;
515 /* we have to search for all entries across all depths, looking for matches
516 for our server id */
517 for (depth=0;depth<notify->array->num_depths;depth++) {
518 struct notify_depth *d = &notify->array->depth[depth];
519 for (i=0;i<d->num_entries;i++) {
520 if (cluster_id_equal(&notify->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)));
525 i--;
526 d->num_entries--;
527 del_count++;
532 if (del_count > 0) {
533 status = notify_save(notify);
536 notify_unlock(notify);
538 return status;
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;
549 DATA_BLOB data;
550 NTSTATUS status;
551 enum ndr_err_code ndr_err;
552 TALLOC_CTX *tmp_ctx;
554 ev.action = action;
555 ev.path = path;
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);
563 return;
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
577 notifies
579 void notify_trigger(struct notify_context *notify,
580 uint32_t action, uint32_t filter, const char *path)
582 NTSTATUS status;
583 int depth;
584 const char *p, *next_p;
586 /* see if change notify is enabled at all */
587 if (notify == NULL) {
588 return;
591 status = notify_load(notify);
592 if (!NT_STATUS_IS_OK(status)) {
593 return;
596 /* loop along the given path, working with each directory depth separately */
597 for (depth=0,p=path;
598 p && depth < notify->array->num_depths;
599 p=next_p,depth++) {
600 int p_len = p - path;
601 int min_i, max_i, i;
602 struct notify_depth *d = &notify->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)) {
613 continue;
615 } else {
616 if (0 == (filter & d->max_mask)) {
617 continue;
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 */
624 min_i = 0;
625 max_i = d->num_entries-1;
627 while (min_i < max_i) {
628 struct notify_entry *e;
629 int cmp;
630 i = (min_i+max_i)/2;
631 e = &d->entries[i];
632 cmp = strncmp(path, e->path, p_len);
633 if (cmp == 0) {
634 if (p_len == e->path_len) {
635 max_i = i;
636 } else {
637 max_i = i-1;
639 } else if (cmp < 0) {
640 max_i = i-1;
641 } else {
642 min_i = i+1;
646 if (min_i != max_i) {
647 /* none match */
648 continue;
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)) {
658 continue;
660 } else {
661 if (0 == (filter & e->filter)) {
662 continue;
665 notify_send(notify, e, path + e->path_len + 1, action);