r26003: Split up DB_WRAP, as first step in an attempt to sanitize dependencies.
[kai/samba.git] / source4 / ntvfs / common / notify.c
index 9ea024f2fcffd154dd8c19623a661bcf81429aec..16cf4e4b5421c739e080e6538aea6c4b4562d5fe 100644 (file)
@@ -5,7 +5,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
 */
 
 /*
 #include "includes.h"
 #include "system/filesys.h"
 #include "lib/tdb/include/tdb.h"
 #include "includes.h"
 #include "system/filesys.h"
 #include "lib/tdb/include/tdb.h"
-#include "lib/tdb/include/tdbutil.h"
+#include "lib/util/util_tdb.h"
 #include "messaging/messaging.h"
 #include "messaging/messaging.h"
-#include "db_wrap.h"
+#include "tdb_wrap.h"
 #include "lib/messaging/irpc.h"
 #include "librpc/gen_ndr/ndr_notify.h"
 #include "lib/messaging/irpc.h"
 #include "librpc/gen_ndr/ndr_notify.h"
-#include "dlinklist.h"
+#include "lib/util/dlinklist.h"
+#include "ntvfs/common/ntvfs_common.h"
 #include "ntvfs/sysdep/sys_notify.h"
 #include "ntvfs/sysdep/sys_notify.h"
+#include "cluster/cluster.h"
 
 struct notify_context {
        struct tdb_wrap *w;
 
 struct notify_context {
        struct tdb_wrap *w;
-       uint32_t server;
+       struct server_id server;
        struct messaging_context *messaging_ctx;
        struct notify_list *list;
        struct notify_array *array;
        struct messaging_context *messaging_ctx;
        struct notify_list *list;
        struct notify_array *array;
@@ -48,23 +49,26 @@ struct notify_context {
 
 struct notify_list {
        struct notify_list *next, *prev;
 
 struct notify_list {
        struct notify_list *next, *prev;
-       void *private;
+       void *private_data;
        void (*callback)(void *, const struct notify_event *);
        void *sys_notify_handle;
        void (*callback)(void *, const struct notify_event *);
        void *sys_notify_handle;
+       int depth;
 };
 
 #define NOTIFY_KEY "notify array"
 
 };
 
 #define NOTIFY_KEY "notify array"
 
+#define NOTIFY_ENABLE          "notify:enable"
+#define NOTIFY_ENABLE_DEFAULT  true
+
 static NTSTATUS notify_remove_all(struct notify_context *notify);
 static NTSTATUS notify_remove_all(struct notify_context *notify);
-static void notify_handler(struct messaging_context *msg_ctx, void *private, 
-                          uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
+static void notify_handler(struct messaging_context *msg_ctx, void *private_data
+                          uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
 
 /*
   destroy the notify context
 */
 
 /*
   destroy the notify context
 */
