summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2009-04-14 20:39:14 +0200
committerVolker Lendecke <vl@samba.org>2009-04-16 15:07:06 +0200
commit32a36e470333abae2745e27074a24ab54777b41e (patch)
tree48c56c9ea94b63dd69b19d0ce348dda4a0bb679c /source3/smbd
parentea3a022ca3ed97f0ac3f16536832e8ec43683f8c (diff)
downloadsamba-32a36e470333abae2745e27074a24ab54777b41e.tar.gz
samba-32a36e470333abae2745e27074a24ab54777b41e.tar.bz2
samba-32a36e470333abae2745e27074a24ab54777b41e.zip
Add notify_onelevel.tdb
This optimizes non-recursive notifys. For non-recursive notifies we can use a per-directory file-id indexed notify record. This matters for the Windows Explorer and IIS cases which do not use recursive notifies. In these cases, we do not have to shuffle around the whole notify record on every change. For the cluster case, this improves correctness of the notifies, ctdb only distributes the tdb seqnum once a second, so we can lose notifies.
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/files.c4
-rw-r--r--source3/smbd/notify.c10
-rw-r--r--source3/smbd/notify_internal.c286
3 files changed, 300 insertions, 0 deletions
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