s3:smbd: move pending_auth_data list to struct smbd_server_connection
[kai/samba.git] / source3 / smbd / notify_internal.c
index afa4e0922bb49332c105eb5669614b6a3262f179..973db7b7b1d1bca4ff0443965fda61a5977d36db 100644 (file)
@@ -2,20 +2,19 @@
    Unix SMB/CIFS implementation.
 
    Copyright (C) Andrew Tridgell 2006
-   
+
    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,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    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 "librpc/gen_ndr/ndr_notify.h"
 
 struct notify_context {
-       struct tdb_wrap *w;
+       struct db_context *db_recursive;
+       struct db_context *db_onelevel;
        struct server_id server;
        struct messaging_context *messaging_ctx;
        struct notify_list *list;
        struct notify_array *array;
        int seqnum;
        struct sys_notify_context *sys_notify_ctx;
+       TDB_DATA key;
 };
 
 
@@ -51,7 +52,8 @@ struct notify_list {
 #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,
+                                 const struct server_id *server);
 static void notify_handler(struct messaging_context *msg_ctx, void *private_data, 
                           uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
 
@@ -61,7 +63,11 @@ static void notify_handler(struct messaging_context *msg_ctx, void *private_data
 static int notify_destructor(struct notify_context *notify)
 {
        messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
-       notify_remove_all(notify);
+
+       if (notify->list != NULL) {
+               notify_remove_all(notify, &notify->server);
+       }
+
        return 0;
 }
 
@@ -86,10 +92,18 @@ struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
                return NULL;
        }
 
-       notify->w = tdb_wrap_open(notify, lock_path("notify.tdb"),
-                                 0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
-                                 O_RDWR|O_CREAT, 0644);
-       if (notify->w == NULL) {
+       notify->db_recursive = db_open(notify, lock_path("notify.tdb"),
+                                      0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
+                                      O_RDWR|O_CREAT, 0644);
+       if (notify->db_recursive == NULL) {
+               talloc_free(notify);
+               return NULL;
+       }
+
+       notify->db_onelevel = db_open(notify, lock_path("notify_onelevel.tdb"),
+                                     0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST,
+                                     O_RDWR|O_CREAT, 0644);
+       if (notify->db_onelevel == NULL) {
                talloc_free(notify);
                return NULL;
        }
@@ -98,7 +112,9 @@ struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
        notify->messaging_ctx = messaging_ctx;
        notify->list = NULL;
        notify->array = NULL;
-       notify->seqnum = tdb_get_seqnum(notify->w->tdb);
+       notify->seqnum = notify->db_recursive->get_seqnum(
+               notify->db_recursive);
+       notify->key = string_term_tdb_data(NOTIFY_KEY);
 
        talloc_set_destructor(notify, notify_destructor);
 
@@ -112,37 +128,30 @@ struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
        return notify;
 }
 
-
 /*
-  lock the notify db
+  lock and fetch the record
 */
-static NTSTATUS notify_lock(struct notify_context *notify)
+static NTSTATUS notify_fetch_locked(struct notify_context *notify, struct db_record **rec)
 {
-       if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
+       *rec = notify->db_recursive->fetch_locked(notify->db_recursive,
+                                                 notify, notify->key);
+       if (*rec == NULL) {
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
        return NT_STATUS_OK;
 }
 
-/*
-  unlock the notify db
-*/
-static void notify_unlock(struct notify_context *notify)
-{
-       tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
-}
-
 /*
   load the notify array
 */
-static NTSTATUS notify_load(struct notify_context *notify)
+static NTSTATUS notify_load(struct notify_context *notify, struct db_record *rec)
 {
        TDB_DATA dbuf;
        DATA_BLOB blob;
        NTSTATUS status;
        int seqnum;
 
-       seqnum = tdb_get_seqnum(notify->w->tdb);
+       seqnum = notify->db_recursive->get_seqnum(notify->db_recursive);
 
        if (seqnum == notify->seqnum && notify->array != NULL) {
                return NT_STATUS_OK;
@@ -151,20 +160,39 @@ static NTSTATUS notify_load(struct notify_context *notify)
        notify->seqnum = seqnum;
 
        talloc_free(notify->array);
-       notify->array = talloc_zero(notify, struct notify_array);
+       notify->array = TALLOC_ZERO_P(notify, struct notify_array);
        NT_STATUS_HAVE_NO_MEMORY(notify->array);
 
-       dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
-       if (dbuf.dptr == NULL) {
-               return NT_STATUS_OK;
+       if (!rec) {
+               if (notify->db_recursive->fetch(notify->db_recursive, notify,
+                                               notify->key, &dbuf) != 0) {
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+       } else {
+               dbuf = rec->value;
        }
 
        blob.data = (uint8 *)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);
-       free(dbuf.dptr);
+       status = NT_STATUS_OK;
+       if (blob.length > 0) {
+               enum ndr_err_code ndr_err;
+               ndr_err = ndr_pull_struct_blob(&blob, notify->array, NULL, notify->array,
+                                              (ndr_pull_flags_fn_t)ndr_pull_notify_array);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       status = ndr_map_error2ntstatus(ndr_err);
+               }
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10, ("notify_load:\n"));
+               NDR_PRINT_DEBUG(notify_array, notify->array);
+       }
+
+       if (!rec) {
+               talloc_free(dbuf.dptr);
+       }
 
        return status;
 }
@@ -182,12 +210,12 @@ static int notify_compare(const void *p1, const void *p2)
 /*
   save the notify array
 */
-static NTSTATUS notify_save(struct notify_context *notify)
+static NTSTATUS notify_save(struct notify_context *notify, struct db_record *rec)
 {
        TDB_DATA dbuf;
        DATA_BLOB blob;
        NTSTATUS status;
-       int ret;
+       enum ndr_err_code ndr_err;
        TALLOC_CTX *tmp_ctx;
 
        /* if possible, remove some depth arrays */
@@ -198,33 +226,31 @@ static NTSTATUS notify_save(struct notify_context *notify)
 
        /* 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;
-               }
-               return NT_STATUS_OK;
+               return rec->delete_rec(rec);
        }
 
        tmp_ctx = talloc_new(notify);
        NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
 
-       status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array, 
+       ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, NULL, notify->array,
                                      (ndr_push_flags_fn_t)ndr_push_notify_array);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(tmp_ctx);
-               return status;
+               return ndr_map_error2ntstatus(ndr_err);
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10, ("notify_save:\n"));
+               NDR_PRINT_DEBUG(notify_array, notify->array);
        }
 
-       dbuf.dptr = (char *)blob.data;
+       dbuf.dptr = blob.data;
        dbuf.dsize = blob.length;
-               
-       ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
+
+       status = rec->store(rec, dbuf, TDB_REPLACE);
        talloc_free(tmp_ctx);
-       if (ret != 0) {
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
-       }
 
-       return NT_STATUS_OK;
+       return status;
 }
 
 
@@ -235,7 +261,7 @@ 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_data, struct notify_context);
-       NTSTATUS status;
+       enum ndr_err_code ndr_err;
        struct notify_event ev;
        TALLOC_CTX *tmp_ctx = talloc_new(notify);
        struct notify_list *listel;
@@ -244,9 +270,9 @@ static void notify_handler(struct messaging_context *msg_ctx, void *private_data
                return;
        }
 
-       status = ndr_pull_struct_blob(data, tmp_ctx, &ev, 
-                                     (ndr_pull_flags_fn_t)ndr_pull_notify_event);
-       if (!NT_STATUS_IS_OK(status)) {
+       ndr_err = ndr_pull_struct_blob(data, tmp_ctx, NULL, &ev,
+                                      (ndr_pull_flags_fn_t)ndr_pull_notify_event);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(tmp_ctx);
                return;
        }
@@ -277,7 +303,8 @@ static void sys_notify_callback(struct sys_notify_context *ctx,
 /*
   add an entry to the notify array
 */
-static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
+static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record *rec,
+                                struct notify_entry *e,
                                 void *private_data, int depth)
 {
        int i;
@@ -325,9 +352,99 @@ static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_en
                d->max_mask_subdir |= d->entries[i].subdir_filter;
        }
 
-       return notify_save(notify);
+       return notify_save(notify, rec);
 }
 
+/*
+  Add a non-recursive watch
+*/
+
+static void notify_add_onelevel(struct notify_context *notify,
+                               struct notify_entry *e, void *private_data)
+{
+       struct notify_entry_array *array;
+       struct db_record *rec;
+       DATA_BLOB blob;
+       TDB_DATA dbuf;
+       enum ndr_err_code ndr_err;
+       NTSTATUS status;
+
+       array = talloc_zero(talloc_tos(), struct notify_entry_array);
+       if (array == NULL) {
+               return;
+       }
+
+       rec = notify->db_onelevel->fetch_locked(
+               notify->db_onelevel, talloc_tos(),
+               make_tdb_data((uint8_t *)&e->dir_id, sizeof(e->dir_id)));
+       if (rec == NULL) {
+               DEBUG(10, ("notify_add_onelevel: fetch_locked for %s failed"
+                          "\n", file_id_string_tos(&e->dir_id)));
+               TALLOC_FREE(array);
+               return;
+       }
+
+       blob.data = (uint8_t *)rec->value.dptr;
+       blob.length = rec->value.dsize;
+
+       if (blob.length > 0) {
+               ndr_err = ndr_pull_struct_blob(
+                       &blob, array, NULL, array,
+                       (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
+                                  ndr_errstr(ndr_err)));
+                       TALLOC_FREE(array);
+                       return;
+               }
+               if (DEBUGLEVEL >= 10) {
+                       DEBUG(10, ("notify_add_onelevel:\n"));
+                       NDR_PRINT_DEBUG(notify_entry_array, array);
+               }
+       }
+
+       array->entries = talloc_realloc(array, array->entries,
+                                       struct notify_entry,
+                                       array->num_entries+1);
+       if (array->entries == NULL) {
+               TALLOC_FREE(array);
+               return;
+       }
+       array->entries[array->num_entries] = *e;
+       array->entries[array->num_entries].private_data = private_data;
+       array->entries[array->num_entries].server = notify->server;
+       array->num_entries += 1;
+
+       ndr_err = ndr_push_struct_blob(
+               &blob, rec, NULL, array,
+               (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
+                          ndr_errstr(ndr_err)));
+               TALLOC_FREE(array);
+               return;
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10, ("notify_add_onelevel:\n"));
+               NDR_PRINT_DEBUG(notify_entry_array, array);
+       }
+
+       dbuf.dptr = blob.data;
+       dbuf.dsize = blob.length;
+
+       status = rec->store(rec, dbuf, TDB_REPLACE);
+       TALLOC_FREE(array);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
+                          nt_errstr(status)));
+               return;
+       }
+       e->filter = 0;
+       return;
+}
+
+
 /*
   add a notify watch. This is called when a notify is first setup on a open
   directory handle.
@@ -342,18 +459,20 @@ NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
        struct notify_list *listel;
        size_t len;
        int depth;
+       struct db_record *rec;
 
        /* see if change notify is enabled at all */
        if (notify == NULL) {
                return NT_STATUS_NOT_IMPLEMENTED;
        }
 