-static int notify_destructor(void *p)
+static int notify_destructor(struct notify_context *notify)
 {
 {
-       struct notify_context *notify = talloc_get_type(p, struct notify_context);
        messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
        notify_remove_all(notify);
        return 0;
        messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
        notify_remove_all(notify);
        return 0;
@@ -75,23 +79,23 @@ static int notify_destructor(void *p)
   talloc_free(). We need the messaging_ctx to allow for notifications
   via internal messages
 */
   talloc_free(). We need the messaging_ctx to allow for notifications
   via internal messages
 */
-struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server, 
+struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server, 
                                   struct messaging_context *messaging_ctx,
                                   struct messaging_context *messaging_ctx,
-                                  struct event_context *ev)
+                                  struct event_context *ev,
+                                  struct share_config *scfg)
 {
 {
-       char *path;
        struct notify_context *notify;
 
        struct notify_context *notify;
 
+       if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != true) {
+               return NULL;
+       }
+
        notify = talloc(mem_ctx, struct notify_context);
        if (notify == NULL) {
                return NULL;
        }
 
        notify = talloc(mem_ctx, struct notify_context);
        if (notify == NULL) {
                return NULL;
        }
 
-       path = smbd_tmp_path(notify, "notify.tdb");
-       notify->w = tdb_wrap_open(notify, path, 0,  
-                                 TDB_SEQNUM,
-                                 O_RDWR|O_CREAT, 0600);
-       talloc_free(path);
+       notify->w = cluster_tdb_tmp_open(notify, "notify.tdb", TDB_SEQNUM);
        if (notify->w == NULL) {
                talloc_free(notify);
                return NULL;
        if (notify->w == NULL) {
                talloc_free(notify);
                return NULL;
@@ -110,7 +114,7 @@ struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server,
        messaging_register(notify->messaging_ctx, notify, 
                           MSG_PVFS_NOTIFY, notify_handler);
 
        messaging_register(notify->messaging_ctx, notify, 
                           MSG_PVFS_NOTIFY, notify_handler);
 
-       notify->sys_notify_ctx = sys_notify_init(-1, notify, ev);
+       notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
 
        return notify;
 }
 
        return notify;
 }
@@ -142,7 +146,7 @@ static NTSTATUS notify_load(struct notify_context *notify)
 {
        TDB_DATA dbuf;
        DATA_BLOB blob;
 {
        TDB_DATA dbuf;
        DATA_BLOB blob;
-       NTSTATUS status;
+       enum ndr_err_code ndr_err;
        int seqnum;
 
        seqnum = tdb_get_seqnum(notify->w->tdb);
        int seqnum;
 
        seqnum = tdb_get_seqnum(notify->w->tdb);
@@ -165,11 +169,14 @@ static NTSTATUS notify_load(struct notify_context *notify)
        blob.data = dbuf.dptr;
        blob.length = dbuf.dsize;
 
        blob.data = dbuf.dptr;
        blob.length = dbuf.dsize;
 
-       status = ndr_pull_struct_blob(&blob, notify->array, notify->array, 
-                                     (ndr_pull_flags_fn_t)ndr_pull_notify_array);
+       ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
+                                      (ndr_pull_flags_fn_t)ndr_pull_notify_array);
        free(dbuf.dptr);
        free(dbuf.dptr);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               return ndr_map_error2ntstatus(ndr_err);
+       }
 
 
-       return status;
+       return NT_STATUS_OK;
 }
 
 /*
 }
 
 /*
@@ -188,11 +195,18 @@ static NTSTATUS notify_save(struct notify_context *notify)
 {
        TDB_DATA dbuf;
        DATA_BLOB blob;
 {
        TDB_DATA dbuf;
        DATA_BLOB blob;
-       NTSTATUS status;
+       enum ndr_err_code ndr_err;
        int ret;
        TALLOC_CTX *tmp_ctx;
 
        int ret;
        TALLOC_CTX *tmp_ctx;
 
-       if (notify->array->num_entries == 0) {
+       /* if possible, remove some depth arrays */
+       while (notify->array->num_depths > 0 &&
+              notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
+               notify->array->num_depths--;
+       }
+
+       /* we might just be able to delete the record */
+       if (notify->array->num_depths == 0) {
                ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
                if (ret != 0) {
                        return NT_STATUS_INTERNAL_DB_CORRUPTION;
                ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
                if (ret != 0) {
                        return NT_STATUS_INTERNAL_DB_CORRUPTION;
@@ -200,18 +214,14 @@ static NTSTATUS notify_save(struct notify_context *notify)
                return NT_STATUS_OK;
        }
 
                return NT_STATUS_OK;
        }
 
-       if (notify->array->num_entries > 1) {
-               qsort(notify->array->entries, notify->array->num_entries, 
-                     sizeof(struct notify_entry), notify_compare);
-       }
-
        tmp_ctx = talloc_new(notify);
        tmp_ctx = talloc_new(notify);
+       NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
 
 
-       status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array, 
-                                     (ndr_push_flags_fn_t)ndr_push_notify_array);
-       if (!NT_STATUS_IS_OK(status)) {
+       ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
+                                      (ndr_push_flags_fn_t)ndr_push_notify_array);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(tmp_ctx);
                talloc_free(tmp_ctx);
-               return status;
+               return ndr_map_error2ntstatus(ndr_err);
        }
 
        dbuf.dptr = blob.data;
        }
 
        dbuf.dptr = blob.data;
@@ -230,25 +240,29 @@ static NTSTATUS notify_save(struct notify_context *notify)
 /*
   handle incoming notify messages
 */
 /*
   handle incoming notify messages
 */
