diff options
Diffstat (limited to 'source3/smbd/notify_internal.c')
-rw-r--r-- | source3/smbd/notify_internal.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/source3/smbd/notify_internal.c b/source3/smbd/notify_internal.c index 1b66865cb6..a42404db3e 100644 --- a/source3/smbd/notify_internal.c +++ b/source3/smbd/notify_internal.c @@ -28,6 +28,7 @@ struct notify_context { struct db_context *db_recursive; + struct db_context *db_onelevel; struct server_id server; struct messaging_context *messaging_ctx; struct notify_list *list; @@ -99,6 +100,14 @@ struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server, 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; + } + notify->server = server; notify->messaging_ctx = messaging_ctx; notify->list = NULL; @@ -347,6 +356,96 @@ static NTSTATUS notify_add_array(struct notify_context *notify, struct db_record } /* + 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. */ @@ -414,6 +513,11 @@ 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 @@ -429,6 +533,102 @@ done: 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(¬ify->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 */ @@ -577,6 +777,92 @@ static NTSTATUS notify_send(struct notify_context *notify, struct notify_entry * 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; + + 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 |