-       status = notify_lock(notify);
+       status = notify_fetch_locked(notify, &rec);
        NT_STATUS_NOT_OK_RETURN(status);
 
-       status = notify_load(notify);
+       status = notify_load(notify, rec);
        if (!NT_STATUS_IS_OK(status)) {
-               goto done;
+               talloc_free(rec);
+               return status;
        }
 
        /* cope with /. on the end of the path */
@@ -369,7 +488,7 @@ NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
 
        depth = count_chars(e.path, '/');
 
-       listel = talloc_zero(notify, struct notify_list);
+       listel = TALLOC_ZERO_P(notify, struct notify_list);
        if (listel == NULL) {
                status = NT_STATUS_NO_MEMORY;
                goto done;
@@ -394,21 +513,122 @@ NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
                }
        }
 
+       if (e.filter != 0) {
+               notify_add_onelevel(notify, &e, private_data);
+               status = NT_STATUS_OK;
+       }
+
        /* 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);
+               status = notify_add_array(notify, rec, &e, private_data, depth);
        }
 
 done:
-       notify_unlock(notify);
+       talloc_free(rec);
        talloc_free(tmp_path);
 
        return status;
 }
 
+NTSTATUS notify_remove_onelevel(struct notify_context *notify,
+                               const struct file_id *fid,
+                               void *private_data)
+{
+       struct notify_entry_array *array;
+       struct db_record *rec;
+       DATA_BLOB blob;
+       TDB_DATA dbuf;
+       enum ndr_err_code ndr_err;
+       NTSTATUS status;
+       int i;
+
+       array = talloc_zero(talloc_tos(), struct notify_entry_array);
+       if (array == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       rec = notify->db_onelevel->fetch_locked(
+               notify->db_onelevel, talloc_tos(),
+               make_tdb_data((uint8_t *)fid, sizeof(*fid)));
+       if (rec == NULL) {
+               DEBUG(10, ("notify_remove_onelevel: fetch_locked for %s failed"
+                          "\n", file_id_string_tos(fid)));
+               TALLOC_FREE(array);
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+       blob.data = (uint8_t *)rec->value.dptr;
+       blob.length = rec->value.dsize;
+
+       if (blob.length > 0) {
+               ndr_err = ndr_pull_struct_blob(
+                       &blob, array, NULL, array,
+                       (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
+                                  ndr_errstr(ndr_err)));
+                       TALLOC_FREE(array);
+                       return ndr_map_error2ntstatus(ndr_err);
+               }
+               if (DEBUGLEVEL >= 10) {
+                       DEBUG(10, ("notify_remove_onelevel:\n"));
+                       NDR_PRINT_DEBUG(notify_entry_array, array);
+               }
+       }
+
+       for (i=0; i<array->num_entries; i++) {
+               if ((private_data == array->entries[i].private_data) &&
+                   cluster_id_equal(&notify->server,
+                                    &array->entries[i].server)) {
+                       break;
+               }
+       }
+
+       if (i == array->num_entries) {
+               TALLOC_FREE(array);
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
+       array->entries[i] = array->entries[array->num_entries-1];
+       array->num_entries -= 1;
+
+       if (array->num_entries == 0) {
+               rec->delete_rec(rec);
+               TALLOC_FREE(array);
+               return NT_STATUS_OK;
+       }
+
+       ndr_err = ndr_push_struct_blob(
+               &blob, rec, NULL, array,
+               (ndr_push_flags_fn_t)ndr_push_notify_entry_array);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(10, ("ndr_push_notify_entry_array failed: %s\n",
+                          ndr_errstr(ndr_err)));
+               TALLOC_FREE(array);
+               return ndr_map_error2ntstatus(ndr_err);
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10, ("notify_add_onelevel:\n"));
+               NDR_PRINT_DEBUG(notify_entry_array, array);
+       }
+
+       dbuf.dptr = blob.data;
+       dbuf.dsize = blob.length;
+
+       status = rec->store(rec, dbuf, TDB_REPLACE);
+       TALLOC_FREE(array);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("notify_add_onelevel: store failed: %s\n",
+                          nt_errstr(status)));
+               return status;
+       }
+       return NT_STATUS_OK;
+}
+
 /*
   remove a notify watch. Called when the directory handle is closed
 */
