r20897: fix compiler warnings
[bbaumbach/samba-autobuild/.git] / source4 / ntvfs / common / notify.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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22   this is the change notify database. It implements mechanisms for
23   storing current change notify waiters in a tdb, and checking if a
24   given event matches any of the stored notify waiiters.
25 */
26
27 #include "includes.h"
28 #include "system/filesys.h"
29 #include "lib/tdb/include/tdb.h"
30 #include "lib/util/util_tdb.h"
31 #include "messaging/messaging.h"
32 #include "db_wrap.h"
33 #include "lib/messaging/irpc.h"
34 #include "librpc/gen_ndr/ndr_notify.h"
35 #include "lib/util/dlinklist.h"
36 #include "ntvfs/common/ntvfs_common.h"
37 #include "ntvfs/sysdep/sys_notify.h"
38 #include "cluster/cluster.h"
39
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 };
49
50
51 struct notify_list {
52         struct notify_list *next, *prev;
53         void *private;
54         void (*callback)(void *, const struct notify_event *);
55         void *sys_notify_handle;
56         int depth;
57 };
58
59 #define NOTIFY_KEY "notify array"
60
61 #define NOTIFY_ENABLE           "notify:enable"
62 #define NOTIFY_ENABLE_DEFAULT   True
63
64 static NTSTATUS notify_remove_all(struct notify_context *notify);
65 static void notify_handler(struct messaging_context *msg_ctx, void *private, 
66                            uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
67
68 /*
69   destroy the notify context
70 */
71 static int notify_destructor(struct notify_context *notify)
72 {
73         messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
74         notify_remove_all(notify);
75         return 0;
76 }
77
78 /*
79   Open up the notify.tdb database. You should close it down using
80   talloc_free(). We need the messaging_ctx to allow for notifications
81   via internal messages
82 */
83 struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server, 
84                                    struct messaging_context *messaging_ctx,
85                                    struct event_context *ev,
86                                    struct share_config *scfg)
87 {
88         char *path;
89         struct notify_context *notify;
90
91         if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != True) {
92                 return NULL;
93         }
94
95         notify = talloc(mem_ctx, struct notify_context);
96         if (notify == NULL) {
97                 return NULL;
98         }
99
100         path = smbd_tmp_path(notify, "notify.tdb");
101         notify->w = tdb_wrap_open(notify, path, 0,  
102                                   TDB_SEQNUM,
103                                   O_RDWR|O_CREAT, 0600);
104         talloc_free(path);
105         if (notify->w == NULL) {
106                 talloc_free(notify);
107                 return NULL;
108         }
109
110         notify->server = server;
111         notify->messaging_ctx = messaging_ctx;
112         notify->list = NULL;
113         notify->array = NULL;
114         notify->seqnum = tdb_get_seqnum(notify->w->tdb);
115
116         talloc_set_destructor(notify, notify_destructor);
117
118         /* register with the messaging subsystem for the notify
119            message type */
120         messaging_register(notify->messaging_ctx, notify, 
121                            MSG_PVFS_NOTIFY, notify_handler);
122
123         notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
124
125         return notify;
126 }
127
128
129 /*
130   lock the notify db
131 */
132 static NTSTATUS notify_lock(struct notify_context *notify)
133 {
134         if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
135                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
136         }
137         return NT_STATUS_OK;
138 }
139
140 /*
141   unlock the notify db
142 */
143 static void notify_unlock(struct notify_context *notify)
144 {
145         tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
146 }
147
148 /*
149   load the notify array
150 */
151 static NTSTATUS notify_load(struct notify_context *notify)
152 {
153         TDB_DATA dbuf;
154         DATA_BLOB blob;
155         NTSTATUS status;
156         int seqnum;
157
158         seqnum = tdb_get_seqnum(notify->w->tdb);
159
160         if (seqnum == notify->seqnum && notify->array != NULL) {
161                 return NT_STATUS_OK;
162         }
163
164         notify->seqnum = seqnum;
165
166         talloc_free(notify->array);
167         notify->array = talloc_zero(notify, struct notify_array);
168         NT_STATUS_HAVE_NO_MEMORY(notify->array);
169
170         dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
171         if (dbuf.dptr == NULL) {
172                 return NT_STATUS_OK;
173         }
174
175         blob.data = dbuf.dptr;
176         blob.length = dbuf.dsize;
177
178         status = ndr_pull_struct_blob(&blob, notify->array, notify->array, 
179                                       (ndr_pull_flags_fn_t)ndr_pull_notify_array);
180         free(dbuf.dptr);
181
182         return status;
183 }
184
185 /*
186   compare notify entries for sorting
187 */
188 static int notify_compare(const void *p1, const void *p2)
189 {
190         const struct notify_entry *e1 = p1, *e2 = p2;
191         return strcmp(e1->path, e2->path);
192 }
193
194 /*
195   save the notify array
196 */
197 static NTSTATUS notify_save(struct notify_context *notify)
198 {
199         TDB_DATA dbuf;
200         DATA_BLOB blob;
201         NTSTATUS status;
202         int ret;
203         TALLOC_CTX *tmp_ctx;
204
205         /* if possible, remove some depth arrays */
206         while (notify->array->num_depths > 0 &&
207                notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
208                 notify->array->num_depths--;
209         }
210
211         /* we might just be able to delete the record */
212         if (notify->array->num_depths == 0) {
213                 ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
214                 if (ret != 0) {
215                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
216                 }
217                 return NT_STATUS_OK;
218         }
219
220         tmp_ctx = talloc_new(notify);
221
222         status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array, 
223                                       (ndr_push_flags_fn_t)ndr_push_notify_array);
224         if (!NT_STATUS_IS_OK(status)) {
225                 talloc_free(tmp_ctx);
226                 return status;
227         }
228
229         dbuf.dptr = blob.data;
230         dbuf.dsize = blob.length;
231                 
232         ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
233         talloc_free(tmp_ctx);
234         if (ret != 0) {
235                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
236         }
237
238         return NT_STATUS_OK;
239 }
240
241
242 /*
243   handle incoming notify messages
244 */
245 static void notify_handler(struct messaging_context *msg_ctx, void *private, 
246                            uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
247 {
248         struct notify_context *notify = talloc_get_type(private, struct notify_context);
249         NTSTATUS status;
250         struct notify_event ev;
251         TALLOC_CTX *tmp_ctx = talloc_new(notify);
252         struct notify_list *listel;
253
254         status = ndr_pull_struct_blob(data, tmp_ctx, &ev, 
255                                       (ndr_pull_flags_fn_t)ndr_pull_notify_event);
256         if (!NT_STATUS_IS_OK(status)) {
257                 talloc_free(tmp_ctx);
258                 return;
259         }
260
261         for (listel=notify->list;listel;listel=listel->next) {
262                 if (listel->private == ev.private) {
263                         listel->callback(listel->private, &ev);
264                         break;
265                 }
266         }
267
268         talloc_free(tmp_ctx);   
269 }
270
271 /*
272   callback from sys_notify telling us about changes from the OS
273 */
274 static void sys_notify_callback(struct sys_notify_context *ctx, 
275                                 void *ptr, struct notify_event *ev)
276 {
277         struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
278         ev->private = listel;
279         listel->callback(listel->private, ev);
280 }
281
282 /*
283   add an entry to the notify array
284 */
285 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
286                                  void *private, int depth)
287 {
288         int i;
289         struct notify_depth *d;
290         struct notify_entry *ee;
291
292         /* possibly expand the depths array */
293         if (depth >= notify->array->num_depths) {
294                 d = talloc_realloc(notify->array, notify->array->depth, 
295                                    struct notify_depth, depth+1);
296                 NT_STATUS_HAVE_NO_MEMORY(d);
297                 for (i=notify->array->num_depths;i<=depth;i++) {
298                         ZERO_STRUCT(d[i]);
299                 }
300                 notify->array->depth = d;
301                 notify->array->num_depths = depth+1;
302         }
303         d = &notify->array->depth[depth];
304
305         /* expand the entries array */
306         ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
307                             d->num_entries+1);
308         NT_STATUS_HAVE_NO_MEMORY(ee);
309         d->entries = ee;
310
311         d->entries[d->num_entries] = *e;
312         d->entries[d->num_entries].private = private;
313         d->entries[d->num_entries].server = notify->server;
314         d->entries[d->num_entries].path_len = strlen(e->path);
315         d->num_entries++;
316
317         d->max_mask |= e->filter;
318         d->max_mask_subdir |= e->subdir_filter;
319
320         if (d->num_entries > 1) {
321                 qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
322         }
323
324         /* recalculate the maximum masks */
325         d->max_mask = 0;
326         d->max_mask_subdir = 0;
327
328         for (i=0;i<d->num_entries;i++) {
329                 d->max_mask |= d->entries[i].filter;
330                 d->max_mask_subdir |= d->entries[i].subdir_filter;
331         }
332
333         return notify_save(notify);
334 }
335
336 /*
337   add a notify watch. This is called when a notify is first setup on a open
338   directory handle.
339 */
340 NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
341                     void (*callback)(void *, const struct notify_event *), 
342                     void *private)
343 {
344         struct notify_entry e = *e0;
345         NTSTATUS status;
346         char *tmp_path = NULL;
347         struct notify_list *listel;
348         size_t len;
349         int depth;
350
351         /* see if change notify is enabled at all */
352         if (notify == NULL) {
353                 return NT_STATUS_NOT_IMPLEMENTED;
354         }
355
356         status = notify_lock(notify);
357         NT_STATUS_NOT_OK_RETURN(status);
358
359         status = notify_load(notify);
360         if (!NT_STATUS_IS_OK(status)) {
361                 goto done;
362         }
363
364         /* cope with /. on the end of the path */
365         len = strlen(e.path);
366         if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
367                 tmp_path = talloc_strndup(notify, e.path, len-2);
368                 if (tmp_path == NULL) {
369                         status = NT_STATUS_NO_MEMORY;
370                         goto done;
371                 }
372                 e.path = tmp_path;
373         }
374
375         depth = count_chars(e.path, '/');
376
377         listel = talloc_zero(notify, struct notify_list);
378         if (listel == NULL) {
379                 status = NT_STATUS_NO_MEMORY;
380                 goto done;
381         }
382
383         listel->private = private;
384         listel->callback = callback;
385         listel->depth = depth;
386         DLIST_ADD(notify->list, listel);
387
388         /* ignore failures from sys_notify */
389         if (notify->sys_notify_ctx != NULL) {
390                 /*
391                   this call will modify e.filter and e.subdir_filter
392                   to remove bits handled by the backend
393                 */
394                 status = sys_notify_watch(notify->sys_notify_ctx, &e,
395                                           sys_notify_callback, listel, 
396                                           &listel->sys_notify_handle);
397                 if (NT_STATUS_IS_OK(status)) {
398                         talloc_steal(listel, listel->sys_notify_handle);
399                 }
400         }
401
402         /* if the system notify handler couldn't handle some of the
403            filter bits, or couldn't handle a request for recursion
404            then we need to install it in the array used for the
405            intra-samba notify handling */
406         if (e.filter != 0 || e.subdir_filter != 0) {
407                 status = notify_add_array(notify, &e, private, depth);
408         }
409
410 done:
411         notify_unlock(notify);
412         talloc_free(tmp_path);
413
414         return status;
415 }
416
417 /*
418   remove a notify watch. Called when the directory handle is closed
419 */
420 NTSTATUS notify_remove(struct notify_context *notify, void *private)
421 {
422         NTSTATUS status;
423         struct notify_list *listel;
424         int i, depth;
425         struct notify_depth *d;
426
427         /* see if change notify is enabled at all */
428         if (notify == NULL) {
429                 return NT_STATUS_NOT_IMPLEMENTED;
430         }
431
432         for (listel=notify->list;listel;listel=listel->next) {
433                 if (listel->private == private) {
434                         DLIST_REMOVE(notify->list, listel);
435                         break;
436                 }
437         }
438         if (listel == NULL) {
439                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
440         }
441
442         depth = listel->depth;
443
444         talloc_free(listel);
445
446         status = notify_lock(notify);
447         NT_STATUS_NOT_OK_RETURN(status);
448
449         status = notify_load(notify);
450         if (!NT_STATUS_IS_OK(status)) {
451                 notify_unlock(notify);
452                 return status;
453         }
454
455         if (depth >= notify->array->num_depths) {
456                 notify_unlock(notify);
457                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
458         }
459
460         /* we only have to search at the depth of this element */
461         d = &notify->array->depth[depth];
462
463         for (i=0;i<d->num_entries;i++) {
464                 if (private == d->entries[i].private &&
465                     cluster_id_equal(&notify->server, &d->entries[i].server)) {
466                         break;
467                 }
468         }
469         if (i == d->num_entries) {
470                 notify_unlock(notify);
471                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
472         }
473
474         if (i < d->num_entries-1) {
475                 memmove(&d->entries[i], &d->entries[i+1], 
476                         sizeof(d->entries[i])*(d->num_entries-(i+1)));
477         }
478         d->num_entries--;
479
480         status = notify_save(notify);
481
482         notify_unlock(notify);
483
484         return status;
485 }
486
487 /*
488   remove all notify watches for this messaging server
489 */
490 static NTSTATUS notify_remove_all(struct notify_context *notify)
491 {
492         NTSTATUS status;
493         int i, depth, del_count=0;
494
495         if (notify->list == NULL) {
496                 return NT_STATUS_OK;
497         }
498
499         status = notify_lock(notify);
500         NT_STATUS_NOT_OK_RETURN(status);
501
502         status = notify_load(notify);
503         if (!NT_STATUS_IS_OK(status)) {
504                 notify_unlock(notify);
505                 return status;
506         }
507
508         /* we have to search for all entries across all depths, looking for matches
509            for our server id */
510         for (depth=0;depth<notify->array->num_depths;depth++) {
511                 struct notify_depth *d = &notify->array->depth[depth];
512                 for (i=0;i<d->num_entries;i++) {
513                         if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
514                                 if (i < d->num_entries-1) {
515                                         memmove(&d->entries[i], &d->entries[i+1], 
516                                                 sizeof(d->entries[i])*(d->num_entries-(i+1)));
517                                 }
518                                 i--;
519                                 d->num_entries--;
520                                 del_count++;
521                         }
522                 }
523         }
524
525         if (del_count > 0) {
526                 status = notify_save(notify);
527         }
528
529         notify_unlock(notify);
530
531         return status;
532 }
533
534
535 /*
536   send a notify message to another messaging server
537 */
538 static void notify_send(struct notify_context *notify, struct notify_entry *e,
539                         const char *path, uint32_t action)
540 {
541         struct notify_event ev;
542         DATA_BLOB data;
543         NTSTATUS status;
544         TALLOC_CTX *tmp_ctx;
545
546         ev.action = action;
547         ev.path = path;
548         ev.private = e->private;
549
550         tmp_ctx = talloc_new(notify);
551
552         status = ndr_push_struct_blob(&data, tmp_ctx, &ev, 
553                                       (ndr_push_flags_fn_t)ndr_push_notify_event);
554         if (!NT_STATUS_IS_OK(status)) {
555                 talloc_free(tmp_ctx);
556                 return;
557         }
558
559         status = messaging_send(notify->messaging_ctx, e->server, 
560                                 MSG_PVFS_NOTIFY, &data);
561         talloc_free(tmp_ctx);
562 }
563
564
565 /*
566   trigger a notify message for anyone waiting on a matching event
567
568   This function is called a lot, and needs to be very fast. The unusual data structure
569   and traversal is designed to be fast in the average case, even for large numbers of
570   notifies
571 */
572 void notify_trigger(struct notify_context *notify,
573                     uint32_t action, uint32_t filter, const char *path)
574 {
575         NTSTATUS status;
576         int depth;
577         const char *p, *next_p;
578
579         /* see if change notify is enabled at all */
580         if (notify == NULL) {
581                 return;
582         }
583
584         status = notify_load(notify);
585         if (!NT_STATUS_IS_OK(status)) {
586                 return;
587         }
588
589         /* loop along the given path, working with each directory depth separately */
590         for (depth=0,p=path;
591              p && depth < notify->array->num_depths;
592              p=next_p,depth++) {
593                 int p_len = p - path;
594                 int min_i, max_i, i;
595                 struct notify_depth *d = &notify->array->depth[depth];
596                 next_p = strchr(p+1, '/');
597
598                 /* see if there are any entries at this depth */
599                 if (d->num_entries == 0) continue;
600                 
601                 /* try to skip based on the maximum mask. If next_p is
602                  NULL then we know it will be a 'this directory'
603                  match, otherwise it must be a subdir match */
604                 if (next_p != NULL) {
605                         if (0 == (filter & d->max_mask_subdir)) {
606                                 continue;
607                         }
608                 } else {
609                         if (0 == (filter & d->max_mask)) {
610                                 continue;
611                         }
612                 }
613
614                 /* we know there is an entry here worth looking
615                  for. Use a bisection search to find the first entry
616                  with a matching path */
617                 min_i = 0;
618                 max_i = d->num_entries-1;
619
620                 while (min_i < max_i) {
621                         struct notify_entry *e;
622                         int cmp;
623                         i = (min_i+max_i)/2;
624                         e = &d->entries[i];
625                         cmp = strncmp(path, e->path, p_len);
626                         if (cmp == 0) {
627                                 if (p_len == e->path_len) {
628                                         max_i = i;
629                                 } else {
630                                         max_i = i-1;
631                                 }
632                         } else if (cmp < 0) {
633                                 max_i = i-1;
634                         } else {
635                                 min_i = i+1;
636                         }
637                 }
638
639                 if (min_i != max_i) {
640                         /* none match */
641                         continue;
642                 }
643
644                 /* we now know that the entries start at min_i */
645                 for (i=min_i;i<d->num_entries;i++) {
646                         struct notify_entry *e = &d->entries[i];
647                         if (p_len != e->path_len ||
648                             strncmp(path, e->path, p_len) != 0) break;
649                         if (next_p != NULL) {
650                                 if (0 == (filter & e->subdir_filter)) {
651                                         continue;
652                                 }
653                         } else {
654                                 if (0 == (filter & e->filter)) {
655                                         continue;
656                                 }
657                         }
658                         notify_send(notify, e, path + e->path_len + 1, action);
659                 }
660         }
661 }