summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/include/proto.h5
-rw-r--r--source3/librpc/gen_ndr/ndr_notify.c63
-rw-r--r--source3/librpc/gen_ndr/ndr_notify.h3
-rw-r--r--source3/librpc/gen_ndr/notify.h7
-rw-r--r--source3/librpc/idl/notify.idl5
-rw-r--r--source3/smbd/files.c4
-rw-r--r--source3/smbd/notify.c10
-rw-r--r--source3/smbd/notify_internal.c286
8 files changed, 383 insertions, 0 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 25e81fde11..07e04edc3b 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -6554,6 +6554,11 @@ NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
void (*callback)(void *, const struct notify_event *),
void *private_data);
NTSTATUS notify_remove(struct notify_context *notify, void *private_data);
+NTSTATUS notify_remove_onelevel(struct notify_context *notify,
+ const struct file_id *fid,
+ void *private_data);
+void notify_onelevel(struct notify_context *notify, uint32_t action,
+ uint32_t filter, struct file_id fid, const char *name);
void notify_trigger(struct notify_context *notify,
uint32_t action, uint32_t filter, const char *path);
diff --git a/source3/librpc/gen_ndr/ndr_notify.c b/source3/librpc/gen_ndr/ndr_notify.c
index d4ac42e961..844c278cd2 100644
--- a/source3/librpc/gen_ndr/ndr_notify.c
+++ b/source3/librpc/gen_ndr/ndr_notify.c
@@ -68,6 +68,69 @@ _PUBLIC_ void ndr_print_notify_entry(struct ndr_print *ndr, const char *name, co
ndr->depth--;
}
+_PUBLIC_ enum ndr_err_code ndr_push_notify_entry_array(struct ndr_push *ndr, int ndr_flags, const struct notify_entry_array *r)
+{
+ uint32_t cntr_entries_0;
+ if (ndr_flags & NDR_SCALARS) {
+ NDR_CHECK(ndr_push_align(ndr, 8));
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->num_entries));
+ for (cntr_entries_0 = 0; cntr_entries_0 < r->num_entries; cntr_entries_0++) {
+ NDR_CHECK(ndr_push_notify_entry(ndr, NDR_SCALARS, &r->entries[cntr_entries_0]));
+ }
+ }
+ if (ndr_flags & NDR_BUFFERS) {
+ for (cntr_entries_0 = 0; cntr_entries_0 < r->num_entries; cntr_entries_0++) {
+ NDR_CHECK(ndr_push_notify_entry(ndr, NDR_BUFFERS, &r->entries[cntr_entries_0]));
+ }
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_pull_notify_entry_array(struct ndr_pull *ndr, int ndr_flags, struct notify_entry_array *r)
+{
+ uint32_t cntr_entries_0;
+ TALLOC_CTX *_mem_save_entries_0;
+ if (ndr_flags & NDR_SCALARS) {
+ NDR_CHECK(ndr_pull_align(ndr, 8));
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->num_entries));
+ NDR_PULL_ALLOC_N(ndr, r->entries, r->num_entries);
+ _mem_save_entries_0 = NDR_PULL_GET_MEM_CTX(ndr);
+ NDR_PULL_SET_MEM_CTX(ndr, r->entries, 0);
+ for (cntr_entries_0 = 0; cntr_entries_0 < r->num_entries; cntr_entries_0++) {
+ NDR_CHECK(ndr_pull_notify_entry(ndr, NDR_SCALARS, &r->entries[cntr_entries_0]));
+ }
+ NDR_PULL_SET_MEM_CTX(ndr, _mem_save_entries_0, 0);
+ }
+ if (ndr_flags & NDR_BUFFERS) {
+ _mem_save_entries_0 = NDR_PULL_GET_MEM_CTX(ndr);
+ NDR_PULL_SET_MEM_CTX(ndr, r->entries, 0);
+ for (cntr_entries_0 = 0; cntr_entries_0 < r->num_entries; cntr_entries_0++) {
+ NDR_CHECK(ndr_pull_notify_entry(ndr, NDR_BUFFERS, &r->entries[cntr_entries_0]));
+ }
+ NDR_PULL_SET_MEM_CTX(ndr, _mem_save_entries_0, 0);
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ void ndr_print_notify_entry_array(struct ndr_print *ndr, const char *name, const struct notify_entry_array *r)
+{
+ uint32_t cntr_entries_0;
+ ndr_print_struct(ndr, name, "notify_entry_array");
+ ndr->depth++;
+ ndr_print_uint32(ndr, "num_entries", r->num_entries);
+ ndr->print(ndr, "%s: ARRAY(%d)", "entries", (int)r->num_entries);
+ ndr->depth++;
+ for (cntr_entries_0=0;cntr_entries_0<r->num_entries;cntr_entries_0++) {
+ char *idx_0=NULL;
+ if (asprintf(&idx_0, "[%d]", cntr_entries_0) != -1) {
+ ndr_print_notify_entry(ndr, "entries", &r->entries[cntr_entries_0]);
+ free(idx_0);
+ }
+ }
+ ndr->depth--;
+ ndr->depth--;
+}
+
static enum ndr_err_code ndr_push_notify_depth(struct ndr_push *ndr, int ndr_flags, const struct notify_depth *r)
{
uint32_t cntr_entries_0;
diff --git a/source3/librpc/gen_ndr/ndr_notify.h b/source3/librpc/gen_ndr/ndr_notify.h
index 23d3d3fc0a..fa2972dbc6 100644
--- a/source3/librpc/gen_ndr/ndr_notify.h
+++ b/source3/librpc/gen_ndr/ndr_notify.h
@@ -10,6 +10,9 @@
enum ndr_err_code ndr_push_notify_entry(struct ndr_push *ndr, int ndr_flags, const struct notify_entry *r);
enum ndr_err_code ndr_pull_notify_entry(struct ndr_pull *ndr, int ndr_flags, struct notify_entry *r);
void ndr_print_notify_entry(struct ndr_print *ndr, const char *name, const struct notify_entry *r);
+enum ndr_err_code ndr_push_notify_entry_array(struct ndr_push *ndr, int ndr_flags, const struct notify_entry_array *r);
+enum ndr_err_code ndr_pull_notify_entry_array(struct ndr_pull *ndr, int ndr_flags, struct notify_entry_array *r);
+void ndr_print_notify_entry_array(struct ndr_print *ndr, const char *name, const struct notify_entry_array *r);
void ndr_print_notify_depth(struct ndr_print *ndr, const char *name, const struct notify_depth *r);
enum ndr_err_code ndr_push_notify_array(struct ndr_push *ndr, int ndr_flags, const struct notify_array *r);
enum ndr_err_code ndr_pull_notify_array(struct ndr_pull *ndr, int ndr_flags, struct notify_array *r);
diff --git a/source3/librpc/gen_ndr/notify.h b/source3/librpc/gen_ndr/notify.h
index a5ec4a46e6..a390fa8a0b 100644
--- a/source3/librpc/gen_ndr/notify.h
+++ b/source3/librpc/gen_ndr/notify.h
@@ -2,6 +2,8 @@
#include <stdint.h>
+#include "libcli/util/ntstatus.h"
+
#ifndef _HEADER_notify
#define _HEADER_notify
@@ -16,6 +18,11 @@ struct notify_entry {
void* private_data;
}/* [public] */;
+struct notify_entry_array {
+ uint32_t num_entries;
+ struct notify_entry *entries;
+}/* [public] */;
+
struct notify_depth {
uint32_t max_mask;
uint32_t max_mask_subdir;
diff --git a/source3/librpc/idl/notify.idl b/source3/librpc/idl/notify.idl
index 550783b5cd..0e80679074 100644
--- a/source3/librpc/idl/notify.idl
+++ b/source3/librpc/idl/notify.idl
@@ -25,6 +25,11 @@ interface notify
pointer private_data;
} notify_entry;
+ typedef [public] struct {
+ uint32 num_entries;
+ notify_entry entries[num_entries];
+ } notify_entry_array;
+
/*
to allow for efficient search for matching entries, we
divide them by the directory depth, with a separate array
diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index 36e80a086a..d2ea520146 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -433,6 +433,10 @@ void file_free(struct smb_request *req, files_struct *fsp)
}
if (fsp->notify) {
+ if (fsp->is_directory) {
+ notify_remove_onelevel(fsp->conn->notify_ctx,
+ &fsp->file_id, fsp);
+ }
notify_remove(fsp->conn->notify_ctx, fsp);
TALLOC_FREE(fsp->notify);
}
diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index d141fb2180..12a75cc9f6 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -339,6 +339,9 @@ void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
const char *path)
{
char *fullpath;
+ char *parent;
+ const char *name;
+ SMB_STRUCT_STAT sbuf;
if (path[0] == '.' && path[1] == '/') {
path += 2;
@@ -348,6 +351,13 @@ void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
return;
}
+ if (parent_dirname(talloc_tos(), path, &parent, &name)
+ && (SMB_VFS_STAT(conn, parent, &sbuf) != -1)) {
+ notify_onelevel(conn->notify_ctx, action, filter,
+ SMB_VFS_FILE_ID_CREATE(conn, &sbuf),
+ name);
+ }
+
notify_trigger(conn->notify_ctx, action, filter, fullpath);
SAFE_FREE(fullpath);
}
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(&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
*/
@@ -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