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