~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/ntvfs/common/notify.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

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