@@ -418,6 +638,7 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
        struct notify_list *listel;
        int i, depth;
        struct notify_depth *d;
+       struct db_record *rec;
 
        /* see if change notify is enabled at all */
        if (notify == NULL) {
@@ -438,17 +659,17 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
 
        talloc_free(listel);
 
-       status = notify_lock(notify);
+       status = notify_fetch_locked(notify, &rec);
        NT_STATUS_NOT_OK_RETURN(status);
 
-       status = notify_load(notify);
+       status = notify_load(notify, rec);
        if (!NT_STATUS_IS_OK(status)) {
-               notify_unlock(notify);
+               talloc_free(rec);
                return status;
        }
 
        if (depth >= notify->array->num_depths) {
-               notify_unlock(notify);
+               talloc_free(rec);
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
@@ -462,7 +683,7 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
                }
        }
        if (i == d->num_entries) {
-               notify_unlock(notify);
+               talloc_free(rec);
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
@@ -472,40 +693,38 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
        }
        d->num_entries--;
 
-       status = notify_save(notify);
+       status = notify_save(notify, rec);
 
-       notify_unlock(notify);
+       talloc_free(rec);
 
        return status;
 }
 
 /*
-  remove all notify watches for this messaging server
+  remove all notify watches for a messaging server
 */