-static void notify_handler(struct messaging_context *msg_ctx, void *private, 
-                          uint32_t msg_type, uint32_t server_id, DATA_BLOB *data)
+static void notify_handler(struct messaging_context *msg_ctx, void *private_data
+                          uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
 {
 {
-       struct notify_context *notify = talloc_get_type(private, struct notify_context);
-       NTSTATUS status;
+       struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
+       enum ndr_err_code ndr_err;
        struct notify_event ev;
        TALLOC_CTX *tmp_ctx = talloc_new(notify);
        struct notify_list *listel;
 
        struct notify_event ev;
        TALLOC_CTX *tmp_ctx = talloc_new(notify);
        struct notify_list *listel;
 
-       status = ndr_pull_struct_blob(data, tmp_ctx, &ev, 
+       if (tmp_ctx == NULL) {
+               return;
+       }
+
+       ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
                                      (ndr_pull_flags_fn_t)ndr_pull_notify_event);
                                      (ndr_pull_flags_fn_t)ndr_pull_notify_event);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(tmp_ctx);
                return;
        }
 
        for (listel=notify->list;listel;listel=listel->next) {
                talloc_free(tmp_ctx);
                return;
        }
 
        for (listel=notify->list;listel;listel=listel->next) {
-               if (listel->private == ev.private) {
-                       listel->callback(listel->private, &ev);
+               if (listel->private_data == ev.private_data) {
+                       listel->callback(listel->private_data, &ev);
                        break;
                }
        }
                        break;
                }
        }
@@ -263,26 +277,61 @@ static void sys_notify_callback(struct sys_notify_context *ctx,
                                void *ptr, struct notify_event *ev)
 {
        struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
                                void *ptr, struct notify_event *ev)
 {
        struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
-       ev->private = listel;
-       listel->callback(listel->private, ev);
+       ev->private_data = listel;
+       listel->callback(listel->private_data, ev);
 }
 
 /*
   add an entry to the notify array
 */
 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
 }
 
 /*
   add an entry to the notify array
 */
 static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
-                                const char *path, void *private)
+                                void *private_data, int depth)
 {
 {
-       notify->array->entries[notify->array->num_entries] = *e;
-       notify->array->entries[notify->array->num_entries].private = private;
-       notify->array->entries[notify->array->num_entries].server = notify->server;
-       
-       if (path) {
-               notify->array->entries[notify->array->num_entries].path = path;
+       int i;
+       struct notify_depth *d;
+       struct notify_entry *ee;
+
+       /* possibly expand the depths array */
+       if (depth >= notify->array->num_depths) {
+               d = talloc_realloc(notify->array, notify->array->depth, 
+                                  struct notify_depth, depth+1);
+               NT_STATUS_HAVE_NO_MEMORY(d);
+               for (i=notify->array->num_depths;i<=depth;i++) {
+                       ZERO_STRUCT(d[i]);
+               }
+               notify->array->depth = d;
+               notify->array->num_depths = depth+1;
+       }
+       d = &notify->array->depth[depth];
+
+       /* expand the entries array */
+       ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
+                           d->num_entries+1);
+       NT_STATUS_HAVE_NO_MEMORY(ee);
+       d->entries = ee;
+
+       d->entries[d->num_entries] = *e;
+       d->entries[d->num_entries].private_data = private_data;
+       d->entries[d->num_entries].server = notify->server;
+       d->entries[d->num_entries].path_len = strlen(e->path);
+       d->num_entries++;
+
+       d->max_mask |= e->filter;
+       d->max_mask_subdir |= e->subdir_filter;
+
+       if (d->num_entries > 1) {
+               qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
+       }
+
+       /* recalculate the maximum masks */
+       d->max_mask = 0;
+       d->max_mask_subdir = 0;
+
+       for (i=0;i<d->num_entries;i++) {
+               d->max_mask |= d->entries[i].filter;
+               d->max_mask_subdir |= d->entries[i].subdir_filter;
        }
 
        }
 
-       notify->array->num_entries++;
-       
        return notify_save(notify);
 }
 
        return notify_save(notify);
 }
 
@@ -290,64 +339,79 @@ static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_en
   add a notify watch. This is called when a notify is first setup on a open
   directory handle.
 */
   add a notify watch. This is called when a notify is first setup on a open
   directory handle.
 */
-NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e,
+NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
                    void (*callback)(void *, const struct notify_event *), 
                    void (*callback)(void *, const struct notify_event *), 
