Change to using TDB_INCOMPATIBLE_HASH (the jenkins hash) on all
[sfrench/samba-autobuild/.git] / source3 / smbd / notify_internal.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Andrew Tridgell 2006
5
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.
10
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.
15
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/>.
18 */
19
20 /*
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.
24 */
25
26 #include "includes.h"
27 #include "librpc/gen_ndr/ndr_notify.h"
28 #include "librpc/gen_ndr/messaging.h"
29 #include "dbwrap.h"
30
31 struct notify_context {
32         struct db_context *db_recursive;
33         struct db_context *db_onelevel;
34         struct server_id server;
35         struct messaging_context *messaging_ctx;
36         struct notify_list *list;
37         struct notify_array *array;
38         int seqnum;
39         struct sys_notify_context *sys_notify_ctx;
40         TDB_DATA key;
41 };
42
43
44 struct notify_list {
45         struct notify_list *next, *prev;
46         void *private_data;
47         void (*callback)(void *, const struct notify_event *);
48         void *sys_notify_handle;
49         int depth;
50 };
51
52 #define NOTIFY_KEY "notify array"
53
54 #define NOTIFY_ENABLE           "notify:enable"
55 #define NOTIFY_ENABLE_DEFAULT   True
56
57 static NTSTATUS notify_remove_all(struct notify_context *notify,
58                                   const struct server_id *server);
59 static void notify_handler(struct messaging_context *msg_ctx, void *private_data, 
60                            uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
61
62 /*
63   destroy the notify context
64 */
65 static int notify_destructor(struct notify_context *notify)
66 {
67         messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
68
69         if (notify->list != NULL) {
70                 notify_remove_all(notify, &notify->server);
71         }
72
73         return 0;
74 }
75
76 /*
77   Open up the notify.tdb database. You should close it down using
78   talloc_free(). We need the messaging_ctx to allow for notifications
79   via internal messages
80 */
81 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server, 
82                                    struct messaging_context *messaging_ctx,
83                                    struct event_context *ev,
84                                    connection_struct *conn)
85 {
86         struct notify_context *notify;
87
88         if (!lp_change_notify(conn->params)) {
89                 return NULL;
90         }
91
92         notify = talloc(mem_ctx, struct notify_context);
93         if (notify == NULL) {
94                 return NULL;
95         }
96
97         notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
98                                        0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
99                                        O_RDWR|O_CREAT, 0644);
100         if (notify->db_recursive == NULL) {
101                 talloc_free(notify);
102                 return NULL;
103         }
104
105         notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
106                                       0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
107                                       O_RDWR|O_CREAT, 0644);
108         if (notify->db_onelevel == NULL) {
109                 talloc_free(notify);
110                 return NULL;
111         }
112
113         notify->server = server;
114         notify->messaging_ctx = messaging_ctx;
115         notify->list = NULL;
116         notify->array = NULL;
117         notify->seqnum = notify->db_recursive->get_seqnum(
118                 notify->db_recursive);
119         notify->key = string_term_tdb_data(NOTIFY_KEY);
120
121         talloc_set_destructor(notify, notify_destructor);
122
123         /* register with the messaging subsystem for the notify
124            message type */
125         messaging_register(notify->messaging_ctx, notify, 
126                            MSG_PVFS_NOTIFY, notify_handler);
127
128         notify->sys_notify_ctx = sys_notify_context_create(conn, notify, ev);
129
130         return notify;
131 }
132
133 bool notify_internal_parent_init(TALLOC_CTX *mem_ctx)
134 {
135         struct tdb_wrap *db1, *db2;
136
137         if (lp_clustering()) {
138                 return true;
139         }
140
141         /*
142          * Open the tdbs in the parent process (smbd) so that our
143          * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
144          * work.
145          */
146
147         db1 = tdb_wrap_open(mem_ctx, lock_path("notify.tdb"),
148                             0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
149                            O_RDWR|O_CREAT, 0644);
150         if (db1 == NULL) {
151                 DEBUG(1, ("could not open notify.tdb: %s\n", strerror(errno)));
152                 return false;
153         }
154         db2 = tdb_wrap_open(mem_ctx, lock_path("notify_onelevel.tdb"),
155                             0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644);
156         if (db2 == NULL) {
157                 DEBUG(1, ("could not open notify_onelevel.tdb: %s\n",
158                           strerror(errno)));
159                 TALLOC_FREE(db1);
160                 return false;
161         }
162         return true;
163 }
164
165 /*
166   lock and fetch the record
167 */
168 static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
169 {
170         *rec = notify->db_recursive->fetch_locked(notify->db_recursive,
171                                                   notify, notify->key);
172         if (*rec == NULL) {
173                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
174         }
175         return NT_STATUS_OK;
176 }
177
178 /*
179   load the notify array
180 */
181 static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
182 {
183         TDB_DATA dbuf;
184         DATA_BLOB blob;
185         NTSTATUS status;
186         int seqnum;
187
188         seqnum = notify->db_recursive->get_seqnum(notify->db_recursive);
189
190         if (seqnum == notify->seqnum && notify->array != NULL) {
191                 return NT_STATUS_OK;
192         }
193
194         notify->seqnum = seqnum;
195
196         talloc_free(notify->array);
197         notify->array = TALLOC_ZERO_P(notify, struct notify_array);
198         NT_STATUS_HAVE_NO_MEMORY(notify->array);
199
200         if (!rec) {
201                 if (notify->db_recursive->fetch(notify->db_recursive, notify,
202                                                 notify->key, &dbuf) != 0) {
203                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
204                 }
205         } else {
206                 dbuf = rec->value;
207         }
208
209         blob.data = (uint8 *)dbuf.dptr;
210         blob.length = dbuf.dsize;
211
212         status = NT_STATUS_OK;
213         if (blob.length > 0) {
214                 enum ndr_err_code ndr_err;
215                 ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
216                                                (ndr_pull_flags_fn_t)ndr_pull_notify_array);
217                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
218                         /* 1. log that we got a corrupt notify_array
219                          * 2. clear the variable the garbage was stored into to not trip
220                          *  over it next time this method is entered with the same seqnum
221                          * 3. delete it from the database */
222                         DEBUG(2, ("notify_array is corrupt, discarding it\n"));
223
224                         ZERO_STRUCTP(notify->array);
225                         if (rec != NULL) {
226                                 rec->delete_rec(rec);
227                         }
228
229                 } else {
230                         if (DEBUGLEVEL >= 10) {
231                                 DEBUG(10, ("notify_load:\n"));
232                                 NDR_PRINT_DEBUG(notify_array, notify->array);
233                         }
234                 }
235         }
236
237
238         if (!rec) {
239                 talloc_free(dbuf.dptr);
240         }
241
242         return status;
243 }
244
245 /*
246   compare notify entries for sorting
247 */
248 static int notify_compare(const struct notify_entry *e1, const struct notify_entry *e2)
249 {
250         return strcmp(e1->path, e2->path);
251 }
252
253 /*
254   save the notify array
255 */
256 static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
257 {
258         TDB_DATA dbuf;
259         DATA_BLOB blob;
260         NTSTATUS status;
261         enum ndr_err_code ndr_err;
262         TALLOC_CTX *tmp_ctx;
263
264         /* if possible, remove some depth arrays */
265         while (notify->array->num_depths > 0 &&
266                notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
267                 notify->array->num_depths--;
268         }
269
270         /* we might just be able to delete the record */
271         if (notify->array->num_depths == 0) {
272                 return rec->delete_rec(rec);
273         }
274
275         tmp_ctx = talloc_new(notify);
276         NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
277
278         ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
279                                       (ndr_push_flags_fn_t)ndr_push_notify_array);
280         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
281                 talloc_free(tmp_ctx);
282                 return ndr_map_error2ntstatus(ndr_err);
283         }
284
285         if (DEBUGLEVEL >= 10) {
286                 DEBUG(10, ("notify_save:\n"));
287                 NDR_PRINT_DEBUG(notify_array, notify->array);
288         }
289
290         dbuf.dptr = blob.data;
291         dbuf.dsize = blob.length;
292
293         status = rec->store(rec, dbuf, TDB_REPLACE);
294         talloc_free(tmp_ctx);
295
296         return status;
297 }
298
299
300 /*
301   handle incoming notify messages
302 */
303 static void notify_handler(struct messaging_context *msg_ctx, void *private_data, 
304                            uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
305 {
306         struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
307         enum ndr_err_code ndr_err;
308         struct notify_event ev;
309         TALLOC_CTX *tmp_ctx = talloc_new(notify);
310         struct notify_list *listel;
311
312         if (tmp_ctx == NULL) {
313                 return;
314         }
315
316         ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
317                                        (ndr_pull_flags_fn_t)ndr_pull_notify_event);
318         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
319                 talloc_free(tmp_ctx);
320                 return;
321         }
322
323         for (listel=notify->list;listel;listel=listel->next) {
324                 if (listel->private_data == ev.private_data) {
325                         listel->callback(listel->private_data, &ev);
326                         break;
327                 }
328         }
329
330         talloc_free(tmp_ctx);   
331 }
332
333 /*
334   callback from sys_notify telling us about changes from the OS
335 */
336 static void sys_notify_callback(struct sys_notify_context *ctx, 
337                                 void *ptr, struct notify_event *ev)
338 {
339         struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
340         ev->private_data = listel;
341         DEBUG(10, ("sys_notify_callback called with action=%d, for %s\n",
342                    ev->action, ev->path));
343         listel->callback(listel->private_data, ev);
344 }
345
346 /*
347   add an entry to the notify array
348 */
349 static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
350                                  struct notify_entry *e,
351                                  void *private_data, int depth)
352 {
353         int i;
354         struct notify_depth *d;
355         struct notify_entry *ee;
356
357         /* possibly expand the depths array */
358         if (depth >= notify->array->num_depths) {
359                 d = talloc_realloc(notify->array, notify->array->depth, 
360                                    struct notify_depth, depth+1);
361                 NT_STATUS_HAVE_NO_MEMORY(d);
362                 for (i=notify->array->num_depths;i<=depth;i++) {
363                         ZERO_STRUCT(d[i]);
364                 }
365                 notify->array->depth = d;
366                 notify->array->num_depths = depth+1;
367         }
368         d = &notify->array->depth[depth];
369
370         /* expand the entries array */
371         ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
372                             d->num_entries+1);
373         NT_STATUS_HAVE_NO_MEMORY(ee);
374         d->entries = ee;
375
376         d->entries[d->num_entries] = *e;
377         d->entries[d->num_entries].private_data = private_data;
378         d->entries[d->num_entries].server = notify->server;
379         d->entries[d->num_entries].path_len = strlen(e->path);
380         d->num_entries++;
381
382         d->max_mask |= e->filter;
383         d->max_mask_subdir |= e->subdir_filter;
384
385         TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
386
387         /* recalculate the maximum masks */
388         d->max_mask = 0;
389         d->max_mask_subdir = 0;
390
391         for (i=0;i<d->num_entries;i++) {
392                 d->max_mask |= d->entries[i].filter;
393                 d->max_mask_subdir |= d->entries[i].subdir_filter;
394         }
395
396         return notify_save(notify, rec);
397 }
398
399 /*
400   Add a non-recursive watch
401 */
402
403 static void notify_add_onelevel(struct notify_context *notify,
404                                 struct notify_entry *e, void *private_data)
405 {
406         struct notify_entry_array *array;
407         struct db_record *rec;
408         DATA_BLOB blob;
409         TDB_DATA dbuf;
410         enum ndr_err_code ndr_err;
411         NTSTATUS status;
412
413         array = talloc_zero(talloc_tos(), struct notify_entry_array);
414         if (array == NULL) {
415                 return;
416         }
417
418         rec = notify->db_onelevel->fetch_locked(
419                 notify->db_onelevel, talloc_tos(),
420                 make_tdb_data((uint8_t *)&e->dir_id, sizeof(e->dir_id)));
421         if (rec == NULL) {
422                 DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
423                            "\n", file_id_string_tos(&e->dir_id)));
424                 TALLOC_FREE(array);
425                 return;
426         }
427
428         blob.data = (uint8_t *)rec->value.dptr;
429         blob.length = rec->value.dsize;
430
431         if (blob.length > 0) {
432                 ndr_err = ndr_pull_struct_blob(&blob, array, array,
433                         (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
434                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
435                         DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
436                                    ndr_errstr(ndr_err)));
437                         TALLOC_FREE(array);
438                         return;
439                 }
440                 if (DEBUGLEVEL >= 10) {
441                         DEBUG(10, ("notify_add_onelevel:\n"));
442                         NDR_PRINT_DEBUG(notify_entry_array, array);
443                 }
444         }
445
446         array->entries = talloc_realloc(array, array->entries,
447                                         struct notify_entry,
448                                         array->num_entries+1);
449         if (array->entries == NULL) {
450                 TALLOC_FREE(array);
451                 return;
452         }
453         array->entries[array->num_entries] = *e;
454         array->entries[array->num_entries].private_data = private_data;
455         array->entries[array->num_entries].server = notify->server;
456         array->num_entries += 1;
457
458         ndr_err = ndr_push_struct_blob(&blob, rec, array,
459                 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
460         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
461                 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
462                            ndr_errstr(ndr_err)));
463                 TALLOC_FREE(array);
464                 return;
465         }
466
467         if (DEBUGLEVEL >= 10) {
468                 DEBUG(10, ("notify_add_onelevel:\n"));
469                 NDR_PRINT_DEBUG(notify_entry_array, array);
470         }
471
472         dbuf.dptr = blob.data;
473         dbuf.dsize = blob.length;
474
475         status = rec->store(rec, dbuf, TDB_REPLACE);
476         TALLOC_FREE(array);
477         if (!NT_STATUS_IS_OK(status)) {
478                 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
479                            nt_errstr(status)));
480                 return;
481         }
482         e->filter = 0;
483         return;
484 }
485
486
487 /*
488   add a notify watch. This is called when a notify is first setup on a open
489   directory handle.
490 */
491 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
492                     void (*callback)(void *, const struct notify_event *), 
493                     void *private_data)
494 {
495         struct notify_entry e = *e0;
496         NTSTATUS status;
497         char *tmp_path = NULL;
498         struct notify_list *listel;
499         size_t len;
500         int depth;
501         struct db_record *rec;
502
503         /* see if change notify is enabled at all */
504         if (notify == NULL) {
505                 return NT_STATUS_NOT_IMPLEMENTED;
506         }
507
508         status = notify_fetch_locked(notify, &rec);
509         NT_STATUS_NOT_OK_RETURN(status);
510
511         status = notify_load(notify, rec);
512         if (!NT_STATUS_IS_OK(status)) {
513                 talloc_free(rec);
514                 return status;
515         }
516
517         /* cope with /. on the end of the path */
518         len = strlen(e.path);
519         if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
520                 tmp_path = talloc_strndup(notify, e.path, len-2);
521                 if (tmp_path == NULL) {
522                         status = NT_STATUS_NO_MEMORY;
523                         goto done;
524                 }
525                 e.path = tmp_path;
526         }
527
528         depth = count_chars(e.path, '/');
529
530         listel = TALLOC_ZERO_P(notify, struct notify_list);
531         if (listel == NULL) {
532                 status = NT_STATUS_NO_MEMORY;
533                 goto done;
534         }
535
536         listel->private_data = private_data;
537         listel->callback = callback;
538         listel->depth = depth;
539         DLIST_ADD(notify->list, listel);
540
541         /* ignore failures from sys_notify */
542         if (notify->sys_notify_ctx != NULL) {
543                 /*
544                   this call will modify e.filter and e.subdir_filter
545                   to remove bits handled by the backend
546                 */
547                 status = sys_notify_watch(notify->sys_notify_ctx, &e,
548                                           sys_notify_callback, listel, 
549                                           &listel->sys_notify_handle);
550                 if (NT_STATUS_IS_OK(status)) {
551                         talloc_steal(listel, listel->sys_notify_handle);
552                 }
553         }
554
555         if (e.filter != 0) {
556                 notify_add_onelevel(notify, &e, private_data);
557                 status = NT_STATUS_OK;
558         }
559
560         /* if the system notify handler couldn't handle some of the
561            filter bits, or couldn't handle a request for recursion
562            then we need to install it in the array used for the
563            intra-samba notify handling */
564         if (e.filter != 0 || e.subdir_filter != 0) {
565                 status = notify_add_array(notify, rec, &e, private_data, depth);
566         }
567
568 done:
569         talloc_free(rec);
570         talloc_free(tmp_path);
571
572         return status;
573 }
574
575 NTSTATUS notify_remove_onelevel(struct notify_context *notify,
576                                 const struct file_id *fid,
577                                 void *private_data)
578 {
579         struct notify_entry_array *array;
580         struct db_record *rec;
581         DATA_BLOB blob;
582         TDB_DATA dbuf;
583         enum ndr_err_code ndr_err;
584         NTSTATUS status;
585         int i;
586
587         if (notify == NULL) {
588                 return NT_STATUS_NOT_IMPLEMENTED;
589         }
590
591         array = talloc_zero(talloc_tos(), struct notify_entry_array);
592         if (array == NULL) {
593                 return NT_STATUS_NO_MEMORY;
594         }
595
596         rec = notify->db_onelevel->fetch_locked(
597                 notify->db_onelevel, array,
598                 make_tdb_data((uint8_t *)fid, sizeof(*fid)));
599         if (rec == NULL) {
600                 DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
601                            "\n", file_id_string_tos(fid)));
602                 TALLOC_FREE(array);
603                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
604         }
605
606         blob.data = (uint8_t *)rec->value.dptr;
607         blob.length = rec->value.dsize;
608
609         if (blob.length > 0) {
610                 ndr_err = ndr_pull_struct_blob(&blob, array, array,
611                         (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
612                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
613                         DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
614                                    ndr_errstr(ndr_err)));
615                         TALLOC_FREE(array);
616                         return ndr_map_error2ntstatus(ndr_err);
617                 }
618                 if (DEBUGLEVEL >= 10) {
619                         DEBUG(10, ("notify_remove_onelevel:\n"));
620                         NDR_PRINT_DEBUG(notify_entry_array, array);
621                 }
622         }
623
624         for (i=0; i<array->num_entries; i++) {
625                 if ((private_data == array->entries[i].private_data) &&
626                     cluster_id_equal(&notify->server,
627                                      &array->entries[i].server)) {
628                         break;
629                 }
630         }
631
632         if (i == array->num_entries) {
633                 TALLOC_FREE(array);
634                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
635         }
636
637         array->entries[i] = array->entries[array->num_entries-1];
638         array->num_entries -= 1;
639
640         if (array->num_entries == 0) {
641                 rec->delete_rec(rec);
642                 TALLOC_FREE(array);
643                 return NT_STATUS_OK;
644         }
645
646         ndr_err = ndr_push_struct_blob(&blob, rec, array,
647                 (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
648         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
649                 DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
650                            ndr_errstr(ndr_err)));
651                 TALLOC_FREE(array);
652                 return ndr_map_error2ntstatus(ndr_err);
653         }
654
655         if (DEBUGLEVEL >= 10) {
656                 DEBUG(10, ("notify_add_onelevel:\n"));
657                 NDR_PRINT_DEBUG(notify_entry_array, array);
658         }
659
660         dbuf.dptr = blob.data;
661         dbuf.dsize = blob.length;
662
663         status = rec->store(rec, dbuf, TDB_REPLACE);
664         TALLOC_FREE(array);
665         if (!NT_STATUS_IS_OK(status)) {
666                 DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
667                            nt_errstr(status)));
668                 return status;
669         }
670         return NT_STATUS_OK;
671 }
672
673 /*
674   remove a notify watch. Called when the directory handle is closed
675 */
676 NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
677 {
678         NTSTATUS status;
679         struct notify_list *listel;
680         int i, depth;
681         struct notify_depth *d;
682         struct db_record *rec;
683
684         /* see if change notify is enabled at all */
685         if (notify == NULL) {
686                 return NT_STATUS_NOT_IMPLEMENTED;
687         }
688
689         for (listel=notify->list;listel;listel=listel->next) {
690                 if (listel->private_data == private_data) {
691                         DLIST_REMOVE(notify->list, listel);
692                         break;
693                 }
694         }
695         if (listel == NULL) {
696                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
697         }
698
699         depth = listel->depth;
700
701         talloc_free(listel);
702
703         status = notify_fetch_locked(notify, &rec);
704         NT_STATUS_NOT_OK_RETURN(status);
705
706         status = notify_load(notify, rec);
707         if (!NT_STATUS_IS_OK(status)) {
708                 talloc_free(rec);
709                 return status;
710         }
711
712         if (depth >= notify->array->num_depths) {
713                 talloc_free(rec);
714                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
715         }
716
717         /* we only have to search at the depth of this element */
718         d = &notify->array->depth[depth];
719
720         for (i=0;i<d->num_entries;i++) {
721                 if (private_data == d->entries[i].private_data &&
722                     cluster_id_equal(&notify->server, &d->entries[i].server)) {
723                         break;
724                 }
725         }
726         if (i == d->num_entries) {
727                 talloc_free(rec);
728                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
729         }
730
731         if (i < d->num_entries-1) {
732                 memmove(&d->entries[i], &d->entries[i+1], 
733                         sizeof(d->entries[i])*(d->num_entries-(i+1)));
734         }
735         d->num_entries--;
736
737         status = notify_save(notify, rec);
738
739         talloc_free(rec);
740
741         return status;
742 }
743
744 /*
745   remove all notify watches for a messaging server
746 */
747 static NTSTATUS notify_remove_all(struct notify_context *notify,
748                                   const struct server_id *server)
749 {
750         NTSTATUS status;
751         int i, depth, del_count=0;
752         struct db_record *rec;
753
754         status = notify_fetch_locked(notify, &rec);
755         NT_STATUS_NOT_OK_RETURN(status);
756
757         status = notify_load(notify, rec);
758         if (!NT_STATUS_IS_OK(status)) {
759                 talloc_free(rec);
760                 return status;
761         }
762
763         /* we have to search for all entries across all depths, looking for matches
764            for the server id */
765         for (depth=0;depth<notify->array->num_depths;depth++) {
766                 struct notify_depth *d = &notify->array->depth[depth];
767                 for (i=0;i<d->num_entries;i++) {
768                         if (cluster_id_equal(server, &d->entries[i].server)) {
769                                 if (i < d->num_entries-1) {
770                                         memmove(&d->entries[i], &d->entries[i+1], 
771                                                 sizeof(d->entries[i])*(d->num_entries-(i+1)));
772                                 }
773                                 i--;
774                                 d->num_entries--;
775                                 del_count++;
776                         }
777                 }
778         }
779
780         if (del_count > 0) {
781                 status = notify_save(notify, rec);
782         }
783
784         talloc_free(rec);
785
786         return status;
787 }
788
789
790 /*
791   send a notify message to another messaging server
792 */
793 static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
794                             const char *path, uint32_t action)
795 {
796         struct notify_event ev;
797         DATA_BLOB data;
798         NTSTATUS status;
799         enum ndr_err_code ndr_err;
800         TALLOC_CTX *tmp_ctx;
801
802         ev.action = action;
803         ev.path = path;
804         ev.private_data = e->private_data;
805
806         tmp_ctx = talloc_new(notify);
807
808         ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
809                                        (ndr_push_flags_fn_t)ndr_push_notify_event);
810         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
811                 talloc_free(tmp_ctx);
812                 return ndr_map_error2ntstatus(ndr_err);
813         }
814
815         status = messaging_send(notify->messaging_ctx, e->server, 
816                                 MSG_PVFS_NOTIFY, &data);
817         talloc_free(tmp_ctx);
818         return status;
819 }
820
821 void notify_onelevel(struct notify_context *notify, uint32_t action,
822                      uint32_t filter, struct file_id fid, const char *name)
823 {
824         struct notify_entry_array *array;
825         TDB_DATA dbuf;
826         DATA_BLOB blob;
827         bool have_dead_entries = false;
828         int i;
829
830         if (notify == NULL) {
831                 return;
832         }
833
834         array = talloc_zero(talloc_tos(), struct notify_entry_array);
835         if (array == NULL) {
836                 return;
837         }
838
839         if (notify->db_onelevel->fetch(
840                     notify->db_onelevel, array,
841                     make_tdb_data((uint8_t *)&fid, sizeof(fid)),
842                     &dbuf) == -1) {
843                 TALLOC_FREE(array);
844                 return;
845         }
846
847         blob.data = (uint8 *)dbuf.dptr;
848         blob.length = dbuf.dsize;
849
850         if (blob.length > 0) {
851                 enum ndr_err_code ndr_err;
852                 ndr_err = ndr_pull_struct_blob(&blob, array, array,
853                         (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
854                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
855                         DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
856                                    ndr_errstr(ndr_err)));
857                         TALLOC_FREE(array);
858                         return;
859                 }
860                 if (DEBUGLEVEL >= 10) {
861                         DEBUG(10, ("notify_onelevel:\n"));
862                         NDR_PRINT_DEBUG(notify_entry_array, array);
863                 }
864         }
865
866         for (i=0; i<array->num_entries; i++) {
867                 struct notify_entry *e = &array->entries[i];
868
869                 if ((e->filter & filter) != 0) {
870                         NTSTATUS status;
871
872                         status = notify_send(notify, e, name, action);
873                         if (NT_STATUS_EQUAL(
874                                     status, NT_STATUS_INVALID_HANDLE)) {
875                                 /*
876                                  * Mark the entry as dead. All entries have a
877                                  * path set. The marker used here is setting
878                                  * that to NULL.
879                                  */
880                                 e->path = NULL;
881                                 have_dead_entries = true;
882                         }
883                 }
884         }
885
886         if (!have_dead_entries) {
887                 TALLOC_FREE(array);
888                 return;
889         }
890
891         for (i=0; i<array->num_entries; i++) {
892                 struct notify_entry *e = &array->entries[i];
893                 if (e->path != NULL) {
894                         continue;
895                 }
896                 DEBUG(10, ("Deleting notify entries for process %s because "
897                            "it's gone\n", procid_str_static(&e->server)));
898                 /*
899                  * Potential TODO: This might need optimizing,
900                  * notify_remove_onelevel() does a fetch_locked() operation at
901                  * every call. But this would only matter if a process with
902                  * MANY notifies has died without shutting down properly.
903                  */
904                 notify_remove_onelevel(notify, &e->dir_id, e->private_data);
905         }
906
907         TALLOC_FREE(array);
908         return;
909 }
910
911 /*
912   trigger a notify message for anyone waiting on a matching event
913
914   This function is called a lot, and needs to be very fast. The unusual data structure
915   and traversal is designed to be fast in the average case, even for large numbers of
916   notifies
917 */
918 void notify_trigger(struct notify_context *notify,
919                     uint32_t action, uint32_t filter, const char *path)
920 {
921         NTSTATUS status;
922         int depth;
923         const char *p, *next_p;
924
925         DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
926                    "path=%s\n", (unsigned)action, (unsigned)filter, path));
927
928         /* see if change notify is enabled at all */
929         if (notify == NULL) {
930                 return;
931         }
932
933  again:
934         status = notify_load(notify, NULL);
935         if (!NT_STATUS_IS_OK(status)) {
936                 return;
937         }
938
939         /* loop along the given path, working with each directory depth separately */
940         for (depth=0,p=path;
941              p && depth < notify->array->num_depths;
942              p=next_p,depth++) {
943                 int p_len = p - path;
944                 int min_i, max_i, i;
945                 struct notify_depth *d = &notify->array->depth[depth];
946                 next_p = strchr(p+1, '/');
947
948                 /* see if there are any entries at this depth */
949                 if (d->num_entries == 0) continue;
950
951                 /* try to skip based on the maximum mask. If next_p is
952                  NULL then we know it will be a 'this directory'
953                  match, otherwise it must be a subdir match */
954                 if (next_p != NULL) {
955                         if (0 == (filter & d->max_mask_subdir)) {
956                                 continue;
957                         }
958                 } else {
959                         if (0 == (filter & d->max_mask)) {
960                                 continue;
961                         }
962                 }
963
964                 /* we know there is an entry here worth looking
965                  for. Use a bisection search to find the first entry
966                  with a matching path */
967                 min_i = 0;
968                 max_i = d->num_entries-1;
969
970                 while (min_i < max_i) {
971                         struct notify_entry *e;
972                         int cmp;
973                         i = (min_i+max_i)/2;
974                         e = &d->entries[i];
975                         cmp = strncmp(path, e->path, p_len);
976                         if (cmp == 0) {
977                                 if (p_len == e->path_len) {
978                                         max_i = i;
979                                 } else {
980                                         max_i = i-1;
981                                 }
982                         } else if (cmp < 0) {
983                                 max_i = i-1;
984                         } else {
985                                 min_i = i+1;
986                         }
987                 }
988
989                 if (min_i != max_i) {
990                         /* none match */
991                         continue;
992                 }
993
994                 /* we now know that the entries start at min_i */
995                 for (i=min_i;i<d->num_entries;i++) {
996                         struct notify_entry *e = &d->entries[i];
997                         if (p_len != e->path_len ||
998                             strncmp(path, e->path, p_len) != 0) break;
999                         if (next_p != NULL) {
1000                                 if (0 == (filter & e->subdir_filter)) {
1001                                         continue;
1002                                 }
1003                         } else {
1004                                 if (0 == (filter & e->filter)) {
1005                                         continue;
1006                                 }
1007                         }
1008                         status = notify_send(notify, e, path + e->path_len + 1,
1009                                              action);
1010
1011                         if (NT_STATUS_EQUAL(
1012                                     status, NT_STATUS_INVALID_HANDLE)) {
1013                                 struct server_id server = e->server;
1014
1015                                 DEBUG(10, ("Deleting notify entries for "
1016                                            "process %s because it's gone\n",
1017                                            procid_str_static(&e->server)));
1018                                 notify_remove_all(notify, &server);
1019                                 goto again;
1020                         }
1021                 }
1022         }
1023 }