-static NTSTATUS notify_remove_all(struct notify_context *notify)
+static NTSTATUS notify_remove_all(struct notify_context *notify,
+                                 const struct server_id *server)
 {
        NTSTATUS status;
        int i, depth, del_count=0;
+       struct db_record *rec;
 
-       if (notify->list == NULL) {
-               return NT_STATUS_OK;
-       }
-
-       status = notify_lock(notify);
+       status = notify_fetch_locked(notify, &rec);
        NT_STATUS_NOT_OK_RETURN(status);
 
-       status = notify_load(notify);
+       status = notify_load(notify, rec);
        if (!NT_STATUS_IS_OK(status)) {
-               notify_unlock(notify);
+               talloc_free(rec);
                return status;
        }
 
        /* we have to search for all entries across all depths, looking for matches
-          for our server id */
+          for the 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 (cluster_id_equal(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)));
@@ -518,10 +737,10 @@ static NTSTATUS notify_remove_all(struct notify_context *notify)
        }
 
        if (del_count > 0) {
-               status = notify_save(notify);
+               status = notify_save(notify, rec);
        }
 
-       notify_unlock(notify);
+       talloc_free(rec);
 
        return status;
 }
@@ -530,12 +749,13 @@ static NTSTATUS notify_remove_all(struct notify_context *notify)
 /*
   send a notify message to another messaging server
 */