-                   void *private)
+                   void *private_data)
 {
 {
+       struct notify_entry e = *e0;
        NTSTATUS status;
        NTSTATUS status;
+       char *tmp_path = NULL;
        struct notify_list *listel;
        struct notify_list *listel;
-       char *path = NULL;
        size_t len;
        size_t len;
+       int depth;
+
+       /* see if change notify is enabled at all */
+       if (notify == NULL) {
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
 
        status = notify_lock(notify);
        NT_STATUS_NOT_OK_RETURN(status);
 
        status = notify_load(notify);
        if (!NT_STATUS_IS_OK(status)) {
 
        status = notify_lock(notify);
        NT_STATUS_NOT_OK_RETURN(status);
 
        status = notify_load(notify);
        if (!NT_STATUS_IS_OK(status)) {
-               notify_unlock(notify);
-               return status;
-       }
-
-       notify->array->entries = talloc_realloc(notify->array, notify->array->entries, 
-                                               struct notify_entry,
-                                               notify->array->num_entries+1);
-
-       if (notify->array->entries == NULL) {
-               notify_unlock(notify);
-               return NT_STATUS_NO_MEMORY;
+               goto done;
        }
 
        /* cope with /. on the end of the path */
        }
 
        /* cope with /. on the end of the path */
-       len = strlen(e->path);
-       if (len > 1 && e->path[len-1] == '.' && e->path[len-2] == '/') {
-               path = talloc_strndup(notify, e->path, len-2);
+       len = strlen(e.path);
+       if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
+               tmp_path = talloc_strndup(notify, e.path, len-2);
+               if (tmp_path == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+               e.path = tmp_path;
        }
 
        }
 
+       depth = count_chars(e.path, '/');
+
        listel = talloc_zero(notify, struct notify_list);
        listel = talloc_zero(notify, struct notify_list);
-       NT_STATUS_HAVE_NO_MEMORY(listel);
+       if (listel == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
 
 
-       listel->private = private;
+       listel->private_data = private_data;
        listel->callback = callback;
        listel->callback = callback;
+       listel->depth = depth;
        DLIST_ADD(notify->list, listel);
 
        /* ignore failures from sys_notify */
        if (notify->sys_notify_ctx != NULL) {
        DLIST_ADD(notify->list, listel);
 
        /* ignore failures from sys_notify */
        if (notify->sys_notify_ctx != NULL) {
-               status = sys_notify_watch(notify->sys_notify_ctx, e->path, e->filter, 
+               /*
+                 this call will modify e.filter and e.subdir_filter
+                 to remove bits handled by the backend
+               */
+               status = sys_notify_watch(notify->sys_notify_ctx, &e,
                                          sys_notify_callback, listel, 
                                          &listel->sys_notify_handle);
                if (NT_STATUS_IS_OK(status)) {
                                          sys_notify_callback, listel, 
                                          &listel->sys_notify_handle);
                if (NT_STATUS_IS_OK(status)) {
-                       /* if the kernel handler has said it can handle this notify then
-                          we don't need to add it to the array */
                        talloc_steal(listel, listel->sys_notify_handle);
                        talloc_steal(listel, listel->sys_notify_handle);
-                       goto done;
                }
        }
 
                }
        }
 
-       status = notify_add_array(notify, e, path, private);
+       /* if the system notify handler couldn't handle some of the
+          filter bits, or couldn't handle a request for recursion
+          then we need to install it in the array used for the
+          intra-samba notify handling */
+       if (e.filter != 0 || e.subdir_filter != 0) {
+               status = notify_add_array(notify, &e, private_data, depth);
+       }
 
 done:
        notify_unlock(notify);
 
 done:
        notify_unlock(notify);
-       talloc_free(path);
+       talloc_free(tmp_path);
 
        return status;
 }
 
        return status;
 }
@@ -355,14 +419,20 @@ done:
 /*
   remove a notify watch. Called when the directory handle is closed
 */
 /*
   remove a notify watch. Called when the directory handle is closed
 */
-NTSTATUS notify_remove(struct notify_context *notify, void *private)
+NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
 {
        NTSTATUS status;
        struct notify_list *listel;
 {
        NTSTATUS status;
        struct notify_list *listel;
-       int i;
+       int i, depth;
+       struct notify_depth *d;
+
+       /* see if change notify is enabled at all */
+       if (notify == NULL) {
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
 
        for (listel=notify->list;listel;listel=listel->next) {
 
        for (listel=notify->list;listel;listel=listel->next) {
-               if (listel->private == private) {
+               if (listel->private_data == private_data) {
                        DLIST_REMOVE(notify->list, listel);
                        break;
                }
                        DLIST_REMOVE(notify->list, listel);
                        break;
                }
@@ -371,6 +441,8 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private)
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
+       depth = listel->depth;
+
        talloc_free(listel);
 
        status = notify_lock(notify);
        talloc_free(listel);
 
        status = notify_lock(notify);
@@ -382,22 +454,30 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private)
                return status;
        }
 
                return status;
        }
 
-       for (i=0;i<notify->array->num_entries;i++) {
-               if (notify->server == notify->array->entries[i].server && 
-                   private == notify->array->entries[i].private) {
+       if (depth >= notify->array->num_depths) {
+               notify_unlock(notify);
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
+       /* we only have to search at the depth of this element */
+       d = &notify->array->depth[depth];
+
+       for (i=0;i<d->num_entries;i++) {
+               if (private_data == d->entries[i].private_data &&
+                   cluster_id_equal(&notify->server, &d->entries[i].server)) {
                        break;
                }
        }
                        break;
                }
        }
-       if (i == notify->array->num_entries) {
+       if (i == d->num_entries) {
                notify_unlock(notify);
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
                notify_unlock(notify);
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
-       if (i < notify->array->num_entries-1) {
-               memmove(&notify->array->entries[i], &notify->array->entries[i+1], 
-                       sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
+       if (i < d->num_entries-1) {
+               memmove(&d->entries[i], &d->entries[i+1], 
+                       sizeof(d->entries[i])*(d->num_entries-(i+1)));
        }
        }
-       notify->array->num_entries--;
+       d->num_entries--;
 
        status = notify_save(notify);
 
 
        status = notify_save(notify);
 
@@ -412,7 +492,7 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private)
 static NTSTATUS notify_remove_all(struct notify_context *notify)
 {
        NTSTATUS status;
 static NTSTATUS notify_remove_all(struct notify_context *notify)
 {
        NTSTATUS status;
-       int i;
+       int i, depth, del_count=0;
 
        if (notify->list == NULL) {
                return NT_STATUS_OK;
 
        if (notify->list == NULL) {
                return NT_STATUS_OK;
@@ -427,19 +507,26 @@ static NTSTATUS notify_remove_all(struct notify_context *notify)
                return status;
        }
 
                return status;
        }
 
-       for (i=0;i<notify->array->num_entries;i++) {
-               if (notify->server == notify->array->entries[i].server) {
-                       if (i < notify->array->num_entries-1) {
-                               memmove(&notify->array->entries[i], &notify->array->entries[i+1], 
-                                       sizeof(notify->array->entries[i])*(notify->array->num_entries-(i+1)));
+       /* we have to search for all entries across all depths, looking for matches
+          for our server id */
+       for (depth=0;depth<notify->array->num_depths;depth++) {
+               struct notify_depth *d = &notify->array->depth[depth];
+               for (i=0;i<d->num_entries;i++) {
+                       if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
+                               if (i < d->num_entries-1) {
+                                       memmove(&d->entries[i], &d->entries[i+1], 
+                                               sizeof(d->entries[i])*(d->num_entries-(i+1)));
+                               }
+                               i--;
+                               d->num_entries--;
+                               del_count++;
                        }
                        }
-                       i--;
-                       notify->array->num_entries--;
                }
        }
 
                }
        }
 
-
-       status = notify_save(notify);
+       if (del_count > 0) {
+               status = notify_save(notify);
+       }
 
        notify_unlock(notify);
 
 
        notify_unlock(notify);
 
@@ -456,17 +543,18 @@ static void notify_send(struct notify_context *notify, struct notify_entry *e,
        struct notify_event ev;
        DATA_BLOB data;
        NTSTATUS status;
        struct notify_event ev;
        DATA_BLOB data;
        NTSTATUS status;
+       enum ndr_err_code ndr_err;
        TALLOC_CTX *tmp_ctx;
 
        ev.action = action;
        ev.path = path;
        TALLOC_CTX *tmp_ctx;
 
        ev.action = action;
        ev.path = path;
-       ev.private = e->private;
+       ev.private_data = e->private_data;
 
        tmp_ctx = talloc_new(notify);
 
 
        tmp_ctx = talloc_new(notify);
 
-       status = ndr_push_struct_blob(&data, tmp_ctx, &ev, 
+       ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev,
                                      (ndr_push_flags_fn_t)ndr_push_notify_event);
                                      (ndr_push_flags_fn_t)ndr_push_notify_event);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(tmp_ctx);
                return;
        }
                talloc_free(tmp_ctx);
                return;
        }
@@ -476,58 +564,101 @@ static void notify_send(struct notify_context *notify, struct notify_entry *e,
        talloc_free(tmp_ctx);
 }
 
        talloc_free(tmp_ctx);
 }
 
-/*
-  see if a notify event matches
-*/
-static BOOL notify_match(struct notify_context *notify, struct notify_entry *e,
-                        const char *path, uint32_t filter)
-{
-       size_t len;
-
-       if (!(filter & e->filter)) {
-               return False;
-       }
-
-       len = strlen(e->path);
-
-       if (strncmp(path, e->path, len) != 0) {
-               return False;
-       }
-
-       if (path[len] != '/') {
-               return False;
-       }
-
-       if (!e->recursive) {
-               if (strchr(&path[len+1], '/') != NULL) {
-                       return False;
-               }
-       }
-
-       return True;
-}
-
 
 /*
   trigger a notify message for anyone waiting on a matching event
 
 /*
   trigger a notify message for anyone waiting on a matching event
+
+  This function is called a lot, and needs to be very fast. The unusual data structure
+  and traversal is designed to be fast in the average case, even for large numbers of
+  notifies
 */
 void notify_trigger(struct notify_context *notify,
                    uint32_t action, uint32_t filter, const char *path)
 {
        NTSTATUS status;
 */
 void notify_trigger(struct notify_context *notify,
                    uint32_t action, uint32_t filter, const char *path)
 {
        NTSTATUS status;
-       int i;
+       int depth;
+       const char *p, *next_p;
+
+       /* see if change notify is enabled at all */
+       if (notify == NULL) {
+               return;
+       }
 
        status = notify_load(notify);
        if (!NT_STATUS_IS_OK(status)) {
                return;
        }
 
 
        status = notify_load(notify);
        if (!NT_STATUS_IS_OK(status)) {
                return;
        }
 
-       /* TODO: this needs to be changed to a log(n) search */
-       for (i=0;i<notify->array->num_entries;i++) {
-               if (notify_match(notify, &notify->array->entries[i], path, filter)) {
-                       notify_send(notify, &notify->array->entries[i], 
-                                   path + strlen(notify->array->entries[i].path) + 1, 
-                                   action);
+       /* loop along the given path, working with each directory depth separately */
+       for (depth=0,p=path;
+            p && depth < notify->array->num_depths;
+            p=next_p,depth++) {
+               int p_len = p - path;
+               int min_i, max_i, i;
+               struct notify_depth *d = &notify->array->depth[depth];
+               next_p = strchr(p+1, '/');
+
+               /* see if there are any entries at this depth */
+               if (d->num_entries == 0) continue;
+               
+               /* try to skip based on the maximum mask. If next_p is
+                NULL then we know it will be a 'this directory'
+                match, otherwise it must be a subdir match */
+               if (next_p != NULL) {
+                       if (0 == (filter & d->max_mask_subdir)) {
+                               continue;
+                       }
+               } else {
+                       if (0 == (filter & d->max_mask)) {
+                               continue;
+                       }
+               }
+
+               /* we know there is an entry here worth looking
+                for. Use a bisection search to find the first entry
+                with a matching path */
+               min_i = 0;
+               max_i = d->num_entries-1;
+
+               while (min_i < max_i) {
+                       struct notify_entry *e;
+                       int cmp;
+                       i = (min_i+max_i)/2;
+                       e = &d->entries[i];
+                       cmp = strncmp(path, e->path, p_len);
+                       if (cmp == 0) {
+                               if (p_len == e->path_len) {
+                                       max_i = i;
+                               } else {
+                                       max_i = i-1;
+                               }
+                       } else if (cmp < 0) {
+                               max_i = i-1;
+                       } else {
+                               min_i = i+1;
+                       }
+               }
+
+               if (min_i != max_i) {
+                       /* none match */
+                       continue;
+               }
+
+               /* we now know that the entries start at min_i */
+               for (i=min_i;i<d->num_entries;i++) {
+                       struct notify_entry *e = &d->entries[i];
+                       if (p_len != e->path_len ||
+                           strncmp(path, e->path, p_len) != 0) break;
+                       if (next_p != NULL) {
+                               if (0 == (filter & e->subdir_filter)) {
+                                       continue;
+                               }
+                       } else {
+                               if (0 == (filter & e->filter)) {
+                                       continue;
+                               }
+                       }
+                       notify_send(notify, e, path + e->path_len + 1, action);
                }
        }
 }
                }
        }
 }