[PATCH] avoid lock oder violation between xattr.tdb and g_lock.tdb

Volker Lendecke vl at samba.org
Thu Jul 14 05:52:28 UTC 2016


On Tue, Jul 12, 2016 at 05:12:48PM +0200, Michael Adam wrote:
> > Not sure this will work. g_lock is a watched database, and if there is
> > lock contention, this will conflict with dbwrap_watchers.tdb.
> 
> Ouch. So without the below-mentioned rework of dbwrap_watch
> (or another solution), we would need an additional lock oder
> value(4)?

Attached find a patchset that survived a private autobuild for me.

It is incomplete as it does not have the removal patch for the current
version of dbwrap_record_watch_send. Also, I'm not sure about the
performance implications of the malloc/memcpy when storing large
records. But the typical record is normally not too large. The only
case where this could matter is highly popular share root directories
in locking.tdb. So sooner than later we might have to add a tdb_storev
call and the related dbwrap support.

Comments?

Volker
-------------- next part --------------
>From 886b4b5086711d7b20fc505cf20a57ba2bed292a Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 12 Jul 2016 15:33:59 +0200
Subject: [PATCH 1/9] lib: Add server_id_watch_send

This is a brute force variant, trying twice a second. We'll have better
variants with tmsgd in the future.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/server_id_watch.c | 106 ++++++++++++++++++++++++++++++++++++++++++
 source3/lib/server_id_watch.h |  36 ++++++++++++++
 source3/wscript_build         |   1 +
 3 files changed, 143 insertions(+)
 create mode 100644 source3/lib/server_id_watch.c
 create mode 100644 source3/lib/server_id_watch.h

