Oops. Don't break the build..
[Samba.git] / source4 / ntvfs / common / notify.c
blob135458a00fd88371abc2fd28d49b509e4c74d5b2
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 "../tdb/include/tdb.h"
29 #include "../lib/util/util_tdb.h"
30 #include "messaging/messaging.h"
31 #include "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"
40 struct notify_context {
41 struct tdb_wrap *w;
42 struct server_id server;
43 struct messaging_context *messaging_ctx;
44 struct notify_list *list;
45 struct notify_array *array;
46 int seqnum;
47 struct sys_notify_context *sys_notify_ctx;
48 struct smb_iconv_convenience *iconv_convenience;
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 messaging_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 messaging_deregister(notify->messaging_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 messaging_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 messaging_context *messaging_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->messaging_ctx = messaging_ctx;
113 notify->list = NULL;
114 notify->array = NULL;
115 notify->iconv_convenience = lp_iconv_convenience(lp_ctx);
116 notify->seqnum = tdb_get_seqnum(notify->w->tdb);
118 talloc_set_destructor(notify, notify_destructor);
120 /* register with the messaging subsystem for the notify
121 message type */
122 messaging_register(notify->messaging_ctx, notify,
123 MSG_PVFS_NOTIFY, notify_handler);
125 notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
127 return notify;
132 lock the notify db
134 static NTSTATUS notify_lock(struct notify_context *notify)
136 if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
137 return NT_STATUS_INTERNAL_DB_CORRUPTION;
139 return NT_STATUS_OK;
143 unlock the notify db
145 static void notify_unlock(struct notify_context *notify)
147 tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
151 load the notify array
153 static NTSTATUS notify_load(struct notify_context *notify)
155 TDB_DATA dbuf;
156 DATA_BLOB blob;
157 enum ndr_err_code ndr_err;
158 int seqnum;
160 seqnum = tdb_get_seqnum(notify->w->tdb);
162 if (seqnum == notify->seqnum && notify->array != NULL) {
163 return NT_STATUS_OK;
166 notify->seqnum = seqnum;
168 talloc_free(notify->array);
169 notify->array = talloc_zero(notify, struct notify_array);
170 NT_STATUS_HAVE_NO_MEMORY(notify->array);
172 dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
173 if (dbuf.dptr == NULL) {
174 return NT_STATUS_OK;
177 blob.data = dbuf.dptr;
178 blob.length = dbuf.dsize;
180 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->iconv_convenience,
181 notify->array,
182 (ndr_pull_flags_fn_t)ndr_pull_notify_array);
183 free(dbuf.dptr);
184 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
185 return ndr_map_error2ntstatus(ndr_err);
188 return NT_STATUS_OK;
192 compare notify entries for sorting
194 static int notify_compare(const void *p1, const void *p2)
196 const struct notify_entry *e1 = p1, *e2 = p2;
197 return strcmp(e1->path, e2->path);
201 save the notify array
203 static NTSTATUS notify_save(struct notify_context *notify)
205 TDB_DATA dbuf;
206 DATA_BLOB blob;
207 enum ndr_err_code ndr_err;
208 int ret;
209 TALLOC_CTX *tmp_ctx;
211 /* if possible, remove some depth arrays */
212 while (notify->array->num_depths > 0 &&
213 notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
214 notify->array->num_depths--;
217 /* we might just be able to delete the record */
218 if (notify->array->num_depths == 0) {
219 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
220 if (ret != 0) {
221 return NT_STATUS_INTERNAL_DB_CORRUPTION;
223 return NT_STATUS_OK;
226 tmp_ctx = talloc_new(notify);
227 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
229 ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->iconv_convenience, notify->array,
230 (ndr_push_flags_fn_t)ndr_push_notify_array);
231 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
232 talloc_free(tmp_ctx);
233 return ndr_map_error2ntstatus(ndr_err);
236 dbuf.dptr = blob.data;
237 dbuf.dsize = blob.length;
239 ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
240 talloc_free(tmp_ctx);
241 if (ret != 0) {
242 return NT_STATUS_INTERNAL_DB_CORRUPTION;
245 return NT_STATUS_OK;
250 handle incoming notify messages
252 static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
253 uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
255 struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
256 enum ndr_err_code ndr_err;
257 struct notify_event ev;
258 TALLOC_CTX *tmp_ctx = talloc_new(notify);
259 struct notify_list *listel;
261 if (tmp_ctx == NULL) {
262 return;
265 ndr_err = ndr_pull_struct_blob(data, tmp_ctx, notify->iconv_convenience, &ev,
266 (ndr_pull_flags_fn_t)ndr_pull_notify_event);
267 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
268 talloc_free(tmp_ctx);
269 return;
272 for (listel=notify->list;listel;listel=listel->next) {
273 if (listel->private_data == ev.private_data) {
274 listel->callback(listel->private_data, &ev);
275 break;
279 talloc_free(tmp_ctx);
283 callback from sys_notify telling us about changes from the OS
285 static void sys_notify_callback(struct sys_notify_context *ctx,
286 void *ptr, struct notify_event *ev)
288 struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
289 ev->private_data = listel;
290 listel->callback(listel->private_data, ev);
294 add an entry to the notify array
296 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
297 void *private_data, int depth)
299 int i;
300 struct notify_depth *d;
301 struct notify_entry *ee;
303 /* possibly expand the depths array */
304 if (depth >= notify->array->num_depths) {
305 d = talloc_realloc(notify->array, notify->array->depth,
306 struct notify_depth, depth+1);
307 NT_STATUS_HAVE_NO_MEMORY(d);
308 for (i=notify->array->num_depths;i<=depth;i++) {
309 ZERO_STRUCT(d[i]);
311 notify->array->depth = d;
312 notify->array->num_depths = depth+1;
314 d = &notify->array->depth[depth];
316 /* expand the entries array */
317 ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
318 d->num_entries+1);
319 NT_STATUS_HAVE_NO_MEMORY(ee);
320 d->entries = ee;
322 d->entries[d->num_entries] = *e;
323 d->entries[d->num_entries].private_data = private_data;
324 d->entries[d->num_entries].server = notify->server;
325 d->entries[d->num_entries].path_len = strlen(e->path);
326 d->num_entries++;
328 d->max_mask |= e->filter;
329 d->max_mask_subdir |= e->subdir_filter;
331 if (d->num_entries > 1) {
332 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
335 /* recalculate the maximum masks */
336 d->max_mask = 0;
337 d->max_mask_subdir = 0;
339 for (i=0;i<d->num_entries;i++) {
340 d->max_mask |= d->entries[i].filter;
341 d->max_mask_subdir |= d->entries[i].subdir_filter;
344 return notify_save(notify);
348 add a notify watch. This is called when a notify is first setup on a open
349 directory handle.
351 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
352 void (*callback)(void *, const struct notify_event *),
353 void *private_data)
355 struct notify_entry e = *e0;
356 NTSTATUS status;
357 char *tmp_path = NULL;
358 struct notify_list *listel;
359 size_t len;
360 int depth;
362 /* see if change notify is enabled at all */
363 if (notify == NULL) {
364 return NT_STATUS_NOT_IMPLEMENTED;
367 status = notify_lock(notify);
368 NT_STATUS_NOT_OK_RETURN(status);
370 status = notify_load(notify);
371 if (!NT_STATUS_IS_OK(status)) {
372 goto done;
375 /* cope with /. on the end of the path */
376 len = strlen(e.path);
377 if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
378 tmp_path = talloc_strndup(notify, e.path, len-2);
379 if (tmp_path == NULL) {
380 status = NT_STATUS_NO_MEMORY;
381 goto done;
383 e.path = tmp_path;
386 depth = count_chars(e.path, '/');
388 listel = talloc_zero(notify, struct notify_list);
389 if (listel == NULL) {
390 status = NT_STATUS_NO_MEMORY;
391 goto done;
394 listel->private_data = private_data;
395 listel->callback = callback;
396 listel->depth = depth;
397 DLIST_ADD(notify->list, listel);
399 /* ignore failures from sys_notify */
400 if (notify->sys_notify_ctx != NULL) {
402 this call will modify e.filter and e.subdir_filter
403 to remove bits handled by the backend
405 status = sys_notify_watch(notify->sys_notify_ctx, &e,
406 sys_notify_callback, listel,
407 &listel->sys_notify_handle);
408 if (NT_STATUS_IS_OK(status)) {
409 talloc_steal(listel, listel->sys_notify_handle);
413 /* if the system notify handler couldn't handle some of the
414 filter bits, or couldn't handle a request for recursion
415 then we need to install it in the array used for the
416 intra-samba notify handling */
417 if (e.filter != 0 || e.subdir_filter != 0) {
418 status = notify_add_array(notify, &e, private_data, depth);
421 done:
422 notify_unlock(notify);
423 talloc_free(tmp_path);
425 return status;
429 remove a notify watch. Called when the directory handle is closed
431 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
433 NTSTATUS status;
434 struct notify_list *listel;
435 int i, depth;
436 struct notify_depth *d;
438 /* see if change notify is enabled at all */
439 if (notify == NULL) {
440 return NT_STATUS_NOT_IMPLEMENTED;
443 for (listel=notify->list;listel;listel=listel->next) {
444 if (listel->private_data == private_data) {
445 DLIST_REMOVE(notify->list, listel);
446 break;
449 if (listel == NULL) {
450 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
453 depth = listel->depth;
455 talloc_free(listel);
457 status = notify_lock(notify);
458 NT_STATUS_NOT_OK_RETURN(status);
460 status = notify_load(notify);
461 if (!NT_STATUS_IS_OK(status)) {
462 notify_unlock(notify);
463 return status;
466 if (depth >= notify->array->num_depths) {
467 notify_unlock(notify);
468 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
471 /* we only have to search at the depth of this element */
472 d = &notify->array->depth[depth];
474 for (i=0;i<d->num_entries;i++) {
475 if (private_data == d->entries[i].private_data &&
476 cluster_id_equal(&notify->server, &d->entries[i].server)) {
477 break;
480 if (i == d->num_entries) {
481 notify_unlock(notify);
482 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
485 if (i < d->num_entries-1) {
486 memmove(&d->entries[i], &d->entries[i+1],
487 sizeof(d->entries[i])*(d->num_entries-(i+1)));
489 d->num_entries--;
491 status = notify_save(notify);
493 notify_unlock(notify);
495 return status;
499 remove all notify watches for this messaging server
501 static NTSTATUS notify_remove_all(struct notify_context *notify)
503 NTSTATUS status;
504 int i, depth, del_count=0;
506 if (notify->list == NULL) {
507 return NT_STATUS_OK;
510 status = notify_lock(notify);
511 NT_STATUS_NOT_OK_RETURN(status);
513 status = notify_load(notify);
514 if (!NT_STATUS_IS_OK(status)) {
515 notify_unlock(notify);
516 return status;
519 /* we have to search for all entries across all depths, looking for matches
520 for our server id */
521 for (depth=0;depth<notify->array->num_depths;depth++) {
522 struct notify_depth *d = &notify->array->depth[depth];
523 for (i=0;i<d->num_entries;i++) {
524 if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
525 if (i < d->num_entries-1) {
526 memmove(&d->entries[i], &d->entries[i+1],
527 sizeof(d->entries[i])*(d->num_entries-(i+1)));
529 i--;
530 d->num_entries--;
531 del_count++;
536 if (del_count > 0) {
537 status = notify_save(notify);
540 notify_unlock(notify);
542 return status;
547 send a notify message to another messaging server
549 static void notify_send(struct notify_context *notify, struct notify_entry *e,
550 const char *path, uint32_t action)
552 struct notify_event ev;
553 DATA_BLOB data;
554 NTSTATUS status;
555 enum ndr_err_code ndr_err;
556 TALLOC_CTX *tmp_ctx;
558 ev.action = action;
559 ev.path = path;
560 ev.private_data = e->private_data;
562 tmp_ctx = talloc_new(notify);
564 ndr_err = ndr_push_struct_blob(&data, tmp_ctx, notify->iconv_convenience, &ev, (ndr_push_flags_fn_t)ndr_push_notify_event);
565 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
566 talloc_free(tmp_ctx);
567 return;
570 status = messaging_send(notify->messaging_ctx, e->server,
571 MSG_PVFS_NOTIFY, &data);
572 talloc_free(tmp_ctx);
577 trigger a notify message for anyone waiting on a matching event
579 This function is called a lot, and needs to be very fast. The unusual data structure
580 and traversal is designed to be fast in the average case, even for large numbers of
581 notifies
583 void notify_trigger(struct notify_context *notify,
584 uint32_t action, uint32_t filter, const char *path)
586 NTSTATUS status;
587 int depth;
588 const char *p, *next_p;
590 /* see if change notify is enabled at all */
591 if (notify == NULL) {
592 return;
595 status = notify_load(notify);
596 if (!NT_STATUS_IS_OK(status)) {
597 return;
600 /* loop along the given path, working with each directory depth separately */
601 for (depth=0,p=path;
602 p && depth < notify->array->num_depths;
603 p=next_p,depth++) {
604 int p_len = p - path;
605 int min_i, max_i, i;
606 struct notify_depth *d = &notify->array->depth[depth];
607 next_p = strchr(p+1, '/');
609 /* see if there are any entries at this depth */
610 if (d->num_entries == 0) continue;
612 /* try to skip based on the maximum mask. If next_p is
613 NULL then we know it will be a 'this directory'
614 match, otherwise it must be a subdir match */
615 if (next_p != NULL) {
616 if (0 == (filter & d->max_mask_subdir)) {
617 continue;
619 } else {
620 if (0 == (filter & d->max_mask)) {
621 continue;
625 /* we know there is an entry here worth looking
626 for. Use a bisection search to find the first entry
627 with a matching path */
628 min_i = 0;
629 max_i = d->num_entries-1;
631 while (min_i < max_i) {
632 struct notify_entry *e;
633 int cmp;
634 i = (min_i+max_i)/2;
635 e = &d->entries[i];
636 cmp = strncmp(path, e->path, p_len);
637 if (cmp == 0) {
638 if (p_len == e->path_len) {
639 max_i = i;
640 } else {
641 max_i = i-1;
643 } else if (cmp < 0) {
644 max_i = i-1;
645 } else {
646 min_i = i+1;
650 if (min_i != max_i) {
651 /* none match */
652 continue;
655 /* we now know that the entries start at min_i */
656 for (i=min_i;i<d->num_entries;i++) {
657 struct notify_entry *e = &d->entries[i];
658 if (p_len != e->path_len ||
659 strncmp(path, e->path, p_len) != 0) break;
660 if (next_p != NULL) {
661 if (0 == (filter & e->subdir_filter)) {
662 continue;
664 } else {
665 if (0 == (filter & e->filter)) {
666 continue;
669 notify_send(notify, e, path + e->path_len + 1, action);