-static void notify_send(struct notify_context *notify, struct notify_entry *e,
-                       const char *path, uint32_t action)
+static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry *e,
+                           const char *path, uint32_t action)
 {
        struct notify_event ev;
        DATA_BLOB data;
        NTSTATUS status;
+       enum ndr_err_code ndr_err;
        TALLOC_CTX *tmp_ctx;
 
        ev.action = action;
@@ -544,18 +764,109 @@ static void notify_send(struct notify_context *notify, struct notify_entry *e,
 
        tmp_ctx = talloc_new(notify);
 
-       status = ndr_push_struct_blob(&data, tmp_ctx, &ev, 
-                                     (ndr_push_flags_fn_t)ndr_push_notify_event);
-       if (!NT_STATUS_IS_OK(status)) {
+       ndr_err = ndr_push_struct_blob(&data, tmp_ctx, NULL, &ev,
+                                      (ndr_push_flags_fn_t)ndr_push_notify_event);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(tmp_ctx);
-               return;
+               return ndr_map_error2ntstatus(ndr_err);
        }
 
        status = messaging_send(notify->messaging_ctx, e->server, 
                                MSG_PVFS_NOTIFY, &data);
        talloc_free(tmp_ctx);
+       return status;
 }
 
+void notify_onelevel(struct notify_context *notify, uint32_t action,
+                    uint32_t filter, struct file_id fid, const char *name)
+{
+       struct notify_entry_array *array;
+       TDB_DATA dbuf;
+       DATA_BLOB blob;
+       bool have_dead_entries = false;
+       int i;
+
+       if (notify == NULL) {
+               return;
+       }
+
+       array = talloc_zero(talloc_tos(), struct notify_entry_array);
+       if (array == NULL) {
+               return;
+       }
+
+       if (notify->db_onelevel->fetch(
+                   notify->db_onelevel, array,
+                   make_tdb_data((uint8_t *)&fid, sizeof(fid)),
+                   &dbuf) == -1) {
+               TALLOC_FREE(array);
+               return;
+       }
+
+       blob.data = (uint8 *)dbuf.dptr;
+       blob.length = dbuf.dsize;
+
+       if (blob.length > 0) {
+               enum ndr_err_code ndr_err;
+               ndr_err = ndr_pull_struct_blob(
+                       &blob, array, NULL, array,
+                       (ndr_pull_flags_fn_t)ndr_pull_notify_entry_array);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       DEBUG(10, ("ndr_pull_notify_entry_array failed: %s\n",
+                                  ndr_errstr(ndr_err)));
+                       TALLOC_FREE(array);
+                       return;
+               }
+               if (DEBUGLEVEL >= 10) {
+                       DEBUG(10, ("notify_onelevel:\n"));
+                       NDR_PRINT_DEBUG(notify_entry_array, array);
+               }
+       }
+
+       for (i=0; i<array->num_entries; i++) {
+               struct notify_entry *e = &array->entries[i];
+
+               if ((e->filter & filter) != 0) {
+                       NTSTATUS status;
+
+                       status = notify_send(notify, e, name, action);
+                       if (NT_STATUS_EQUAL(
+                                   status, NT_STATUS_INVALID_HANDLE)) {
+                               /*
+                                * Mark the entry as dead. All entries have a
+                                * path set. The marker used here is setting
+                                * that to NULL.
+                                */
+                               e->path = NULL;
+                               have_dead_entries = true;
+                       }
+               }
+       }
+
+       if (!have_dead_entries) {
+               TALLOC_FREE(array);
+               return;
+       }
+
+       for (i=0; i<array->num_entries; i++) {
+               struct notify_entry *e = &array->entries[i];
+               if (e->path != NULL) {
+                       continue;
+               }
+               DEBUG(10, ("Deleting notify entries for process %s because "
+                          "it's gone\n", procid_str_static(&e->server)));
+               /*
+                * Potential TODO: This might need optimizing,
+                * notify_remove_onelevel() does a fetch_locked() operation at
+                * every call. But this would only matter if a process with
+                * MANY notifies has died without shutting down properly.
+                */
+               notify_remove_onelevel(notify, &e->dir_id, e->private_data);
+       }
+
+       TALLOC_FREE(array);
+       return;
+}
 
 /*
   trigger a notify message for anyone waiting on a matching event
@@ -579,7 +890,8 @@ void notify_trigger(struct notify_context *notify,
                return;
        }
 
-       status = notify_load(notify);
+ again:
+       status = notify_load(notify, NULL);
        if (!NT_STATUS_IS_OK(status)) {
                return;
        }
@@ -595,7 +907,7 @@ void notify_trigger(struct notify_context *notify,
 
                /* 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 */
@@ -653,7 +965,19 @@ void notify_trigger(struct notify_context *notify,
                                        continue;
                                }
                        }
-                       notify_send(notify, e, path + e->path_len + 1, action);
+                       status = notify_send(notify, e, path + e->path_len + 1,
+                                            action);
+
+                       if (NT_STATUS_EQUAL(
+                                   status, NT_STATUS_INVALID_HANDLE)) {
+                               struct server_id server = e->server;
+
+                               DEBUG(10, ("Deleting notify entries for "
+                                          "process %s because it's gone\n",
+                                          procid_str_static(&e->server)));
+                               notify_remove_all(notify, &server);
+                               goto again;
+                       }
                }
        }
 }