diff --git a/source3/lib/server_id_watch.c b/source3/lib/server_id_watch.c
new file mode 100644
index 0000000..0dfeb8c
--- /dev/null
+++ b/source3/lib/server_id_watch.c
@@ -0,0 +1,106 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Wait for process death
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include <tevent.h>
+#include <talloc.h>
+#include "serverid.h"
+#include "server_id_watch.h"
+#include "messages.h"
+#include "lib/util/tevent_unix.h"
+
+struct server_id_watch_state {
+	struct tevent_context *ev;
+	struct server_id pid;
+};
+
+static void server_id_watch_waited(struct tevent_req *subreq);
+
+struct tevent_req *server_id_watch_send(TALLOC_CTX *mem_ctx,
+					struct tevent_context *ev,
+					struct messaging_context *msg,
+					struct server_id pid)
+{
+	struct tevent_req *req, *subreq;
+	struct server_id_watch_state *state;
+
+	req = tevent_req_create(mem_ctx, &state, struct server_id_watch_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->pid = pid;
+
+	if (!serverid_exists(&state->pid)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(0, 500000));
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, server_id_watch_waited, req);
+
+	return req;
+}
+
+static void server_id_watch_waited(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct server_id_watch_state *state = tevent_req_data(
+		req, struct server_id_watch_state);
+	bool ok;
+
+	ok = tevent_wakeup_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (!ok) {
+		tevent_req_oom(req);
+		return;
+	}
+
+	if (!serverid_exists(&state->pid)) {
+		tevent_req_done(req);
+		return;
+	}
+
+	subreq = tevent_wakeup_send(state, state->ev,
+				    timeval_current_ofs(0, 500000));
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, server_id_watch_waited, req);
+}
+
+int server_id_watch_recv(struct tevent_req *req, struct server_id *pid)
+{
+	struct server_id_watch_state *state = tevent_req_data(
+		req, struct server_id_watch_state);
+	int err;
+
+	if (tevent_req_is_unix_error(req, &err)) {
+		return err;
+	}
+	if (pid) {
+		*pid = state->pid;
+	}
+	return 0;
+}
+
diff --git a/source3/lib/server_id_watch.h b/source3/lib/server_id_watch.h
new file mode 100644
index 0000000..917dfeb
--- /dev/null
+++ b/source3/lib/server_id_watch.h
@@ -0,0 +1,36 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Wait for process death
+ * Copyright (C) Volker Lendecke 2016
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIB_SERVER_ID_WATCH_H__
+#define __LIB_SERVER_ID_WATCH_H__
+
+#include "replace.h"
+#include <tevent.h>
+#include <talloc.h>
+#include "librpc/gen_ndr/server_id.h"
+
+struct messaging_context;
+
+struct tevent_req *server_id_watch_send(TALLOC_CTX *mem_ctx,
+					struct tevent_context *ev,
+					struct messaging_context *msg,
+					struct server_id pid);
+int server_id_watch_recv(struct tevent_req *req, struct server_id *pid);
+
+#endif
diff --git a/source3/wscript_build b/source3/wscript_build
index 365b250..b9a2ee6 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -317,6 +317,7 @@ bld.SAMBA3_SUBSYSTEM('samba3core',
                    lib/id_cache.c
                    lib/talloc_dict.c
                    lib/serverid.c
+                   lib/server_id_watch.c
                    lib/server_id_db_util.c
                    lib/addrchange.c
                    ../lib/util/debug_s3.c
-- 
1.9.1


>From 2b1a4a3053f761b3af584d3314b183dc300c89ef Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 10 Mar 2016 14:37:12 +0100
Subject: [PATCH 2/9] dbwrap: Add "blocker" to record_watch_send

Typicall, when we watch a record, we wait for a process to give up some
resource. Be it an oplock, a share mode or the g_lock. If everything goes well,
the blocker sends us a message. If the blocker dies hard, we want to also be
informed immediately.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/dbwrap/dbwrap_watch.c   | 45 +++++++++++++++++++++++++++++++++++--
 source3/lib/dbwrap/dbwrap_watch.h   |  7 ++++--
 source3/lib/g_lock.c                |  9 +++++---
 source3/smbd/open.c                 |  5 +++--
 source3/smbd/smb2_setinfo.c         |  6 +++--
 source3/smbd/smbXsrv_session.c      |  6 +++--
 source3/torture/test_dbwrap_watch.c |  6 +++--
 7 files changed, 69 insertions(+), 15 deletions(-)

diff --git a/source3/lib/dbwrap/dbwrap_watch.c b/source3/lib/dbwrap/dbwrap_watch.c
index 09e67fb..714c54d 100644
--- a/source3/lib/dbwrap/dbwrap_watch.c
+++ b/source3/lib/dbwrap/dbwrap_watch.c
@@ -24,6 +24,7 @@
 #include "dbwrap_open.h"
 #include "lib/util/util_tdb.h"
 #include "lib/util/tevent_ntstatus.h"
+#include "server_id_watch.h"
 
 static struct db_context *dbwrap_record_watchers_db(void)
 {
@@ -199,18 +200,22 @@ struct dbwrap_record_watch_state {
 	struct tevent_req *req;
 	struct messaging_context *msg;
 	TDB_DATA w_key;
+	bool blockerdead;
+	struct server_id blocker;
 };
 
 static bool dbwrap_record_watch_filter(struct messaging_rec *rec,
 				       void *private_data);
 static void dbwrap_record_watch_done(struct tevent_req *subreq);
+static void dbwrap_record_watch_blocker_died(struct tevent_req *subreq);
 static int dbwrap_record_watch_state_destructor(
 	struct dbwrap_record_watch_state *state);
 
 struct tevent_req *dbwrap_record_watch_send(TALLOC_CTX *mem_ctx,
 					    struct tevent_context *ev,
 					    struct db_record *rec,
-					    struct messaging_context *msg)
+					    struct messaging_context *msg,
+					    struct server_id blocker)
 {
 	struct tevent_req *req, *subreq;
 	struct dbwrap_record_watch_state *state;
@@ -226,6 +231,7 @@ struct tevent_req *dbwrap_record_watch_send(TALLOC_CTX *mem_ctx,
 	state->ev = ev;
 	state->req = req;
 	state->msg = msg;
+	state->blocker = blocker;
 
 	watchers_db = dbwrap_record_watchers_db();
 	if (watchers_db == NULL) {
@@ -250,6 +256,15 @@ struct tevent_req *dbwrap_record_watch_send(TALLOC_CTX *mem_ctx,
 	}
 	tevent_req_set_callback(subreq, dbwrap_record_watch_done, req);
 
+	if (blocker.pid != 0) {
+		subreq = server_id_watch_send(state, ev, msg, blocker);
+		if (tevent_req_nomem(subreq, req)) {
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_set_callback(
+			subreq, dbwrap_record_watch_blocker_died, req);
+	}
+
 	status = dbwrap_record_add_watcher(
 		state->w_key, messaging_server_id(state->msg));
 	if (tevent_req_nterror(req, status)) {
@@ -371,9 +386,29 @@ static void dbwrap_record_watch_done(struct tevent_req *subreq)
 	tevent_req_done(req);
 }
 
+static void dbwrap_record_watch_blocker_died(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct dbwrap_record_watch_state *state = tevent_req_data(
+		req, struct dbwrap_record_watch_state);
+	int ret;
+
+	ret = server_id_watch_recv(subreq, NULL);
+	TALLOC_FREE(subreq);
+	if (ret != 0) {
+		tevent_req_nterror(req, map_nt_error_from_unix(ret));
+		return;
+	}
+	state->blockerdead = true;
+	tevent_req_done(req);
+}
+
 NTSTATUS dbwrap_record_watch_recv(struct tevent_req *req,
 				  TALLOC_CTX *mem_ctx,
-				  struct db_record **prec)
+				  struct db_record **prec,
+				  bool *blockerdead,
+				  struct server_id *blocker)
 {
 	struct dbwrap_record_watch_state *state = tevent_req_data(
 		req, struct dbwrap_record_watch_state);
@@ -385,6 +420,12 @@ NTSTATUS dbwrap_record_watch_recv(struct tevent_req *req,
 	if (tevent_req_is_nterror(req, &status)) {
 		return status;
 	}
+	if (blockerdead != NULL) {
+		*blockerdead = state->blockerdead;
+	}
+	if (blocker != NULL) {
+		*blocker = state->blocker;
+	}
 	if (prec == NULL) {
 		return NT_STATUS_OK;
 	}
diff --git a/source3/lib/dbwrap/dbwrap_watch.h b/source3/lib/dbwrap/dbwrap_watch.h
index 3362e45..b14128c 100644
--- a/source3/lib/dbwrap/dbwrap_watch.h
+++ b/source3/lib/dbwrap/dbwrap_watch.h
@@ -29,10 +29,13 @@ void dbwrap_watch_db(struct db_context *db, struct messaging_context *msg);
 struct tevent_req *dbwrap_record_watch_send(TALLOC_CTX *mem_ctx,
 					    struct tevent_context *ev,
 					    struct db_record *rec,
-					    struct messaging_context *msg);
+					    struct messaging_context *msg,
+					    struct server_id blocker);
 NTSTATUS dbwrap_record_watch_recv(struct tevent_req *req,
 				  TALLOC_CTX *mem_ctx,
-				  struct db_record **prec);
+				  struct db_record **prec,
+				  bool *blockerdead,
+				  struct server_id *blocker);
 
 void dbwrap_watchers_traverse_read(
 	int (*fn)(const uint8_t *db_id, size_t db_id_len, const TDB_DATA key,
diff --git a/source3/lib/g_lock.c b/source3/lib/g_lock.c
index 1928f5e..1976291 100644
--- a/source3/lib/g_lock.c
+++ b/source3/lib/g_lock.c
@@ -237,7 +237,8 @@ struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 	subreq = dbwrap_record_watch_send(state, state->ev, rec,
-					  state->ctx->msg);
+					  state->ctx->msg,
+					  (struct server_id){0});
 	TALLOC_FREE(rec);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
@@ -262,7 +263,8 @@ static void g_lock_lock_retry(struct tevent_req *subreq)
 	struct db_record *rec;
 	NTSTATUS status;
 
-	status = dbwrap_record_watch_recv(subreq, talloc_tos(), &rec);
+	status = dbwrap_record_watch_recv(subreq, talloc_tos(), &rec, NULL,
+					  NULL);
 	TALLOC_FREE(subreq);
 
 	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
@@ -291,7 +293,8 @@ static void g_lock_lock_retry(struct tevent_req *subreq)
 		return;
 	}
 	subreq = dbwrap_record_watch_send(state, state->ev, rec,
-					  state->ctx->msg);
+					  state->ctx->msg,
+					  (struct server_id){0});
 	TALLOC_FREE(rec);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 883c6ae..57cd4f1 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1954,7 +1954,7 @@ static void defer_open(struct share_mode_lock *lck,
 
 		watch_req = dbwrap_record_watch_send(
 			watch_state, req->sconn->ev_ctx, lck->data->record,
-			req->sconn->msg_ctx);
+			req->sconn->msg_ctx, (struct server_id){0});
 		if (watch_req == NULL) {
 			exit_server("Could not watch share mode record");
 		}
@@ -1981,7 +1981,8 @@ static void defer_open_done(struct tevent_req *req)
 	NTSTATUS status;
 	bool ret;
 
-	status = dbwrap_record_watch_recv(req, talloc_tos(), NULL);
+	status = dbwrap_record_watch_recv(req, talloc_tos(), NULL, NULL,
+					  NULL);
 	TALLOC_FREE(req);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c
index 2a02610..0a678ea 100644
--- a/source3/smbd/smb2_setinfo.c
+++ b/source3/smbd/smb2_setinfo.c
@@ -252,7 +252,8 @@ static struct tevent_req *delay_rename_for_lease_break(struct tevent_req *req,
 				rename_state,
 				ev,
 				lck->data->record,
-				fsp->conn->sconn->msg_ctx);
+				fsp->conn->sconn->msg_ctx,
+				(struct server_id){0});
 
 	if (subreq == NULL) {
 		exit_server("Could not watch share mode record for rename\n");
@@ -279,7 +280,8 @@ static void defer_rename_done(struct tevent_req *subreq)
 	int ret_size = 0;
 	bool ok;
 
-	status = dbwrap_record_watch_recv(subreq, state->req, NULL);
+	status = dbwrap_record_watch_recv(subreq, state->req, NULL, NULL,
+					  NULL);
 	TALLOC_FREE(subreq);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c
index cdad47f..51668c2 100644
--- a/source3/smbd/smbXsrv_session.c
+++ b/source3/smbd/smbXsrv_session.c
@@ -1066,7 +1066,8 @@ static void smb2srv_session_close_previous_check(struct tevent_req *req)
 	}
 
 	subreq = dbwrap_record_watch_send(state, state->ev,
-					  state->db_rec, conn->msg_ctx);
+					  state->db_rec, conn->msg_ctx,
+					  (struct server_id){0});
 	if (tevent_req_nomem(subreq, req)) {
 		TALLOC_FREE(state->db_rec);
 		return;
@@ -1120,7 +1121,8 @@ static void smb2srv_session_close_previous_modified(struct tevent_req *subreq)
 		struct smb2srv_session_close_previous_state);
 	NTSTATUS status;
 
-	status = dbwrap_record_watch_recv(subreq, state, &state->db_rec);
+	status = dbwrap_record_watch_recv(subreq, state, &state->db_rec, NULL,
+					  NULL);
 	TALLOC_FREE(subreq);
 	if (tevent_req_nterror(req, status)) {
 		return;
diff --git a/source3/torture/test_dbwrap_watch.c b/source3/torture/test_dbwrap_watch.c
index ab9330f..a912bd2 100644
--- a/source3/torture/test_dbwrap_watch.c
+++ b/source3/torture/test_dbwrap_watch.c
@@ -60,7 +60,8 @@ bool run_dbwrap_watch1(int dummy)
 		fprintf(stderr, "dbwrap_fetch_locked failed\n");
 		goto fail;
 	}
-	req = dbwrap_record_watch_send(talloc_tos(), ev, rec, msg);
+	req = dbwrap_record_watch_send(talloc_tos(), ev, rec, msg,
+				       (struct server_id){0});
 	if (req == NULL) {
 		fprintf(stderr, "dbwrap_record_watch_send failed\n");
 		goto fail;
@@ -86,7 +87,8 @@ bool run_dbwrap_watch1(int dummy)
 		goto fail;
 	}
 
-	status = dbwrap_record_watch_recv(req, talloc_tos(), &rec);
+	status = dbwrap_record_watch_recv(req, talloc_tos(), &rec, NULL,
+					  NULL);
 	if (!NT_STATUS_IS_OK(status)) {
 		fprintf(stderr, "dbwrap_record_watch_recv failed: %s\n",
 			nt_errstr(status));
-- 
1.9.1


>From 37954329c547a9b903a78f038999b08bde1881d5 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 12 Jul 2016 16:07:51 +0200
Subject: [PATCH 3/9] g_lock: Use "blocker" argument to
 dbwrap_record_watch_send

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/g_lock.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/source3/lib/g_lock.c b/source3/lib/g_lock.c
index 1976291..7b91e3c 100644
--- a/source3/lib/g_lock.c
+++ b/source3/lib/g_lock.c
@@ -111,7 +111,8 @@ static bool g_lock_parse(TALLOC_CTX *mem_ctx, TDB_DATA data,
 }
 
 static NTSTATUS g_lock_trylock(struct db_record *rec, struct server_id self,
-			       enum g_lock_type type)
+			       enum g_lock_type type,
+			       struct server_id *blocker)
 {
 	TDB_DATA data;
 	unsigned i, num_locks;
@@ -142,6 +143,7 @@ static NTSTATUS g_lock_trylock(struct db_record *rec, struct server_id self,
 
 			if (serverid_exists(&pid)) {
 				status = NT_STATUS_LOCK_NOT_GRANTED;
+				*blocker = locks[i].pid;
 				goto done;
 			}
 
@@ -203,7 +205,7 @@ struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx,
 	struct tevent_req *req, *subreq;
 	struct g_lock_lock_state *state;
 	struct db_record *rec;
-	struct server_id self;
+	struct server_id self, blocker;
 	NTSTATUS status;
 
 	req = tevent_req_create(mem_ctx, &state, struct g_lock_lock_state);
@@ -225,7 +227,7 @@ struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx,
 
 	self = messaging_server_id(state->ctx->msg);
 
-	status = g_lock_trylock(rec, self, state->type);
+	status = g_lock_trylock(rec, self, state->type, &blocker);
 	if (NT_STATUS_IS_OK(status)) {
 		TALLOC_FREE(rec);
 		tevent_req_done(req);
@@ -237,8 +239,7 @@ struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 	subreq = dbwrap_record_watch_send(state, state->ev, rec,
-					  state->ctx->msg,
-					  (struct server_id){0});
+					  state->ctx->msg, blocker);
 	TALLOC_FREE(rec);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
@@ -260,6 +261,7 @@ static void g_lock_lock_retry(struct tevent_req *subreq)
 	struct g_lock_lock_state *state = tevent_req_data(
 		req, struct g_lock_lock_state);
 	struct server_id self = messaging_server_id(state->ctx->msg);
+	struct server_id blocker;
 	struct db_record *rec;
 	NTSTATUS status;
 
@@ -281,7 +283,7 @@ static void g_lock_lock_retry(struct tevent_req *subreq)
 	if (tevent_req_nterror(req, status)) {
 		return;
 	}
-	status = g_lock_trylock(rec, self, state->type);
+	status = g_lock_trylock(rec, self, state->type, &blocker);
 	if (NT_STATUS_IS_OK(status)) {
 		TALLOC_FREE(rec);
 		tevent_req_done(req);
@@ -293,8 +295,7 @@ static void g_lock_lock_retry(struct tevent_req *subreq)
 		return;
 	}
 	subreq = dbwrap_record_watch_send(state, state->ev, rec,
-					  state->ctx->msg,
-					  (struct server_id){0});
+					  state->ctx->msg, blocker);
 	TALLOC_FREE(rec);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
-- 
1.9.1


>From 8f09c234b461531cad498a82edce1291d18e58a5 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 12 Jul 2016 15:57:29 +0200
Subject: [PATCH 4/9] dbwrap: Add overflow protection to
 dbwrap_record_watchers_key()

It's highly unlinkely that this will ever kick in, because our current tdb keys
are rather small, but offset calculations without overflow checks are bad.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/dbwrap/dbwrap_watch.c | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/source3/lib/dbwrap/dbwrap_watch.c b/source3/lib/dbwrap/dbwrap_watch.c
index 714c54d..a6e032a 100644
--- a/source3/lib/dbwrap/dbwrap_watch.c
+++ b/source3/lib/dbwrap/dbwrap_watch.c
@@ -46,9 +46,9 @@ static struct db_context *dbwrap_record_watchers_db(void)
 	return watchers_db;
 }
 
-static size_t dbwrap_record_watchers_key(struct db_context *db,
-					 struct db_record *rec,
-					 uint8_t *wkey, size_t wkey_len)
+static ssize_t dbwrap_record_watchers_key(struct db_context *db,
+					  struct db_record *rec,
+					  uint8_t *wkey, size_t wkey_len)
 {
 	size_t db_id_len = dbwrap_db_id(db, NULL, 0);
 	uint8_t db_id[db_id_len];
@@ -59,7 +59,15 @@ static size_t dbwrap_record_watchers_key(struct db_context *db,
 
 	key = dbwrap_record_get_key(rec);
 
-	needed = sizeof(uint32_t) + db_id_len + key.dsize;
+	needed = sizeof(uint32_t) + db_id_len;
+	if (needed < sizeof(uint32_t)) {
+		return -1;
+	}
+
+	needed += key.dsize;
+	if (needed < key.dsize) {
+		return -1;
+	}
 
 	if (wkey_len >= needed) {
 		SIVAL(wkey, 0, db_id_len);
@@ -221,6 +229,7 @@ struct tevent_req *dbwrap_record_watch_send(TALLOC_CTX *mem_ctx,
 	struct dbwrap_record_watch_state *state;
 	struct db_context *watchers_db;
 	NTSTATUS status;
+	ssize_t needed;
 
 	req = tevent_req_create(mem_ctx, &state,
 				struct dbwrap_record_watch_state);
@@ -239,8 +248,12 @@ struct tevent_req *dbwrap_record_watch_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
-	state->w_key.dsize = dbwrap_record_watchers_key(
-		state->db, rec, NULL, 0);
+	needed = dbwrap_record_watchers_key(state->db, rec, NULL, 0);
+	if (needed == -1) {
+		tevent_req_nterror(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+		return tevent_req_post(req, ev);
+	}
+	state->w_key.dsize = needed;
 
 	state->w_key.dptr = talloc_array(state, uint8_t, state->w_key.dsize);
 	if (tevent_req_nomem(state->w_key.dptr, req)) {
-- 
1.9.1


>From 3bf4bf1ccff967397214aa954acac4d9139a9598 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 12 Jul 2016 15:59:56 +0200
Subject: [PATCH 5/9] dbwrap: Add an alternative implementation of
 dbwrap_watch_record_send

The existing one with a separate dbwrap_watchers.tdb turns out to
create a performance penalty in a clustered environment. Non-clustered,
dbwrap_parse_record on non-existent records is very cheap, but in a
cluster environment this is very noticable.

This implementation puts the watcher information into the records itself. For
large records, this might be another performance penalty, because we have to
assemble the final record together with talloc and memcpy, but this might be
fixed later with a tdb_storev call.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/dbwrap/dbwrap_watch.c   | 755 ++++++++++++++++++++++++++++++++++++
 source3/lib/dbwrap/dbwrap_watch.h   |  12 +
 source3/torture/test_dbwrap_watch.c |  21 +-
 3 files changed, 779 insertions(+), 9 deletions(-)

diff --git a/source3/lib/dbwrap/dbwrap_watch.c b/source3/lib/dbwrap/dbwrap_watch.c
index a6e032a..f2cc381 100644
--- a/source3/lib/dbwrap/dbwrap_watch.c
+++ b/source3/lib/dbwrap/dbwrap_watch.c
@@ -25,6 +25,7 @@
 #include "lib/util/util_tdb.h"
 #include "lib/util/tevent_ntstatus.h"
 #include "server_id_watch.h"
+#include "lib/dbwrap/dbwrap_private.h"
 
 static struct db_context *dbwrap_record_watchers_db(void)
 {
@@ -533,3 +534,757 @@ void dbwrap_watchers_wakeall(struct messaging_context *msg)
 {
 	dbwrap_watchers_traverse_read(dbwrap_wakeall_cb, msg);
 }
+
+#define NUM_WATCHERS_DELETED_BIT (1UL<<31)
+#define NUM_WATCHERS_MASK (NUM_WATCHERS_DELETED_BIT-1)
+
+static ssize_t dbwrap_watched_parse(TDB_DATA data, struct server_id *ids,
+				    size_t num_ids, bool *pdeleted,
+				    TDB_DATA *pdata)
+{
+	size_t i, num_watchers;
+	bool deleted;
+
+	if (data.dsize < sizeof(uint32_t)) {
+		/* Fresh or invalid record */
+		return -1;
+	}
+
+	num_watchers = IVAL(data.dptr, 0);
+
+	deleted = num_watchers & NUM_WATCHERS_DELETED_BIT;
+	num_watchers &= NUM_WATCHERS_MASK;
+
+	data.dptr += sizeof(uint32_t);
+	data.dsize -= sizeof(uint32_t);
+
+	if (num_watchers > data.dsize/SERVER_ID_BUF_LENGTH) {
+		/* Invalid record */
+		return -1;
+	}
+
+	if (num_watchers > num_ids) {
+		data.dptr += num_watchers * SERVER_ID_BUF_LENGTH;
+		data.dsize -= num_watchers * SERVER_ID_BUF_LENGTH;
+		goto done;
+	}
+
+	for (i=0; i<num_watchers; i++) {
+		server_id_get(&ids[i], data.dptr);
+		data.dptr += SERVER_ID_BUF_LENGTH;
+		data.dsize -= SERVER_ID_BUF_LENGTH;
+	}
+
+done:
+	if (deleted) {
+		data = (TDB_DATA) {0};
+	}
+	if (pdata != NULL) {
+		*pdata = data;
+	}
+	if (pdeleted != NULL) {
+		*pdeleted = deleted;
+	}
+
+	return num_watchers;
+}
+
+static ssize_t dbwrap_watched_unparse(const struct server_id *watchers,
+				      size_t num_watchers, bool deleted,
+				      TDB_DATA data,
+				      uint8_t *buf, size_t buflen)
+{
+	size_t i, len, ofs;
+	uint32_t num_watchers_buf;
+
+	if (num_watchers > UINT32_MAX/SERVER_ID_BUF_LENGTH) {
+		return -1;
+	}
+
+	len = num_watchers * SERVER_ID_BUF_LENGTH;
+
+	len += sizeof(uint32_t);
+	if (len < sizeof(uint32_t)) {
+		return -1;
+	}
+
+	len += data.dsize;
+	if (len < data.dsize) {
+		return -1;
+	}
+
+	if (len > buflen) {
+		return len;
+	}
+
+	num_watchers_buf = num_watchers;
+	if (deleted) {
+		num_watchers_buf |= NUM_WATCHERS_DELETED_BIT;
+	}
+
+	ofs = 0;
+	SIVAL(buf, ofs, num_watchers_buf);
+	ofs += 4;
+
+	for (i=0; i<num_watchers; i++) {
+		server_id_put(buf+ofs, watchers[i]);
+		ofs += SERVER_ID_BUF_LENGTH;
+	}
+
+	if ((data.dptr != NULL) && (data.dsize != 0)) {
+		memcpy(buf + ofs, data.dptr, data.dsize);
+	}
+
+	return len;
+}
+
+struct db_watched_ctx {
+	struct db_context *backend;
+	struct messaging_context *msg;
+};
+
+struct db_watched_subrec {
+	struct db_record *subrec;
+	struct server_id *watchers;
+	bool deleted;
+};
+
+static NTSTATUS dbwrap_watched_store(struct db_record *rec, TDB_DATA data,
+				     int flag);
+static NTSTATUS dbwrap_watched_delete(struct db_record *rec);
+
+static struct db_record *dbwrap_watched_fetch_locked(
+	struct db_context *db, TALLOC_CTX *mem_ctx, TDB_DATA key)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	struct db_record *rec;
+	struct db_watched_subrec *subrec;
+	TDB_DATA subrec_value;
+	ssize_t num_watchers;
+
+	rec = talloc_zero(mem_ctx, struct db_record);
+	if (rec == NULL) {
+		return NULL;
+	}
+	subrec = talloc_zero(rec, struct db_watched_subrec);
+	if (subrec == NULL) {
+		TALLOC_FREE(rec);
+		return NULL;
+	}
+	rec->private_data = subrec;
+
+	subrec->subrec = dbwrap_fetch_locked(ctx->backend, subrec, key);
+	if (subrec->subrec == NULL) {
+		TALLOC_FREE(rec);
+		return NULL;
+	}
+
+	rec->db = db;
+	rec->key = dbwrap_record_get_key(subrec->subrec);
+	rec->store = dbwrap_watched_store;
+	rec->delete_rec = dbwrap_watched_delete;
+
+	subrec_value = dbwrap_record_get_value(subrec->subrec);
+
+	num_watchers = dbwrap_watched_parse(subrec_value, NULL, 0, NULL, NULL);
+	if (num_watchers == -1) {
+		/* Fresh or invalid record */
+		rec->value = (TDB_DATA) {};
+		return rec;
+	}
+
+	subrec->watchers = talloc_array(subrec, struct server_id,
+					num_watchers);
+	if (subrec->watchers == NULL) {
+		TALLOC_FREE(rec);
+		return NULL;
+	}
+
+	dbwrap_watched_parse(subrec_value, subrec->watchers, num_watchers,
+			     &subrec->deleted, &rec->value);
+
+	return rec;
+}
+
+static void dbwrap_watched_wakeup(struct db_record *rec,
+				  struct db_watched_subrec *subrec)
+{
+	struct db_context *db = dbwrap_record_get_db(rec);
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	size_t i, num_watchers;
+	size_t db_id_len = dbwrap_db_id(db, NULL, 0);
+	uint8_t db_id[db_id_len];
+	uint8_t len_buf[4];
+	struct iovec iov[3];
+
+	SIVAL(len_buf, 0, db_id_len);
+
+	iov[0] = (struct iovec) { .iov_base = len_buf, .iov_len = 4 };
+	iov[1] = (struct iovec) { .iov_base = db_id, .iov_len = db_id_len };
+	iov[2] = (struct iovec) { .iov_base = rec->key.dptr,
+				  .iov_len = rec->key.dsize };
+
+	dbwrap_db_id(db, db_id, db_id_len);
+
+	num_watchers = talloc_array_length(subrec->watchers);
+
+	i = 0;
+
+	while (i < num_watchers) {
+		NTSTATUS status;
+		struct server_id_buf tmp;
+
+		DBG_DEBUG("Alerting %s\n",
+			  server_id_str_buf(subrec->watchers[i], &tmp));
+
+		status = messaging_send_iov(ctx->msg, subrec->watchers[i],
+					    MSG_DBWRAP_MODIFIED,
+					    iov, ARRAY_SIZE(iov), NULL, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_DEBUG("messaging_send_iov to %s failed: %s\n",
+				  server_id_str_buf(subrec->watchers[i], &tmp),
+				  nt_errstr(status));
+		}
+		if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+			subrec->watchers[i] = subrec->watchers[num_watchers-1];
+			num_watchers -= 1;
+
+			subrec->watchers = talloc_realloc(
+				subrec, subrec->watchers, struct server_id,
+				num_watchers);
+			continue;
+		}
+
+		i += 1;
+	}
+}
+
+static NTSTATUS dbwrap_watched_save(struct db_watched_subrec *subrec,
+				    TDB_DATA data, int flag)
+{
+	size_t num_watchers;
+	ssize_t len;
+	uint8_t *buf;
+	NTSTATUS status;
+
+	num_watchers = talloc_array_length(subrec->watchers);
+
+	len = dbwrap_watched_unparse(subrec->watchers, num_watchers,
+				     subrec->deleted, data, NULL, 0);
+	if (len == -1) {
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+
+	buf = talloc_array(subrec, uint8_t, len);
+	if (buf == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	dbwrap_watched_unparse(subrec->watchers, num_watchers,
+			       subrec->deleted, data, buf, len);
+
+	status = dbwrap_record_store(
+		subrec->subrec, (TDB_DATA) { .dptr = buf, .dsize = len },
+		flag);
+
+	TALLOC_FREE(buf);
+
+	return status;
+}
+
+static NTSTATUS dbwrap_watched_store(struct db_record *rec, TDB_DATA data,
+				     int flag)
+{
+	struct db_watched_subrec *subrec = talloc_get_type_abort(
+		rec->private_data, struct db_watched_subrec);
+
+	dbwrap_watched_wakeup(rec, subrec);
+
+	subrec->deleted = false;
+
+	return dbwrap_watched_save(subrec, data, flag);
+
+}
+
+static NTSTATUS dbwrap_watched_delete(struct db_record *rec)
+{
+	struct db_watched_subrec *subrec = talloc_get_type_abort(
+		rec->private_data, struct db_watched_subrec);
+	size_t num_watchers;
+
+	dbwrap_watched_wakeup(rec, subrec);
+
+	num_watchers = talloc_array_length(subrec->watchers);
+	if (num_watchers == 0) {
+		return dbwrap_record_delete(subrec->subrec);
+	}
+
+	subrec->deleted = true;
+
+	return dbwrap_watched_save(subrec, (TDB_DATA) {0}, 0);
+}
+
+struct dbwrap_watched_traverse_state {
+	int (*fn)(struct db_record *rec, void *private_data);
+	void *private_data;
+};
+
+static int dbwrap_watched_traverse_fn(struct db_record *rec,
+				      void *private_data)
+{
+	struct dbwrap_watched_traverse_state *state = private_data;
+	ssize_t num_watchers;
+	struct db_record prec = *rec;
+	bool deleted;
+
+	num_watchers = dbwrap_watched_parse(rec->value, NULL, 0, &deleted,
+					    &prec.value);
+
+	if ((num_watchers == -1) || deleted) {
+		return 0;
+	}
+
+	return state->fn(&prec, state->private_data);
+}
+
+static int dbwrap_watched_traverse(struct db_context *db,
+				   int (*fn)(struct db_record *rec,
+					     void *private_data),
+				   void *private_data)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	struct dbwrap_watched_traverse_state state = {
+		.fn = fn, .private_data = private_data };
+	NTSTATUS status;
+	int ret;
+
+	status = dbwrap_traverse(
+		ctx->backend, dbwrap_watched_traverse_fn, &state, &ret);
+	if (!NT_STATUS_IS_OK(status)) {
+		return -1;
+	}
+	return ret;
+}
+
+static int dbwrap_watched_traverse_read(struct db_context *db,
+					int (*fn)(struct db_record *rec,
+						  void *private_data),
+					void *private_data)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	struct dbwrap_watched_traverse_state state = {
+		.fn = fn, .private_data = private_data };
+	NTSTATUS status;
+	int ret;
+
+	status = dbwrap_traverse_read(
+		ctx->backend, dbwrap_watched_traverse_fn, &state, &ret);
+	if (!NT_STATUS_IS_OK(status)) {
+		return -1;
+	}
+	return ret;
+}
+
+static int dbwrap_watched_get_seqnum(struct db_context *db)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	return dbwrap_get_seqnum(ctx->backend);
+}
+
+static int dbwrap_watched_transaction_start(struct db_context *db)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	return dbwrap_transaction_start(ctx->backend);
+}
+
+static int dbwrap_watched_transaction_commit(struct db_context *db)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	return dbwrap_transaction_commit(ctx->backend);
+}
+
+static int dbwrap_watched_transaction_cancel(struct db_context *db)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	return dbwrap_transaction_cancel(ctx->backend);
+}
+
+struct dbwrap_watched_parse_record_state {
+	void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data);
+	void *private_data;
+	bool deleted;
+};
+
+static void dbwrap_watched_parse_record_parser(TDB_DATA key, TDB_DATA data,
+					       void *private_data)
+{
+	struct dbwrap_watched_parse_record_state *state = private_data;
+	ssize_t num_watchers;
+	TDB_DATA userdata;
+
+	num_watchers = dbwrap_watched_parse(data, NULL, 0, &state->deleted,
+					    &userdata);
+	if ((num_watchers == -1) || state->deleted) {
+		return;
+	}
+	state->parser(key, userdata, state->private_data);
+}
+
+static NTSTATUS dbwrap_watched_parse_record(
+	struct db_context *db, TDB_DATA key,
+	void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data),
+	void *private_data)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+	struct dbwrap_watched_parse_record_state state = {
+		.parser = parser, private_data = private_data,
+		.deleted = false
+	};
+	NTSTATUS status;
+
+	status = dbwrap_parse_record(
+		ctx->backend, key, dbwrap_watched_parse_record_parser, &state);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+	if (state.deleted) {
+		return NT_STATUS_NOT_FOUND;
+	}
+	return NT_STATUS_OK;
+}
+
+static int dbwrap_watched_exists(struct db_context *db, TDB_DATA key)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+
+	return dbwrap_exists(ctx->backend, key);
+}
+
+static size_t dbwrap_watched_id(struct db_context *db, uint8_t *id,
+				size_t idlen)
+{
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+
+	return dbwrap_db_id(ctx->backend, id, idlen);
+}
+
+struct db_context *db_open_watched(TALLOC_CTX *mem_ctx,
+				   struct db_context *backend,
+				   struct messaging_context *msg)
+{
+	struct db_context *db;
+	struct db_watched_ctx *ctx;
+
+	db = talloc_zero(mem_ctx, struct db_context);
+	if (db == NULL) {
+		return NULL;
+	}
+	ctx = talloc_zero(db, struct db_watched_ctx);
+	if (ctx == NULL) {
+		TALLOC_FREE(db);
+		return NULL;
+	}
+	db->private_data = ctx;
+
+	ctx->msg = msg;
+
+	db->lock_order = backend->lock_order;
+	backend->lock_order = DBWRAP_LOCK_ORDER_NONE;
+	ctx->backend = talloc_move(ctx, &backend);
+
+	db->fetch_locked = dbwrap_watched_fetch_locked;
+	db->traverse = dbwrap_watched_traverse;
+	db->traverse_read = dbwrap_watched_traverse_read;
+	db->get_seqnum = dbwrap_watched_get_seqnum;
+	db->transaction_start = dbwrap_watched_transaction_start;
+	db->transaction_commit = dbwrap_watched_transaction_commit;
+	db->transaction_cancel = dbwrap_watched_transaction_cancel;
+	db->parse_record = dbwrap_watched_parse_record;
+	db->exists = dbwrap_watched_exists;
+	db->id = dbwrap_watched_id;
+	db->name = dbwrap_name(ctx->backend);
+
+	return db;
+}
+
+struct dbwrap_watched_watch_state {
+	struct db_context *db;
+	struct server_id me;
+	TDB_DATA w_key;
+	struct server_id blocker;
+	bool blockerdead;
+};
+
+static bool dbwrap_watched_msg_filter(struct messaging_rec *rec,
+				      void *private_data);
+static void dbwrap_watched_watch_done(struct tevent_req *subreq);
+static void dbwrap_watched_watch_blocker_died(struct tevent_req *subreq);
+static int dbwrap_watched_watch_state_destructor(
+	struct dbwrap_watched_watch_state *state);
+
+struct tevent_req *dbwrap_watched_watch_send(TALLOC_CTX *mem_ctx,
+					     struct tevent_context *ev,
+					     struct db_record *rec,
+					     struct server_id blocker)
+{
+	struct db_watched_subrec *subrec = talloc_get_type_abort(
+		rec->private_data, struct db_watched_subrec);
+	struct db_context *db = dbwrap_record_get_db(rec);
+	struct db_watched_ctx *ctx = talloc_get_type_abort(
+		db->private_data, struct db_watched_ctx);
+
+	struct tevent_req *req, *subreq;
+	struct dbwrap_watched_watch_state *state;
+	ssize_t needed;
+	size_t num_watchers;
+	struct server_id *tmp;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct dbwrap_watched_watch_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->db = db;
+	state->blocker = blocker;
+
+	if (ctx->msg == NULL) {
+		tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+		return tevent_req_post(req, ev);
+	}
+
+	state->me = messaging_server_id(ctx->msg);
+
+	needed = dbwrap_record_watchers_key(db, rec, NULL, 0);
+	if (needed == -1) {
+		tevent_req_nterror(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+		return tevent_req_post(req, ev);
+	}
+	state->w_key.dsize = needed;
+
+	state->w_key.dptr = talloc_array(state, uint8_t, state->w_key.dsize);
+	if (tevent_req_nomem(state->w_key.dptr, req)) {
+		return tevent_req_post(req, ev);
+	}
+	dbwrap_record_watchers_key(db, rec, state->w_key.dptr,
+				   state->w_key.dsize);
+
+	subreq = messaging_filtered_read_send(
+		state, ev, ctx->msg, dbwrap_watched_msg_filter, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, dbwrap_watched_watch_done, req);
+
+	num_watchers = talloc_array_length(subrec->watchers);
+
+	tmp = talloc_realloc(subrec, subrec->watchers, struct server_id,
+			     num_watchers + 1);
+	if (tevent_req_nomem(tmp, req)) {
+		return tevent_req_post(req, ev);
+	}
+	subrec->watchers = tmp;
+	subrec->watchers[num_watchers] = state->me;
+
+	status = dbwrap_watched_save(subrec, rec->value, 0);
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	talloc_set_destructor(state, dbwrap_watched_watch_state_destructor);
+
+	if (blocker.pid != 0) {
+		subreq = server_id_watch_send(state, ev, ctx->msg, blocker);
+		if (tevent_req_nomem(subreq, req)) {
+			return tevent_req_post(req, ev);
+		}
+		tevent_req_set_callback(
+			subreq, dbwrap_watched_watch_blocker_died, req);
+	}
+
+	return req;
+}
+
+static void dbwrap_watched_watch_blocker_died(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct dbwrap_watched_watch_state *state = tevent_req_data(
+		req, struct dbwrap_watched_watch_state);
+	int ret;
+
+	ret = server_id_watch_recv(subreq, NULL);
+	TALLOC_FREE(subreq);
+	if (ret != 0) {
+		tevent_req_nterror(req, map_nt_error_from_unix(ret));
+		return;
+	}
+	state->blockerdead = true;
+	tevent_req_done(req);
+}
+
+static bool dbwrap_watched_remove_waiter(struct db_watched_subrec *subrec,
+					 struct server_id id)
+{
+	size_t i, num_watchers;
+
+	num_watchers = talloc_array_length(subrec->watchers);
+
+	for (i=0; i<num_watchers; i++) {
+		if (server_id_equal(&id, &subrec->watchers[i])) {
+			break;
+		}
+	}
+
+	if (i == num_watchers) {
+		DBG_WARNING("Did not find id in state->watchers\n");
+		return false;
+	}
+
+	subrec->watchers[i] = subrec->watchers[num_watchers-1];
+	subrec->watchers = talloc_realloc(subrec, subrec->watchers,
+					  struct server_id, num_watchers-1);
+
+	return true;
+}
+
+static int dbwrap_watched_watch_state_destructor(
+	struct dbwrap_watched_watch_state *state)
+{
+	struct db_record *rec;
+	struct db_watched_subrec *subrec;
+	TDB_DATA key;
+	bool ok;
+
+	ok = dbwrap_record_watchers_key_parse(state->w_key, NULL, NULL, &key);
+	if (!ok) {
+		DBG_WARNING("dbwrap_record_watchers_key_parse failed\n");
+		return 0;
+	}
+
+	rec = dbwrap_fetch_locked(state->db, state, key);
+	if (rec == NULL) {
+		DBG_WARNING("dbwrap_fetch_locked failed\n");
+		return 0;
+	}
+
+	subrec = talloc_get_type_abort(
+		rec->private_data, struct db_watched_subrec);
+
+	ok = dbwrap_watched_remove_waiter(subrec, state->me);
+	if (ok) {
+		NTSTATUS status;
+		status = dbwrap_watched_save(subrec, rec->value, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("dbwrap_watched_save failed: %s\n",
+				    nt_errstr(status));
+		}
+	}
+
+	TALLOC_FREE(rec);
+	return 0;
+}
+
+static bool dbwrap_watched_msg_filter(struct messaging_rec *rec,
+				      void *private_data)
+{
+	struct dbwrap_watched_watch_state *state = talloc_get_type_abort(
+		private_data, struct dbwrap_watched_watch_state);
+	int cmp;
+
+	if (rec->msg_type != MSG_DBWRAP_MODIFIED) {
+		return false;
+	}
+	if (rec->num_fds != 0) {
+		return false;
+	}
+	if (rec->buf.length != state->w_key.dsize) {
+		return false;
+	}
+
+	cmp = memcmp(rec->buf.data, state->w_key.dptr, rec->buf.length);
+
+	return (cmp == 0);
+}
+
+static void dbwrap_watched_watch_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct messaging_rec *rec;
+	int ret;
+
+	ret = messaging_filtered_read_recv(subreq, talloc_tos(), &rec);
+	TALLOC_FREE(subreq);
+	if (ret != 0) {
+		tevent_req_nterror(req, map_nt_error_from_unix(ret));
+		return;
+	}
+	tevent_req_done(req);
+}
+
+NTSTATUS dbwrap_watched_watch_recv(struct tevent_req *req,
+				   TALLOC_CTX *mem_ctx,
+				   struct db_record **prec,
+				   bool *blockerdead,
+				   struct server_id *blocker)
+{
+	struct dbwrap_watched_watch_state *state = tevent_req_data(
+		req, struct dbwrap_watched_watch_state);
+	struct db_watched_subrec *subrec;
+	NTSTATUS status;
+	TDB_DATA key;
+	struct db_record *rec;
+	bool ok;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	if (blockerdead != NULL) {
+		*blockerdead = state->blockerdead;
+	}
+	if (blocker != NULL) {
+		*blocker = state->blocker;
+	}
+	if (prec == NULL) {
+		return NT_STATUS_OK;
+	}
+
+	ok = dbwrap_record_watchers_key_parse(state->w_key, NULL, NULL, &key);
+	if (!ok) {
+		return NT_STATUS_INTERNAL_DB_ERROR;
+	}
+
+	rec = dbwrap_fetch_locked(state->db, mem_ctx, key);
+	if (rec == NULL) {
+		return NT_STATUS_INTERNAL_DB_ERROR;
+	}
+
+	talloc_set_destructor(state, NULL);
+
+	subrec = talloc_get_type_abort(
+		rec->private_data, struct db_watched_subrec);
+
+	ok = dbwrap_watched_remove_waiter(subrec, state->me);
+	if (ok) {
+		status = dbwrap_watched_save(subrec, rec->value, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("dbwrap_watched_save failed: %s\n",
+				    nt_errstr(status));
+		}
+	}
+
+	*prec = rec;
+	return NT_STATUS_OK;
+}
diff --git a/source3/lib/dbwrap/dbwrap_watch.h b/source3/lib/dbwrap/dbwrap_watch.h
index b14128c..a23727c 100644
--- a/source3/lib/dbwrap/dbwrap_watch.h
+++ b/source3/lib/dbwrap/dbwrap_watch.h
@@ -45,5 +45,17 @@ void dbwrap_watchers_traverse_read(
 
 void dbwrap_watchers_wakeall(struct messaging_context *msg);
 
+struct db_context *db_open_watched(TALLOC_CTX *mem_ctx,
+				   struct db_context *backend,
+				   struct messaging_context *msg);
+struct tevent_req *dbwrap_watched_watch_send(TALLOC_CTX *mem_ctx,
+					     struct tevent_context *ev,
+					     struct db_record *rec,
+					     struct server_id blocker);
+NTSTATUS dbwrap_watched_watch_recv(struct tevent_req *req,
+				   TALLOC_CTX *mem_ctx,
+				   struct db_record **prec,
+				   bool *blockerdead,
+				   struct server_id *blocker);
 
 #endif /* __DBWRAP_WATCH_H__ */
diff --git a/source3/torture/test_dbwrap_watch.c b/source3/torture/test_dbwrap_watch.c
index a912bd2..d3eac6f 100644
--- a/source3/torture/test_dbwrap_watch.c
+++ b/source3/torture/test_dbwrap_watch.c
@@ -29,6 +29,7 @@ bool run_dbwrap_watch1(int dummy)
 {
 	struct tevent_context *ev = NULL;
 	struct messaging_context *msg = NULL;
+	struct db_context *backend = NULL;
 	struct db_context *db = NULL;
 	const char *keystr = "key";
 	TDB_DATA key = string_term_tdb_data(keystr);
@@ -47,21 +48,23 @@ bool run_dbwrap_watch1(int dummy)
 		fprintf(stderr, "messaging_init failed\n");
 		goto fail;
 	}
-	db = db_open(msg, "test_watch.tdb", 0, TDB_DEFAULT,
-		     O_CREAT|O_RDWR, 0644, DBWRAP_LOCK_ORDER_1,
-		     DBWRAP_FLAG_NONE);
-	if (db == NULL) {
+	backend = db_open(msg, "test_watch.tdb", 0, TDB_DEFAULT,
+			  O_CREAT|O_RDWR, 0644, DBWRAP_LOCK_ORDER_1,
+			  DBWRAP_FLAG_NONE);
+	if (backend == NULL) {
 		fprintf(stderr, "db_open failed: %s\n", strerror(errno));
 		goto fail;
 	}
-	dbwrap_watch_db(db, msg);
+
+	db = db_open_watched(ev, backend, msg);
+
 	rec = dbwrap_fetch_locked(db, db, key);
 	if (rec == NULL) {
 		fprintf(stderr, "dbwrap_fetch_locked failed\n");
 		goto fail;
 	}
-	req = dbwrap_record_watch_send(talloc_tos(), ev, rec, msg,
-				       (struct server_id){0});
+	req = dbwrap_watched_watch_send(talloc_tos(), ev, rec,
+					(struct server_id){0});
 	if (req == NULL) {
 		fprintf(stderr, "dbwrap_record_watch_send failed\n");
 		goto fail;
@@ -87,8 +90,8 @@ bool run_dbwrap_watch1(int dummy)
 		goto fail;
 	}
 
-	status = dbwrap_record_watch_recv(req, talloc_tos(), &rec, NULL,
-					  NULL);
+	status = dbwrap_watched_watch_recv(req, talloc_tos(), &rec, NULL,
+					   NULL);
 	if (!NT_STATUS_IS_OK(status)) {
 		fprintf(stderr, "dbwrap_record_watch_recv failed: %s\n",
 			nt_errstr(status));
-- 
1.9.1


>From 004709fd5019cf0abec269b35d2a9e3974b425be Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 13 Jul 2016 07:26:52 +0200
Subject: [PATCH 6/9] lib: Convert g_lock to new dbwrap_watch

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/g_lock.c | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/source3/lib/g_lock.c b/source3/lib/g_lock.c
index 7b91e3c..f954978 100644
--- a/source3/lib/g_lock.c
+++ b/source3/lib/g_lock.c
@@ -48,6 +48,7 @@ struct g_lock_ctx *g_lock_ctx_init(TALLOC_CTX *mem_ctx,
 				   struct messaging_context *msg)
 {
 	struct g_lock_ctx *result;
+	struct db_context *backend;
 	char *db_path;
 
 	result = talloc(mem_ctx, struct g_lock_ctx);
@@ -62,18 +63,24 @@ struct g_lock_ctx *g_lock_ctx_init(TALLOC_CTX *mem_ctx,
 		return NULL;
 	}
 
-	result->db = db_open(result, db_path, 0,
-			     TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
-			     O_RDWR|O_CREAT, 0600,
-			     DBWRAP_LOCK_ORDER_2,
-			     DBWRAP_FLAG_NONE);
+	backend = db_open(result, db_path, 0,
+			  TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
+			  O_RDWR|O_CREAT, 0600,
+			  DBWRAP_LOCK_ORDER_2,
+			  DBWRAP_FLAG_NONE);
 	TALLOC_FREE(db_path);
-	if (result->db == NULL) {
+	if (backend == NULL) {
 		DEBUG(1, ("g_lock_init: Could not open g_lock.tdb\n"));
 		TALLOC_FREE(result);
 		return NULL;
 	}
-	dbwrap_watch_db(result->db, msg);
+
+	result->db = db_open_watched(result, backend, msg);
+	if (result->db == NULL) {
+		DBG_WARNING("g_lock_init: db_open_watched failed\n");
+		TALLOC_FREE(result);
+		return NULL;
+	}
 	return result;
 }
 
@@ -238,8 +245,7 @@ struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx,
 		tevent_req_nterror(req, status);
 		return tevent_req_post(req, ev);
 	}
-	subreq = dbwrap_record_watch_send(state, state->ev, rec,
-					  state->ctx->msg, blocker);
+	subreq = dbwrap_watched_watch_send(state, state->ev, rec, blocker);
 	TALLOC_FREE(rec);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
@@ -265,8 +271,8 @@ static void g_lock_lock_retry(struct tevent_req *subreq)
 	struct db_record *rec;
 	NTSTATUS status;
 
-	status = dbwrap_record_watch_recv(subreq, talloc_tos(), &rec, NULL,
-					  NULL);
+	status = dbwrap_watched_watch_recv(subreq, talloc_tos(), &rec, NULL,
+					   NULL);
 	TALLOC_FREE(subreq);
 
 	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
@@ -294,8 +300,7 @@ static void g_lock_lock_retry(struct tevent_req *subreq)
 		tevent_req_nterror(req, status);
 		return;
 	}
-	subreq = dbwrap_record_watch_send(state, state->ev, rec,
-					  state->ctx->msg, blocker);
+	subreq = dbwrap_watched_watch_send(state, state->ev, rec, blocker);
 	TALLOC_FREE(rec);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
-- 
1.9.1


>From 4811ed9e5cc61fedd4490102d768792540eb2e52 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 13 Jul 2016 07:27:30 +0200
Subject: [PATCH 7/9] smbd: Convert locking.tdb to new dbwrap_watch

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/locking/share_mode_lock.c | 18 +++++++++++++-----
 source3/smbd/open.c               |  8 ++++----
 source3/smbd/smb2_setinfo.c       |  7 +++----
 3 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index 4e9de03..b5a63f8 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -60,6 +60,7 @@ static struct db_context *lock_db;
 
 static bool locking_init_internal(bool read_only)
 {
+	struct db_context *backend;
 	char *db_path;
 
 	brl_init(read_only);
@@ -72,21 +73,28 @@ static bool locking_init_internal(bool read_only)
 		return false;
 	}
 
-	lock_db = db_open(NULL, db_path,
+	backend = db_open(NULL, db_path,
 			  SMB_OPEN_DATABASE_TDB_HASH_SIZE,
 			  TDB_DEFAULT|TDB_VOLATILE|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
 			  read_only?O_RDONLY:O_RDWR|O_CREAT, 0644,
 			  DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
 	TALLOC_FREE(db_path);
-	if (!lock_db) {
+	if (!backend) {
 		DEBUG(0,("ERROR: Failed to initialise locking database\n"));
 		return False;
 	}
 
-	if (!posix_locking_init(read_only))
-		return False;
+	lock_db = db_open_watched(NULL, backend, server_messaging_context());
+	if (lock_db == NULL) {
+		DBG_ERR("db_open_watched failed\n");
+		TALLOC_FREE(backend);
+		return false;
+	}
 
-	dbwrap_watch_db(lock_db, server_messaging_context());
+	if (!posix_locking_init(read_only)) {
+		TALLOC_FREE(lock_db);
+		return False;
+	}
 
 	return True;
 }
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 57cd4f1..ab46fe0 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1952,9 +1952,9 @@ static void defer_open(struct share_mode_lock *lck,
 		DEBUG(10, ("defering mid %llu\n",
 			   (unsigned long long)req->mid));
 
-		watch_req = dbwrap_record_watch_send(
+		watch_req = dbwrap_watched_watch_send(
 			watch_state, req->sconn->ev_ctx, lck->data->record,
-			req->sconn->msg_ctx, (struct server_id){0});
+			(struct server_id){0});
 		if (watch_req == NULL) {
 			exit_server("Could not watch share mode record");
 		}
@@ -1981,11 +1981,11 @@ static void defer_open_done(struct tevent_req *req)
 	NTSTATUS status;
 	bool ret;
 
-	status = dbwrap_record_watch_recv(req, talloc_tos(), NULL, NULL,
+	status = dbwrap_watched_watch_recv(req, talloc_tos(), NULL, NULL,
 					  NULL);
 	TALLOC_FREE(req);
 	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
+		DEBUG(5, ("dbwrap_watched_watch_recv returned %s\n",
 			  nt_errstr(status)));
 		/*
 		 * Even if it failed, retry anyway. TODO: We need a way to
diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c
index 0a678ea..db00ba0 100644
--- a/source3/smbd/smb2_setinfo.c
+++ b/source3/smbd/smb2_setinfo.c
@@ -248,11 +248,10 @@ static struct tevent_req *delay_rename_for_lease_break(struct tevent_req *req,
 
 	talloc_set_destructor(rename_state, defer_rename_state_destructor);
 
-	subreq = dbwrap_record_watch_send(
+	subreq = dbwrap_watched_watch_send(
 				rename_state,
 				ev,
 				lck->data->record,
-				fsp->conn->sconn->msg_ctx,
 				(struct server_id){0});
 
 	if (subreq == NULL) {
@@ -280,8 +279,8 @@ static void defer_rename_done(struct tevent_req *subreq)
 	int ret_size = 0;
 	bool ok;
 
-	status = dbwrap_record_watch_recv(subreq, state->req, NULL, NULL,
-					  NULL);
+	status = dbwrap_watched_watch_recv(subreq, state->req, NULL, NULL,
+					   NULL);
 	TALLOC_FREE(subreq);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
-- 
1.9.1


>From 9906e1f15b9e2a33cd60bae32c9067a087448538 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 13 Jul 2016 07:41:02 +0200
Subject: [PATCH 8/9] smbd: Convert smbXsrv_open_global.tdb to new dbwrap_watch

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/globals.h         |  2 +-
 source3/smbd/server.c          |  2 +-
 source3/smbd/smbXsrv_session.c | 42 +++++++++++++++++++++++-------------------
 3 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 9e3e95c..0266533 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -554,7 +554,7 @@ NTSTATUS smb2srv_client_connection_pass(struct smbd_smb2_request *smb2req,
 NTSTATUS smbXsrv_connection_init_tables(struct smbXsrv_connection *conn,
 					enum protocol_types protocol);
 
-NTSTATUS smbXsrv_session_global_init(void);
+NTSTATUS smbXsrv_session_global_init(struct messaging_context *msg_ctx);
 NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn,
 				NTTIME now,
 				struct smbXsrv_session **_session);
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 6e70edc..65dc173 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -1582,7 +1582,7 @@ extern void build_options(bool screen);
 		exit_daemon("Samba cannot init server context", EACCES);
 	}
 
-	status = smbXsrv_session_global_init();
+	status = smbXsrv_session_global_init(msg_ctx);
 	if (!NT_STATUS_IS_OK(status)) {
 		exit_daemon("Samba cannot init session context", EACCES);
 	}
diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c
index 51668c2..381ce40 100644
--- a/source3/smbd/smbXsrv_session.c
+++ b/source3/smbd/smbXsrv_session.c
@@ -53,9 +53,10 @@ struct smbXsrv_session_table {
 
 static struct db_context *smbXsrv_session_global_db_ctx = NULL;
 
-NTSTATUS smbXsrv_session_global_init(void)
+NTSTATUS smbXsrv_session_global_init(struct messaging_context *msg_ctx)
 {
 	char *global_path = NULL;
+	struct db_context *backend = NULL;
 	struct db_context *db_ctx = NULL;
 
 	if (smbXsrv_session_global_db_ctx != NULL) {
@@ -70,16 +71,16 @@ NTSTATUS smbXsrv_session_global_init(void)
 		return NT_STATUS_NO_MEMORY;
 	}
 
-	db_ctx = db_open(NULL, global_path,
-			 0, /* hash_size */
-			 TDB_DEFAULT |
-			 TDB_CLEAR_IF_FIRST |
-			 TDB_INCOMPATIBLE_HASH,
-			 O_RDWR | O_CREAT, 0600,
-			 DBWRAP_LOCK_ORDER_1,
-			 DBWRAP_FLAG_NONE);
+	backend = db_open(NULL, global_path,
+			  0, /* hash_size */
+			  TDB_DEFAULT |
+			  TDB_CLEAR_IF_FIRST |
+			  TDB_INCOMPATIBLE_HASH,
+			  O_RDWR | O_CREAT, 0600,
+			  DBWRAP_LOCK_ORDER_1,
+			  DBWRAP_FLAG_NONE);
 	TALLOC_FREE(global_path);
-	if (db_ctx == NULL) {
+	if (backend == NULL) {
 		NTSTATUS status;
 
 		status = map_nt_error_from_unix_common(errno);
@@ -87,6 +88,12 @@ NTSTATUS smbXsrv_session_global_init(void)
 		return status;
 	}
 
+	db_ctx = db_open_watched(NULL, backend, server_messaging_context());
+	if (db_ctx == NULL) {
+		TALLOC_FREE(backend);
+		return NT_STATUS_NO_MEMORY;
+	}
+
 	smbXsrv_session_global_db_ctx = db_ctx;
 
 	return NT_STATUS_OK;
@@ -242,7 +249,7 @@ static NTSTATUS smbXsrv_session_table_init(struct smbXsrv_connection *conn,
 	table->local.highest_id = highest_id;
 	table->local.max_sessions = max_sessions;
 
-	status = smbXsrv_session_global_init();
+	status = smbXsrv_session_global_init(client->msg_ctx);
 	if (!NT_STATUS_IS_OK(status)) {
 		TALLOC_FREE(table);
 		return status;
@@ -250,8 +257,6 @@ static NTSTATUS smbXsrv_session_table_init(struct smbXsrv_connection *conn,
 
 	table->global.db_ctx = smbXsrv_session_global_db_ctx;
 
-	dbwrap_watch_db(table->global.db_ctx, client->msg_ctx);
-
 	subreq = messaging_read_send(table, client->ev_ctx, client->msg_ctx,
 				     MSG_SMBXSRV_SESSION_CLOSE);
 	if (subreq == NULL) {
@@ -1065,9 +1070,8 @@ static void smb2srv_session_close_previous_check(struct tevent_req *req)
 		return;
 	}
 
-	subreq = dbwrap_record_watch_send(state, state->ev,
-					  state->db_rec, conn->msg_ctx,
-					  (struct server_id){0});
+	subreq = dbwrap_watched_watch_send(state, state->ev, state->db_rec,
+					   (struct server_id){0});
 	if (tevent_req_nomem(subreq, req)) {
 		TALLOC_FREE(state->db_rec);
 		return;
@@ -1121,8 +1125,8 @@ static void smb2srv_session_close_previous_modified(struct tevent_req *subreq)
 		struct smb2srv_session_close_previous_state);
 	NTSTATUS status;
 
-	status = dbwrap_record_watch_recv(subreq, state, &state->db_rec, NULL,
-					  NULL);
+	status = dbwrap_watched_watch_recv(subreq, state, &state->db_rec, NULL,
+					   NULL);
 	TALLOC_FREE(subreq);
 	if (tevent_req_nterror(req, status)) {
 		return;
@@ -1931,7 +1935,7 @@ NTSTATUS smbXsrv_session_global_traverse(
 	};
 
 	become_root();
-	status = smbXsrv_session_global_init();
+	status = smbXsrv_session_global_init(NULL);
 	if (!NT_STATUS_IS_OK(status)) {
 		unbecome_root();
 		DEBUG(0, ("Failed to initialize session_global: %s\n",
-- 
1.9.1


>From aaadabf77970c686ed1330952b2039e85d175d12 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 14 Jul 2016 07:43:20 +0200
Subject: [PATCH 9/9] smbd: Remove a reference to dbwrap_watch_db()

This has never been watched, so it's an unnecessary overhead on
dbwrap_record_store().

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/smbXsrv_client.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/source3/smbd/smbXsrv_client.c b/source3/smbd/smbXsrv_client.c
index 7286b6e..ca04ae7 100644
--- a/source3/smbd/smbXsrv_client.c
+++ b/source3/smbd/smbXsrv_client.c
@@ -167,8 +167,6 @@ static NTSTATUS smbXsrv_client_table_create(TALLOC_CTX *mem_ctx,
 
 	table->global.db_ctx = smbXsrv_client_global_db_ctx;
 
-	dbwrap_watch_db(table->global.db_ctx, msg_ctx);
-
 	*_table = table;
 	return NT_STATUS_OK;
 }
-- 
1.9.1



More information about the samba-technical mailing list