notifyd

Volker Lendecke Volker.Lendecke at SerNet.DE
Fri Jan 9 09:56:44 MST 2015


Hi!

Attached find the notifyd work, rebased on top of current
master. I know it does not have signed-off by tags
everywhere yet, but I think it's in pretty good shape. So
everyone interested in faster clustered change notify with
clustered inotify, please take a look :-)

Thanks,

Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de, mailto:kontakt at sernet.de
-------------- next part --------------
From 87099e556219061a1a9b47af5a6d435761882d85 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 25 Nov 2014 18:50:25 +0100
Subject: [PATCH 01/15] unix_msg: Fix 80-line formatting

This is pretty fresh code, so hope this change does not fall under the "no
reformatting" rule yet

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

diff --git a/source3/lib/unix_msg/unix_msg.c b/source3/lib/unix_msg/unix_msg.c
index 51bb0c6..e4eed1c 100644
--- a/source3/lib/unix_msg/unix_msg.c
+++ b/source3/lib/unix_msg/unix_msg.c
@@ -922,7 +922,8 @@ static void unix_msg_recv(struct unix_dgram_ctx *dgram_ctx,
 	buflen -= sizeof(cookie);
 
 	if (cookie == 0) {
-		ctx->recv_callback(ctx, buf, buflen, fds, num_fds, ctx->private_data);
+		ctx->recv_callback(ctx, buf, buflen, fds, num_fds,
+				   ctx->private_data);
 		return;
 	}
 
@@ -974,7 +975,8 @@ static void unix_msg_recv(struct unix_dgram_ctx *dgram_ctx,
 	}
 
 	DLIST_REMOVE(ctx->msgs, msg);
-	ctx->recv_callback(ctx, msg->buf, msg->msglen, fds, num_fds, ctx->private_data);
+	ctx->recv_callback(ctx, msg->buf, msg->msglen, fds, num_fds,
+			   ctx->private_data);
 	free(msg);
 	return;
 
-- 
1.9.1


From b4b7a4c868b1f5879dd1cf17ff13f126fe82da24 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 21 Dec 2014 14:52:17 +0100
Subject: [PATCH 02/15] lib: Simplify iov_buf

According to

https://www.securecoding.cert.org/confluence/display/seccode/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap

we only need to check against one operand.

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

diff --git a/source3/lib/iov_buf.c b/source3/lib/iov_buf.c
index f0e05a6..82a4af5 100644
--- a/source3/lib/iov_buf.c
+++ b/source3/lib/iov_buf.c
@@ -39,8 +39,8 @@ ssize_t iov_buf(const struct iovec *iov, int iovcnt,
 
 		tmp = needed + thislen;
 
-		if ((tmp < needed) || (tmp < thislen)) {
-			/* overflow */
+		if (tmp < needed) {
+			/* wrap */
 			return -1;
 		}
 		needed = tmp;
-- 
1.9.1


From 42a873715d01086b464d071177921d269f7f0295 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 15:53:53 +0100
Subject: [PATCH 03/15] param: Make "change notify" global

---
 docs-xml/smbdotconf/misc/changenotify.xml | 3 +--
 lib/param/param_table.c                   | 6 +++---
 source3/param/loadparm.c                  | 3 ++-
 source3/smbd/service.c                    | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/docs-xml/smbdotconf/misc/changenotify.xml b/docs-xml/smbdotconf/misc/changenotify.xml
index 3a2debb..91e0c8d 100644
--- a/docs-xml/smbdotconf/misc/changenotify.xml
+++ b/docs-xml/smbdotconf/misc/changenotify.xml
@@ -1,8 +1,7 @@
 <samba:parameter name="change notify"
-                 context="S"
+                 context="G"
 				 type="boolean"
                  advanced="1"
-                 parm="1"
                  xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
 <description>
 	<para>This parameter specifies whether Samba should reply
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index 18b0628..bfb66e7 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -1800,11 +1800,11 @@ struct parm_struct parm_table[] = {
 	{
 		.label		= "change notify",
 		.type		= P_BOOL,
-		.p_class	= P_LOCAL,
-		.offset		= LOCAL_VAR(change_notify),
+		.p_class	= P_GLOBAL,
+		.offset		= GLOBAL_VAR(change_notify),
 		.special	= NULL,
 		.enum_list	= NULL,
-		.flags		= FLAG_ADVANCED | FLAG_SHARE,
+		.flags		= FLAG_ADVANCED,
 	},
 	{
 		.label		= "directory name cache size",
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index da50e3a..3fb7a3f 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -236,7 +236,6 @@ static struct loadparm_service sDefault =
 	.acl_map_full_control = true,
 	.acl_group_control = false,
 	.acl_allow_execute_always = false,
-	.change_notify = true,
 	.kernel_change_notify = true,
 	.allocation_roundup_size = SMB_ROUNDUP_ALLOCATION_SIZE,
 	.aio_read_size = 0,
@@ -719,6 +718,8 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
 	   a large number of sites (tridge) */
 	Globals.hostname_lookups = false;
 
+	Globals.change_notify = true,
+
 	string_set(Globals.ctx, &Globals.passdb_backend, "tdbsam");
 	string_set(Globals.ctx, &Globals.ldap_suffix, "");
 	string_set(Globals.ctx, &Globals.szLdapMachineSuffix, "");
diff --git a/source3/smbd/service.c b/source3/smbd/service.c
index 3fd0fc8..7f24797 100644
--- a/source3/smbd/service.c
+++ b/source3/smbd/service.c
@@ -678,7 +678,7 @@ static NTSTATUS make_connection_snum(struct smbXsrv_connection *xconn,
 	on_err_call_dis_hook = true;
 
 	if ((!conn->printer) && (!conn->ipc) &&
-	    lp_change_notify(conn->params)) {
+	    lp_change_notify()) {
 		if (sconn->notify_ctx == NULL) {
 			sconn->notify_ctx = notify_init(
 				sconn, sconn->msg_ctx, sconn->ev_ctx);
-- 
1.9.1


From c93f47144ae3f29cd229cf15390b9deb06aa4a52 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:02:27 +0100
Subject: [PATCH 04/15] make "kernel change notify" global

---
 docs-xml/smbdotconf/misc/kernelchangenotify.xml | 3 +--
 lib/param/param_table.c                         | 6 +++---
 source3/modules/vfs_default.c                   | 2 +-
 source3/param/loadparm.c                        | 2 +-
 4 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/docs-xml/smbdotconf/misc/kernelchangenotify.xml b/docs-xml/smbdotconf/misc/kernelchangenotify.xml
index e90f845..70ccfd5 100644
--- a/docs-xml/smbdotconf/misc/kernelchangenotify.xml
+++ b/docs-xml/smbdotconf/misc/kernelchangenotify.xml
@@ -1,8 +1,7 @@
 <samba:parameter name="kernel change notify"
-                 context="S"
+                 context="G"
 				 type="boolean"
                  advanced="1"
-                 parm="1"
                  xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
 <description>
 	<para>This parameter specifies whether Samba should ask the 
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index bfb66e7..5f7b9ef 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -1818,11 +1818,11 @@ struct parm_struct parm_table[] = {
 	{
 		.label		= "kernel change notify",
 		.type		= P_BOOL,
-		.p_class	= P_LOCAL,
-		.offset		= LOCAL_VAR(kernel_change_notify),
+		.p_class	= P_GLOBAL,
+		.offset		= GLOBAL_VAR(kernel_change_notify),
 		.special	= NULL,
 		.enum_list	= NULL,
-		.flags		= FLAG_ADVANCED | FLAG_SHARE,
+		.flags		= FLAG_ADVANCED,
 	},
 	{
 		.label		= "lpq cache time",
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 5634cc0..69af305 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2118,7 +2118,7 @@ static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle,
 	 * Until that is the case, hard-code inotify here.
 	 */
 #ifdef HAVE_INOTIFY
-	if (lp_kernel_change_notify(vfs_handle->conn->params)) {
+	if (lp_kernel_change_notify()) {
 		int ret;
 		if (!lp_parm_bool(-1, "notify", "inotify", True)) {
 			return NT_STATUS_INVALID_SYSTEM_SERVICE;
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 3fb7a3f..a5797c4 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -236,7 +236,6 @@ static struct loadparm_service sDefault =
 	.acl_map_full_control = true,
 	.acl_group_control = false,
 	.acl_allow_execute_always = false,
-	.kernel_change_notify = true,
 	.allocation_roundup_size = SMB_ROUNDUP_ALLOCATION_SIZE,
 	.aio_read_size = 0,
 	.aio_write_size = 0,
@@ -719,6 +718,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
 	Globals.hostname_lookups = false;
 
 	Globals.change_notify = true,
+	Globals.kernel_change_notify = true,
 
 	string_set(Globals.ctx, &Globals.passdb_backend, "tdbsam");
 	string_set(Globals.ctx, &Globals.ldap_suffix, "");
-- 
1.9.1


From 5488f260d303a924b45ceade507c82431f7afa8e Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 20 Nov 2014 15:30:51 +0000
Subject: [PATCH 05/15] smbd: Add direct notify_fam support

notifyd won't have the VFS around, it is a systemwide daemon without
a connection to specific shares. To continue FAM support, notifyd
needs to be able to link it directly. This adds code to make fam
equivalent to inotify.
---
 source3/smbd/notify_fam.c | 305 ++++++++++++++++++++++++++++++++++++++++++++++
 source3/smbd/proto.h      |  12 ++
 source3/wscript_build     |   7 +-
 3 files changed, 323 insertions(+), 1 deletion(-)
 create mode 100644 source3/smbd/notify_fam.c

diff --git a/source3/smbd/notify_fam.c b/source3/smbd/notify_fam.c
new file mode 100644
index 0000000..e3e1b12
--- /dev/null
+++ b/source3/smbd/notify_fam.c
@@ -0,0 +1,305 @@
+/*
+ * FAM file notification support.
+ *
+ * Copyright (c) James Peach 2005
+ * Copyright (c) Volker Lendecke 2007
+ *
+ * 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 "includes.h"
+#include "smbd/smbd.h"
+#include "librpc/gen_ndr/notify.h"
+
+#include <fam.h>
+
+#if !defined(HAVE_FAM_H_FAMCODES_TYPEDEF)
+/* Gamin provides this typedef which means we can't use 'enum FAMCodes' as per
+ * every other FAM implementation. Phooey.
+ */
+typedef enum FAMCodes FAMCodes;
+#endif
+
+/* NOTE: There are multiple versions of FAM floating around the net, each with
+ * slight differences from the original SGI FAM implementation. In this file,
+ * we rely only on the SGI features and do not assume any extensions. For
+ * example, we do not look at FAMErrno, because it is not set by the original
+ * implementation.
+ *
+ * Random FAM links:
+ *	http://oss.sgi.com/projects/fam/
+ *	http://savannah.nongnu.org/projects/fam/
+ *	http://sourceforge.net/projects/bsdfam/
+ */
+
+/* ------------------------------------------------------------------------- */
+
+struct fam_watch_context {
+	struct fam_watch_context *prev, *next;
+	FAMConnection *fam_connection;
+	struct FAMRequest fr;
+	struct sys_notify_context *sys_ctx;
+	void (*callback)(struct sys_notify_context *ctx,
+			 void *private_data,
+			 struct notify_event *ev);
+	void *private_data;
+	uint32_t mask; /* the inotify mask */
+	uint32_t filter; /* the windows completion filter */
+	const char *path;
+};
+
+
+/*
+ * We want one FAM connection per smbd, not one per tcon.
+ */
+static FAMConnection fam_connection;
+static bool fam_connection_initialized = False;
+
+static struct fam_watch_context *fam_notify_list;
+static void fam_handler(struct tevent_context *event_ctx,
+			struct tevent_fd *fd_event,
+			uint16 flags,
+			void *private_data);
+
+static NTSTATUS fam_open_connection(FAMConnection *fam_conn,
+				    struct tevent_context *event_ctx)
+{
+	int res;
+	char *name;
+
+	ZERO_STRUCTP(fam_conn);
+	FAMCONNECTION_GETFD(fam_conn) = -1;
+
+
+#ifdef HAVE_FAMNOEXISTS
+	/* We should honor outside setting of the GAM_CLIENT_ID. */
+	setenv("GAM_CLIENT_ID","SAMBA",0);
+#endif
+
+	if (asprintf(&name, "smbd (%lu)", (unsigned long)getpid()) == -1) {
+		DEBUG(0, ("No memory\n"));
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	res = FAMOpen2(fam_conn, name);
+
+#ifdef HAVE_FAMNOEXISTS
+	/*
+	 * This reduces the chatter between GAMIN and samba making the pair
+	 * much more reliable.
+	 */
+	FAMNoExists(fam_conn);
+#endif
+
+	SAFE_FREE(name);
+
+	if (res < 0) {
+		DEBUG(10, ("FAM file change notifications not available: %s\n",
+			   FamErrlist[-res]));
+		/*
+		 * No idea how to get NT_STATUS from a FAM result
+		 */
+		FAMCONNECTION_GETFD(fam_conn) = -1;
+		return NT_STATUS_UNEXPECTED_IO_ERROR;
+	}
+
+	if (tevent_add_fd(event_ctx, event_ctx,
+			 FAMCONNECTION_GETFD(fam_conn),
+			 TEVENT_FD_READ, fam_handler,
+			 (void *)fam_conn) == NULL) {
+		DEBUG(0, ("event_add_fd failed\n"));
+		FAMClose(fam_conn);
+		FAMCONNECTION_GETFD(fam_conn) = -1;
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static void fam_reopen(FAMConnection *fam_conn,
+		       struct tevent_context *event_ctx,
+		       struct fam_watch_context *notify_list)
+{
+	struct fam_watch_context *ctx;
+
+	DEBUG(5, ("Re-opening FAM connection\n"));
+
+	FAMClose(fam_conn);
+
+	if (!NT_STATUS_IS_OK(fam_open_connection(fam_conn, event_ctx))) {
+		DEBUG(5, ("Re-opening fam connection failed\n"));
+		return;
+	}
+
+	for (ctx = notify_list; ctx; ctx = ctx->next) {
+		FAMMonitorDirectory(fam_conn, ctx->path, &ctx->fr, NULL);
+	}
+}
+
+static void fam_handler(struct tevent_context *event_ctx,
+			struct tevent_fd *fd_event,
+			uint16 flags,
+			void *private_data)
+{
+	FAMConnection *fam_conn = (FAMConnection *)private_data;
+	FAMEvent fam_event;
+	struct fam_watch_context *ctx;
+	struct notify_event ne;
+
+	if (FAMPending(fam_conn) == 0) {
+		DEBUG(10, ("fam_handler called but nothing pending\n"));
+		return;
+	}
+
+	if (FAMNextEvent(fam_conn, &fam_event) != 1) {
+		DEBUG(5, ("FAMNextEvent returned an error\n"));
+		TALLOC_FREE(fd_event);
+		fam_reopen(fam_conn, event_ctx, fam_notify_list);
+		return;
+	}
+
+	DEBUG(10, ("Got FAMCode %d for %s\n", fam_event.code,
+		   fam_event.filename));
+
+	switch (fam_event.code) {
+	case FAMChanged:
+		ne.action = NOTIFY_ACTION_MODIFIED;
+		break;
+	case FAMCreated:
+		ne.action = NOTIFY_ACTION_ADDED;
+		break;
+	case FAMDeleted:
+		ne.action = NOTIFY_ACTION_REMOVED;
+		break;
+	default:
+		DEBUG(10, ("Ignoring code FAMCode %d for file %s\n",
+			   (int)fam_event.code, fam_event.filename));
+		return;
+	}
+
+	for (ctx = fam_notify_list; ctx; ctx = ctx->next) {
+		if (memcmp(&fam_event.fr, &ctx->fr, sizeof(FAMRequest)) == 0) {
+			break;
+		}
+	}
+
+	if (ctx == NULL) {
+		DEBUG(5, ("Discarding event for file %s\n",
+			  fam_event.filename));
+		return;
+	}
+
+	if ((ne.path = strrchr_m(fam_event.filename, '\\')) == NULL) {
+		ne.path = fam_event.filename;
+	}
+	ne.dir = ctx->path;
+
+	ctx->callback(ctx->sys_ctx, ctx->private_data, &ne);
+}
+
+static int fam_watch_context_destructor(struct fam_watch_context *ctx)
+{
+	if (FAMCONNECTION_GETFD(ctx->fam_connection) != -1) {
+		FAMCancelMonitor(&fam_connection, &ctx->fr);
+	}
+	DLIST_REMOVE(fam_notify_list, ctx);
+	return 0;
+}
+
+/*
+  add a watch. The watch is removed when the caller calls
+  talloc_free() on *handle
+*/
+int _fam_watch(TALLOC_CTX *mem_ctx,
+	      struct sys_notify_context *ctx,
+	      const char *path,
+	      uint32_t *filter,
+	      uint32_t *subdir_filter,
+	      void (*callback)(struct sys_notify_context *ctx,
+			       void *private_data,
+			       struct notify_event *ev),
+	      void *private_data,
+	      void *handle_p)
+{
+	const uint32 fam_mask = (FILE_NOTIFY_CHANGE_FILE_NAME|
+				 FILE_NOTIFY_CHANGE_DIR_NAME);
+	struct fam_watch_context *watch;
+	void **handle = (void **)handle_p;
+
+	*handle = NULL;
+
+	if ((*filter & fam_mask) == 0) {
+		DEBUG(10, ("filter = %u, ignoring in FAM\n", *filter));
+		return 0;
+	}
+
+	if (!fam_connection_initialized) {
+		if (!NT_STATUS_IS_OK(fam_open_connection(&fam_connection,
+							 ctx->ev))) {
+			/*
+			 * Just let smbd do all the work itself
+			 */
+			return 0;
+		}
+		fam_connection_initialized = True;
+	}
+
+	if (!(watch = talloc(mem_ctx, struct fam_watch_context))) {
+		return ENOMEM;
+	}
+
+	watch->fam_connection = &fam_connection;
+
+	watch->callback = callback;
+	watch->private_data = private_data;
+	watch->sys_ctx = ctx;
+
+	watch->path = talloc_strdup(watch, path);
+	if (watch->path == NULL) {
+		DEBUG(0, ("talloc_asprintf failed\n"));
+		TALLOC_FREE(watch);
+		return ENOMEM;
+	}
+
+	/*
+	 * The FAM module in this early state will only take care of
+	 * FAMCreated and FAMDeleted events, Leave the rest to notifyd
+	 */
+
+	watch->filter = fam_mask;
+	*filter &= ~fam_mask;
+
+	DLIST_ADD(fam_notify_list, watch);
+	talloc_set_destructor(watch, fam_watch_context_destructor);
+
+	/*
+	 * Only directories monitored so far
+	 */
+
+	if (FAMCONNECTION_GETFD(watch->fam_connection) != -1) {
+		FAMMonitorDirectory(watch->fam_connection, watch->path,
+				    &watch->fr, NULL);
+	}
+	else {
+		/*
+		 * If the re-open is successful, this will establish the
+		 * FAMMonitor from the list
+		 */
+		fam_reopen(watch->fam_connection, ctx->ev, fam_notify_list);
+	}
+
+	*handle = watch;
+
+	return 0;
+}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 121682c..16e87f8 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -542,6 +542,18 @@ int inotify_watch(TALLOC_CTX *mem_ctx,
 		  void *private_data,
 		  void *handle_p);
 
+int _fam_watch(TALLOC_CTX *mem_ctx,
+	      struct sys_notify_context *ctx,
+	      const char *path,
+	      uint32_t *filter,
+	      uint32_t *subdir_filter,
+	      void (*callback)(struct sys_notify_context *ctx,
+			       void *private_data,
+			       struct notify_event *ev),
+	      void *private_data,
+	      void *handle_p);
+
+
 /* The following definitions come from smbd/notify_internal.c  */
 
 struct notify_context *notify_init(TALLOC_CTX *mem_ctx,
diff --git a/source3/wscript_build b/source3/wscript_build
index a6ef584..73a6d57 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -506,6 +506,9 @@ NOTIFY_SOURCES=''
 if bld.CONFIG_SET("HAVE_INOTIFY"):
     NOTIFY_SOURCES += ' smbd/notify_inotify.c'
 
+if bld.CONFIG_SET('SAMBA_FAM_LIBS'):
+    NOTIFY_SOURCES += ' smbd/notify_fam.c'
+
 bld.SAMBA3_LIBRARY('smbd_base',
                    source='''
                    smbd/server_reload.c
@@ -626,7 +629,9 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    NDR_SMB_ACL
                    netapi
                    NDR_IOCTL
-                   ''' + bld.env['dmapi_lib'],
+                   ''' + bld.env['dmapi_lib']
+                   + (bld.CONFIG_GET('SAMBA_FAM_LIBS')
+                         if bld.CONFIG_SET('SAMBA_FAM_LIBS') else ''),
                    private_library=True)
 
 bld.SAMBA3_SUBSYSTEM('LOCKING',
-- 
1.9.1


From fac3d887979422021d1a0082a6921e63c09796a0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:52:47 +0100
Subject: [PATCH 06/15] smbd: Add the notify daemon

This adds the notify daemon listening on MSG_SMB_NOTIFY_REC_CHANGE
and MSG_SMB_NOTIFY_TRIGGER messages. It relies on ctdbd to distribute
the notify database and events in a cluster.
---
 source3/librpc/idl/messaging.idl   |   13 +
 source3/smbd/notifyd/notifyd.c     | 1493 ++++++++++++++++++++++++++++++++++++
 source3/smbd/notifyd/notifyd.h     |  142 ++++
 source3/smbd/notifyd/tests.c       |  119 +++
 source3/smbd/notifyd/wscript_build |   12 +
 source3/wscript_build              |    1 +
 6 files changed, 1780 insertions(+)
 create mode 100644 source3/smbd/notifyd/notifyd.c
 create mode 100644 source3/smbd/notifyd/notifyd.h
 create mode 100644 source3/smbd/notifyd/tests.c
 create mode 100644 source3/smbd/notifyd/wscript_build

diff --git a/source3/librpc/idl/messaging.idl b/source3/librpc/idl/messaging.idl
index ce40a7b..cd9c913 100644
--- a/source3/librpc/idl/messaging.idl
+++ b/source3/librpc/idl/messaging.idl
@@ -96,6 +96,13 @@ interface messaging
 		MSG_SMB_TELL_NUM_CHILDREN       = 0x0317,
 		MSG_SMB_NUM_CHILDREN            = 0x0318,
 
+		/* notifyd messages */
+		MSG_SMB_NOTIFY_REC_CHANGE	= 0x0319,
+		MSG_SMB_NOTIFY_TRIGGER		= 0x031A,
+		MSG_SMB_NOTIFY_GET_DB		= 0x031B,
+		MSG_SMB_NOTIFY_DB		= 0x031C,
+		MSG_SMB_NOTIFY_REC_CHANGES	= 0x031D,
+
 		/* winbind messages */
 		MSG_WINBIND_FINISHED		= 0x0401,
 		MSG_WINBIND_FORGET_STATE	= 0x0402,
@@ -133,4 +140,10 @@ interface messaging
 		uint8 num_fds;
 		dlong fds[num_fds];
 	} messaging_rec;
+
+	typedef [public] struct {
+		hyper rec_index;
+		uint32 num_recs;
+		messaging_rec *recs[num_recs];
+	} messaging_reclog;
 }
diff --git a/source3/smbd/notifyd/notifyd.c b/source3/smbd/notifyd/notifyd.c
new file mode 100644
index 0000000..8c4a4ae
--- /dev/null
+++ b/source3/smbd/notifyd/notifyd.c
@@ -0,0 +1,1493 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 "includes.h"
+#include "librpc/gen_ndr/notify.h"
+#include "librpc/gen_ndr/messaging.h"
+#include "librpc/gen_ndr/server_id.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "messages.h"
+#include "proto.h"
+#include "tdb.h"
+#include "util_tdb.h"
+#include "notifyd.h"
+#include "lib/util/server_id_db.h"
+#include "lib/util/tevent_unix.h"
+#include "ctdbd_conn.h"
+#include "ctdb_srvids.h"
+#include "source3/smbd/proto.h"
+#include "ctdb/include/ctdb_protocol.h"
+
+/*
+ * notifyd's representation of a notify instance
+ */
+struct notifyd_instance {
+	struct server_id client;
+	struct notify_instance instance;
+
+	void *sys_watch; /* inotify/fam/etc handle */
+
+	/*
+	 * Filters after sys_watch took responsibility of some bits
+	 */
+	uint32_t internal_filter;
+	uint32_t internal_subdir_filter;
+};
+
+/*
+ * Record held per path
+ */
+struct notifyd_entry {
+	struct notifyd_instance instances[1]; /* we allocate more */
+};
+
+static bool notifyd_rec_change(struct messaging_context *msg_ctx,
+			       struct messaging_rec **prec,
+			       void *private_data);
+static bool notifyd_trigger(struct messaging_context *msg_ctx,
+			    struct messaging_rec **prec,
+			    void *private_data);
+static bool notifyd_get_db(struct messaging_context *msg_ctx,
+			   struct messaging_rec **prec,
+			   void *private_data);
+static void notifyd_broadcast_reclog(struct server_id src,
+				     struct messaging_reclog *log);
+
+struct notifyd_peer {
+	struct messaging_context *msg_ctx;
+	uint32_t vnn;
+
+	uint64_t rec_index;
+	struct db_context *db;
+};
+
+struct notifyd_peers {
+	uint32_t num_peers;
+	struct notifyd_peer **peers;
+};
+
+static struct tevent_req *notifyd_broadcast_reclog_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct server_id src,
+	struct messaging_reclog *log);
+static int notifyd_broadcast_reclog_recv(struct tevent_req *req);
+
+static struct tevent_req *notifyd_snoop_peers_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct messaging_context *msg_ctx,
+	sys_notify_watch_fn sys_notify_watch,
+	struct sys_notify_context *sys_notify_ctx,
+	struct notifyd_peers *peers);
+static int notifyd_snoop_peers_recv(struct tevent_req *req);
+
+struct notifyd_state {
+	struct tevent_context *ev;
+	struct messaging_context *msg_ctx;
+	struct db_context *entries;
+	struct notifyd_peers *peers;
+
+	sys_notify_watch_fn sys_notify_watch;
+	struct sys_notify_context *sys_notify_ctx;
+
+	struct messaging_reclog *log;
+};
+
+static int sys_notify_watch_dummy(TALLOC_CTX *mem_ctx,
+				  struct sys_notify_context *ctx,
+				  const char *path,
+				  uint32_t *filter,
+				  uint32_t *subdir_filter,
+				  void (*callback)(struct sys_notify_context *ctx,
+						   void *private_data,
+						   struct notify_event *ev),
+				  void *private_data,
+				  void *handle_p)
+{
+	void **handle = handle_p;
+	*handle = NULL;
+	return 0;
+}
+
+static void notifyd_handler_done(struct tevent_req *subreq);
+static void notifyd_snoop_peers_finished(struct tevent_req *subreq);
+static void notifyd_broadcast_reclog_finished(struct tevent_req *subreq);
+
+struct tevent_req *notifyd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+				struct messaging_context *msg_ctx,
+				sys_notify_watch_fn sys_notify_watch,
+				struct sys_notify_context *sys_notify_ctx)
+{
+	struct tevent_req *req, *subreq;
+	struct notifyd_state *state;
+	struct server_id_db *names_db;
+	struct ctdbd_connection *ctdbd_conn;
+	NTSTATUS status;
+	int ret;
+
+	req = tevent_req_create(mem_ctx, &state, struct notifyd_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->msg_ctx = msg_ctx;
+
+	if (sys_notify_watch == NULL) {
+		sys_notify_watch = sys_notify_watch_dummy;
+	}
+
+	state->sys_notify_watch = sys_notify_watch;
+	state->sys_notify_ctx = sys_notify_ctx;
+
+	state->entries = db_open_rbt(state);
+	if (tevent_req_nomem(state->entries, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = messaging_handler_send(state, ev, msg_ctx,
+					MSG_SMB_NOTIFY_REC_CHANGE,
+					notifyd_rec_change, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_handler_done, req);
+
+	subreq = messaging_handler_send(state, ev, msg_ctx,
+					MSG_SMB_NOTIFY_TRIGGER,
+					notifyd_trigger, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_handler_done, req);
+
+	subreq = messaging_handler_send(state, ev, msg_ctx,
+					MSG_SMB_NOTIFY_GET_DB,
+					notifyd_get_db, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_handler_done, req);
+
+	names_db = messaging_names_db(msg_ctx);
+
+	ret = server_id_db_add(names_db, "notify-daemon");
+	if (ret != 0) {
+		DEBUG(10, ("%s: server_id_db_add failed: %s\n",
+			   __func__, strerror(ret)));
+		tevent_req_error(req, ret);
+		return tevent_req_post(req, ev);
+	}
+
+	ctdbd_conn = messaging_ctdbd_connection();
+	if (ctdbd_conn == NULL) {
+		/*
+		 * No cluster around, skip the db replication engine
+		 */
+		return req;
+	}
+
+	state->log = talloc_zero(state, struct messaging_reclog);
+	if (tevent_req_nomem(state->log, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = notifyd_broadcast_reclog_send(
+		state->log, ev, messaging_server_id(msg_ctx), state->log);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_broadcast_reclog_finished,
+				req);
+
+	status = register_with_ctdbd(ctdbd_conn,
+				     CTDB_SRVID_SAMBA_NOTIFY_PROXY);
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_error(req, map_errno_from_nt_status(status));
+		return tevent_req_post(req, ev);
+	}
+
+	state->peers = talloc_zero(state, struct notifyd_peers);
+	if (tevent_req_nomem(state->peers, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = notifyd_snoop_peers_send(
+		state, ev, msg_ctx,
+		state->sys_notify_watch, state->sys_notify_ctx,
+		state->peers);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_snoop_peers_finished, req);
+
+	return req;
+}
+
+static void notifyd_handler_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	int ret;
+
+	ret = messaging_handler_recv(subreq);
+	TALLOC_FREE(subreq);
+	tevent_req_error(req, ret);
+}
+
+static void notifyd_broadcast_reclog_finished(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	int ret;
+
+	ret = notifyd_broadcast_reclog_recv(subreq);
+	TALLOC_FREE(subreq);
+	tevent_req_error(req, ret);
+}
+
+static void notifyd_snoop_peers_finished(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	int ret;
+
+	ret = notifyd_snoop_peers_recv(subreq);
+	TALLOC_FREE(subreq);
+	tevent_req_error(req, ret);
+}
+
+int notifyd_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+/*
+ * Parse an entry in the notifyd_context->entries database
+ */
+
+static bool notifyd_parse_entry(uint8_t *buf, size_t buflen,
+				struct notifyd_entry **entry,
+				size_t *num_instances)
+{
+	size_t instances_len;
+
+	if (buflen < offsetof(struct notifyd_entry, instances)) {
+		DEBUG(1, ("%s: buffer too short: %u\n",
+			  __func__, (unsigned)buflen));
+		return false;
+	}
+
+	instances_len = buflen - offsetof(struct notifyd_entry, instances);
+	if ((instances_len % sizeof(struct notifyd_instance)) != 0) {
+		DEBUG(1, ("%s: invalid buffer size: %u\n",
+			  __func__, (unsigned)buflen));
+		return false;
+	}
+
+	if (entry != NULL) {
+		*entry = (struct notifyd_entry *)buf;
+	}
+	if (num_instances != NULL) {
+		*num_instances =
+			instances_len / sizeof(struct notifyd_instance);
+	}
+	return true;
+}
+
+static size_t notifyd_entry_size(size_t num_instances)
+{
+	return offsetof(struct notifyd_entry, instances) +
+		(sizeof(struct notifyd_instance) * num_instances);
+}
+
+static void notifyd_sys_callback(struct sys_notify_context *ctx,
+				 void *private_data, struct notify_event *ev);
+
+static bool notifyd_apply_rec_change(
+	const struct server_id *client,
+	const char *path, size_t pathlen,
+	const struct notify_instance *chg,
+	struct db_context *entries,
+	sys_notify_watch_fn sys_notify_watch,
+	struct sys_notify_context *sys_notify_ctx,
+	struct messaging_context *msg_ctx)
+{
+	struct db_record *rec;
+	struct notifyd_entry *entry;
+	size_t num_instances;
+	size_t i;
+	struct notifyd_instance *instance;
+	TDB_DATA value;
+	NTSTATUS status;
+	bool ok = false;
+
+	if (pathlen == 0) {
+		DEBUG(1, ("%s: pathlen==0\n", __func__));
+		return false;
+	}
+	if (path[pathlen-1] != '\0') {
+		DEBUG(1, ("%s: path not 0-terminated\n", __func__));
+		return false;
+	}
+
+	DEBUG(10, ("%s: path=%s, filter=%u, subdir_filter=%u, "
+		   "private_data=%p\n", __func__, path,
+		   (unsigned)chg->filter, (unsigned)chg->subdir_filter,
+		   chg->private_data));
+
+	rec = dbwrap_fetch_locked(
+		entries, entries,
+		make_tdb_data((const uint8_t *)path, pathlen-1));
+
+	if (rec == NULL) {
+		DEBUG(1, ("%s: dbwrap_fetch_locked failed\n", __func__));
+		goto fail;
+	}
+
+	num_instances = 0;
+	value = dbwrap_record_get_value(rec);
+
+	if (value.dsize != 0) {
+		if (!notifyd_parse_entry(value.dptr, value.dsize, NULL,
+					 &num_instances)) {
+			goto fail;
+		}
+	}
+
+	/*
+	 * Overallocate by one instance to avoid a realloc when adding
+	 */
+	entry = talloc_size(rec, notifyd_entry_size(num_instances+1));
+	if (entry == NULL) {
+		DEBUG(1, ("%s: talloc failed\n", __func__));
+		goto fail;
+	}
+
+	if (value.dsize != 0) {
+		memcpy(entry, value.dptr, value.dsize);
+	}
+
+	for (i=0; i<num_instances; i++) {
+		instance = &entry->instances[i];
+
+		if (server_id_equal(&instance->client, client) &&
+		    (instance->instance.private_data == chg->private_data)) {
+			break;
+		}
+	}
+
+	if (i < num_instances) {
+		instance->instance = *chg;
+	} else {
+		/*
+		 * We've overallocated for one instance
+		 */
+		instance = &entry->instances[num_instances];
+
+		*instance = (struct notifyd_instance) {
+			.client = *client,
+			.instance = *chg,
+			.internal_filter = chg->filter,
+			.internal_subdir_filter = chg->subdir_filter
+		};
+
+		num_instances += 1;
+	}
+
+	if ((instance->instance.filter != 0) ||
+	    (instance->instance.subdir_filter != 0)) {
+		int ret;
+
+		TALLOC_FREE(instance->sys_watch);
+
+		ret = sys_notify_watch(entries, sys_notify_ctx, path,
+				       &instance->internal_filter,
+				       &instance->internal_subdir_filter,
+				       notifyd_sys_callback, msg_ctx,
+				       &instance->sys_watch);
+		if (ret != 0) {
+			DEBUG(1, ("%s: inotify_watch returned %s\n",
+				  __func__, strerror(errno)));
+		}
+	}
+
+	if ((instance->instance.filter == 0) &&
+	    (instance->instance.subdir_filter == 0)) {
+		/* This is a delete request */
+		TALLOC_FREE(instance->sys_watch);
+		*instance = entry->instances[num_instances-1];
+		num_instances -= 1;
+	}
+
+	DEBUG(10, ("%s: %s has %u instances\n", __func__,
+		   path, (unsigned)num_instances));
+
+	if (num_instances == 0) {
+		status = dbwrap_record_delete(rec);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1, ("%s: dbwrap_record_delete returned %s\n",
+				  __func__, nt_errstr(status)));
+			goto fail;
+		}
+	} else {
+		value = make_tdb_data(
+			(uint8_t *)entry, notifyd_entry_size(num_instances));
+
+		status = dbwrap_record_store(rec, value, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1, ("%s: dbwrap_record_store returned %s\n",
+				  __func__, nt_errstr(status)));
+			goto fail;
+		}
+	}
+
+	ok = true;
+fail:
+	TALLOC_FREE(rec);
+	return ok;
+}
+
+static void notifyd_sys_callback(struct sys_notify_context *ctx,
+				 void *private_data, struct notify_event *ev)
+{
+	struct messaging_context *msg_ctx = talloc_get_type_abort(
+		private_data, struct messaging_context);
+	struct notify_trigger_msg msg;
+	struct iovec iov[4];
+	char slash = '/';
+
+	msg = (struct notify_trigger_msg) {
+		.when = timespec_current(),
+		.action = ev->action,
+		.filter = UINT32_MAX
+	};
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_trigger_msg, path);
+	iov[1].iov_base = discard_const_p(char, ev->dir);
+	iov[1].iov_len = strlen(ev->dir);
+	iov[2].iov_base = &slash;
+	iov[2].iov_len = 1;
+	iov[3].iov_base = discard_const_p(char, ev->path);
+	iov[3].iov_len = strlen(ev->path)+1;
+
+	messaging_send_iov(
+		msg_ctx, messaging_server_id(msg_ctx),
+		MSG_SMB_NOTIFY_TRIGGER, iov, ARRAY_SIZE(iov), NULL, 0);
+}
+
+static bool notifyd_parse_rec_change(uint8_t *buf, size_t bufsize,
+				     struct notify_rec_change_msg **pmsg,
+				     size_t *pathlen)
+{
+	struct notify_rec_change_msg *msg;
+
+	if (bufsize < offsetof(struct notify_rec_change_msg, path) + 1) {
+		DEBUG(1, ("%s: message too short, ignoring: %u\n", __func__,
+			  (unsigned)bufsize));
+		return false;
+	}
+
+	*pmsg = msg = (struct notify_rec_change_msg *)buf;
+	*pathlen = bufsize - offsetof(struct notify_rec_change_msg, path);
+
+	DEBUG(10, ("%s: Got rec_change_msg filter=%u, subdir_filter=%u, "
+		   "private_data=%p, path=%.*s\n",
+		   __func__, (unsigned)msg->instance.filter,
+		   (unsigned)msg->instance.subdir_filter,
+		   msg->instance.private_data, (int)(*pathlen), msg->path));
+
+	return true;
+}
+
+static bool notifyd_rec_change(struct messaging_context *msg_ctx,
+			       struct messaging_rec **prec,
+			       void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct server_id_buf idbuf;
+	struct messaging_rec *rec = *prec;
+	struct messaging_rec **tmp;
+	struct messaging_reclog *log;
+	struct notify_rec_change_msg *msg;
+	size_t pathlen;
+	bool ok;
+
+	DEBUG(10, ("%s: Got %d bytes from %s\n", __func__,
+		   (unsigned)rec->buf.length,
+		   server_id_str_buf(rec->src, &idbuf)));
+
+	ok = notifyd_parse_rec_change(rec->buf.data, rec->buf.length,
+				      &msg, &pathlen);
+	if (!ok) {
+		return true;
+	}
+
+	ok = notifyd_apply_rec_change(
+		&rec->src, msg->path, pathlen, &msg->instance,
+		state->entries, state->sys_notify_watch, state->sys_notify_ctx,
+		state->msg_ctx);
+	if (!ok) {
+		DEBUG(1, ("%s: notifyd_apply_rec_change failed, ignoring\n",
+			  __func__));
+		return true;
+	}
+
+	if (state->log == NULL) {
+		return true;
+	}
+	log = state->log;
+
+	tmp = talloc_realloc(log, log->recs, struct messaging_rec *,
+			     log->num_recs+1);
+	if (tmp == NULL) {
+		DEBUG(1, ("%s: talloc_realloc failed, ignoring\n", __func__));
+		return true;
+	}
+	log->recs = tmp;
+
+	log->recs[log->num_recs] = talloc_move(log->recs, prec);
+	log->num_recs += 1;
+
+	if (log->num_recs >= 1000) {
+		/*
+		 * Don't let the log grow too large
+		 */
+		notifyd_broadcast_reclog(messaging_server_id(msg_ctx), log);
+	}
+
+	return true;
+}
+
+struct notifyd_trigger_state {
+	struct messaging_context *msg_ctx;
+	struct notify_trigger_msg *msg;
+	bool recursive;
+	bool covered_by_sys_notify;
+};
+
+static void notifyd_trigger_parser(TDB_DATA key, TDB_DATA data,
+				   void *private_data);
+
+static bool notifyd_trigger(struct messaging_context *msg_ctx,
+			    struct messaging_rec **prec,
+			    void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct server_id my_id = messaging_server_id(msg_ctx);
+	struct messaging_rec *rec = *prec;
+	struct notifyd_trigger_state tstate;
+	const char *path;
+	const char *p, *next_p;
+
+	if (rec->buf.length < offsetof(struct notify_trigger_msg, path) + 1) {
+		DEBUG(1, ("message too short, ignoring: %u\n",
+			  (unsigned)rec->buf.length));
+		return true;
+	}
+	if (rec->buf.data[rec->buf.length-1] != 0) {
+		DEBUG(1, ("%s: path not 0-terminated, ignoring\n", __func__));
+		return true;
+	}
+
+	tstate.msg_ctx = msg_ctx;
+
+	tstate.covered_by_sys_notify = (rec->src.vnn == my_id.vnn);
+	tstate.covered_by_sys_notify &= !server_id_equal(&rec->src, &my_id);
+
+	tstate.msg = (struct notify_trigger_msg *)rec->buf.data;
+	path = tstate.msg->path;
+
+	DEBUG(10, ("%s: Got trigger_msg action=%u, filter=%u, path=%s\n",
+		   __func__, (unsigned)tstate.msg->action,
+		   (unsigned)tstate.msg->filter, path));
+
+	if (path[0] != '/') {
+		DEBUG(1, ("%s: path %s does not start with /, ignoring\n",
+			  __func__, path));
+		return true;
+	}
+
+	for (p = strchr(path+1, '/'); p != NULL; p = next_p) {
+		ptrdiff_t path_len = p - path;
+		TDB_DATA key;
+		uint32_t i;
+
+		next_p = strchr(p+1, '/');
+		tstate.recursive = (next_p != NULL);
+
+		DEBUG(10, ("%s: Trying path %.*s\n", __func__,
+			   (int)path_len, path));
+
+		key = (TDB_DATA) { .dptr = discard_const_p(uint8, path),
+				   .dsize = path_len };
+
+		dbwrap_parse_record(state->entries, key,
+				    notifyd_trigger_parser, &tstate);
+
+		if (state->peers == NULL) {
+			continue;
+		}
+
+		if (rec->src.vnn != my_id.vnn) {
+			continue;
+		}
+
+		for (i=0; i<state->peers->num_peers; i++) {
+			if (state->peers->peers[i]->db == NULL) {
+				/*
+				 * Inactive peer, did not get a db yet
+				 */
+				continue;
+			}
+			dbwrap_parse_record(state->peers->peers[i]->db, key,
+					    notifyd_trigger_parser, &tstate);
+		}
+	}
+
+	return true;
+}
+
+static void notifyd_send_delete(struct messaging_context *msg_ctx,
+				TDB_DATA key,
+				struct notifyd_instance *instance);
+
+static void notifyd_trigger_parser(TDB_DATA key, TDB_DATA data,
+				   void *private_data)
+
+{
+	struct notifyd_trigger_state *tstate = private_data;
+	struct notify_event_msg msg;
+	struct iovec iov[2];
+	size_t path_len = key.dsize;
+	struct notifyd_entry *entry = NULL;
+	size_t num_instances = 0;
+	size_t i;
+
+	if (!notifyd_parse_entry(data.dptr, data.dsize, &entry,
+				 &num_instances)) {
+		DEBUG(1, ("%s: Could not parse notifyd_entry\n", __func__));
+		return;
+	}
+
+	DEBUG(10, ("%s: Found %u instances for %.*s\n", __func__,
+		   (unsigned)num_instances, (int)key.dsize,
+		   (char *)key.dptr));
+
+	msg = (struct notify_event_msg) {
+		.action = tstate->msg->action
+	};
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_event_msg, path);
+	iov[1].iov_base = tstate->msg->path + path_len + 1;
+	iov[1].iov_len = strlen((char *)(iov[1].iov_base)) + 1;
+
+	for (i=0; i<num_instances; i++) {
+		struct notifyd_instance *instance = &entry->instances[i];
+		uint32_t i_filter;
+		NTSTATUS status;
+
+		if (tstate->covered_by_sys_notify) {
+			if (tstate->recursive) {
+				i_filter = instance->internal_subdir_filter;
+			} else {
+				i_filter = instance->internal_filter;
+			}
+		} else {
+			if (tstate->recursive) {
+				i_filter = instance->instance.subdir_filter;
+			} else {
+				i_filter = instance->instance.filter;
+			}
+		}
+
+		if ((i_filter & tstate->msg->filter) == 0) {
+			continue;
+		}
+
+		msg.private_data = instance->instance.private_data;
+
+		status = messaging_send_iov(
+			tstate->msg_ctx, instance->client,
+			MSG_PVFS_NOTIFY, iov, ARRAY_SIZE(iov), NULL, 0);
+
+		if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
+		    procid_is_local(&instance->client)) {
+			/*
+			 * That process has died
+			 */
+			notifyd_send_delete(tstate->msg_ctx, key, instance);
+			continue;
+		}
+
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(1, ("%s: messaging_send_iov returned %s\n",
+				  __func__, nt_errstr(status)));
+		}
+	}
+}
+
+static void notifyd_send_delete(struct messaging_context *msg_ctx,
+				TDB_DATA key,
+				struct notifyd_instance *instance)
+{
+	struct notify_rec_change_msg msg = {};
+	char path[key.dsize+1];
+	struct iovec iov[2];
+	NTSTATUS status;
+
+	msg.instance.private_data = instance->instance.private_data;
+	memcpy(path, key.dptr, key.dsize);
+	path[key.dsize] = 0;
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
+	iov[1].iov_base = path;
+	iov[1].iov_len = sizeof(path);
+
+	status = messaging_send_iov_from(
+		msg_ctx, instance->client, messaging_server_id(msg_ctx),
+		MSG_SMB_NOTIFY_REC_CHANGE, iov, ARRAY_SIZE(iov), NULL, 0);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: messaging_send_iov_from returned %s\n",
+			   __func__, nt_errstr(status)));
+	}
+}
+
+static bool notifyd_get_db(struct messaging_context *msg_ctx,
+			   struct messaging_rec **prec,
+			   void *private_data)
+{
+	struct notifyd_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_state);
+	struct messaging_rec *rec = *prec;
+	struct server_id_buf id1, id2;
+	NTSTATUS status;
+	uint64_t rec_index = UINT64_MAX;
+	size_t dbsize;
+	uint8_t *buf;
+
+	dbsize = dbwrap_marshall(state->entries, NULL, 0);
+
+	buf = talloc_array(rec, uint8_t, dbsize+8);
+	if (buf == NULL) {
+		DEBUG(1, ("%s: talloc_array(%ju) failed\n",
+			  __func__, (uintmax_t)dbsize+8));
+		return true;
+	}
+
+	if (state->log != NULL) {
+		rec_index = state->log->rec_index;
+	}
+	SBVAL(buf, 0, rec_index);
+
+	dbsize = dbwrap_marshall(state->entries, buf+8, dbsize);
+
+	if (dbsize != (talloc_get_size(buf)-8)) {
+		DEBUG(1, ("%s: dbsize changed: %ju->%ju\n", __func__,
+			  (uintmax_t)(talloc_get_size(buf)-8),
+			  (uintmax_t)dbsize));
+		TALLOC_FREE(buf);
+		return true;
+	}
+
+	DEBUG(10, ("%s: Sending %u bytes to %s->%s\n", __func__,
+		   (unsigned)talloc_get_size(buf),
+		   server_id_str_buf(messaging_server_id(msg_ctx), &id1),
+		   server_id_str_buf(rec->src, &id2)));
+
+	status = messaging_send_buf(msg_ctx, rec->src, MSG_SMB_NOTIFY_DB,
+				    buf, talloc_get_size(buf));
+	TALLOC_FREE(buf);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("%s: messaging_send_buf failed: %s\n",
+			  __func__, nt_errstr(status)));
+	}
+
+	return true;
+}
+
+static void notifyd_broadcast_reclog(struct server_id src,
+				     struct messaging_reclog *log)
+{
+	struct ctdbd_connection *ctdbd_conn;
+	NTSTATUS status;
+	enum ndr_err_code ndr_err;
+	struct messaging_rec msg;
+
+	if (log == NULL) {
+		return;
+	}
+
+	DEBUG(10, ("%s: rec_index=%ju, num_recs=%u\n", __func__,
+		   (uintmax_t)log->rec_index, (unsigned)log->num_recs));
+
+	ctdbd_conn = messaging_ctdbd_connection();
+	if (ctdbd_conn == NULL) {
+		goto done;
+	}
+
+	msg = (struct messaging_rec) {
+		.msg_version = MESSAGE_VERSION,
+		.msg_type = MSG_SMB_NOTIFY_REC_CHANGES,
+		.src = src
+	};
+
+	ndr_err = ndr_push_struct_blob(
+		&msg.buf, log, log,
+		(ndr_push_flags_fn_t)ndr_push_messaging_reclog);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(1, ("%s: ndr_push_messaging_recs failed: %s\n",
+			  __func__, ndr_errstr(ndr_err)));
+		goto done;
+	}
+
+	status = ctdbd_messaging_send(
+		ctdbd_conn, CTDB_BROADCAST_VNNMAP,
+		CTDB_SRVID_SAMBA_NOTIFY_PROXY, &msg);
+	TALLOC_FREE(msg.buf.data);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("%s: ctdbd_messaging_send failed: %s\n",
+			  __func__, nt_errstr(status)));
+		goto done;
+	}
+
+	log->rec_index += 1;
+
+done:
+	log->num_recs = 0;
+	TALLOC_FREE(log->recs);
+}
+
+struct notifyd_db_pull_state {
+	uint32_t vnn;
+	uint64_t rec_index;
+	struct db_context *db;
+};
+
+static bool notifyd_db_pull_filter(struct messaging_rec *req,
+				  void *private_data);
+static void notifyd_db_pull_done(struct tevent_req *subreq);
+
+static struct tevent_req *notifyd_db_pull_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct messaging_context *msg_ctx, uint32_t vnn)
+{
+	struct tevent_req *req, *subreq;
+	struct notifyd_db_pull_state *state;
+	struct ctdbd_connection *ctdbd_conn;
+	struct messaging_rec msg;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct notifyd_db_pull_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->vnn = vnn;
+
+	state->db = db_open_rbt(state);
+	if (tevent_req_nomem(state->db, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	ctdbd_conn = messaging_ctdbd_connection();
+	if (ctdbd_conn == NULL) {
+		tevent_req_error(req, ENOSYS);
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = messaging_filtered_read_send(
+		state, ev, msg_ctx, notifyd_db_pull_filter, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_db_pull_done, req);
+
+	msg = (struct messaging_rec) {
+		.msg_version	= MESSAGE_VERSION,
+		.msg_type	= MSG_SMB_NOTIFY_GET_DB,
+		.src		= messaging_server_id(msg_ctx),
+		.buf		= data_blob_null
+	};
+
+	status = ctdbd_messaging_send(
+		ctdbd_conn, vnn, CTDB_SRVID_SAMBA_NOTIFY_PROXY, &msg);
+	if (!NT_STATUS_IS_OK(status)) {
+		int err = map_errno_from_nt_status(status);
+		tevent_req_error(req, err);
+		return tevent_req_post(req, ev);
+	}
+
+	return req;
+}
+
+static bool notifyd_db_pull_filter(struct messaging_rec *rec,
+				  void *private_data)
+{
+	struct notifyd_db_pull_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_db_pull_state);
+
+	if (rec->num_fds != 0) {
+		return false;
+	}
+	if (rec->msg_type != MSG_SMB_NOTIFY_DB) {
+		return false;
+	}
+	return rec->src.vnn == state->vnn;
+}
+
+static void notifyd_db_pull_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct notifyd_db_pull_state *state = tevent_req_data(
+		req, struct notifyd_db_pull_state);
+	struct server_id_buf idbuf;
+	struct messaging_rec *rec;
+	NTSTATUS status;
+	int ret;
+
+	ret = messaging_filtered_read_recv(subreq, state, &rec);
+	TALLOC_FREE(subreq);
+	if (tevent_req_error(req, ret)) {
+		return;
+	}
+
+	DEBUG(10, ("%s: Got %u bytes from %s\n", __func__,
+		   (unsigned)rec->buf.length,
+		   server_id_str_buf(rec->src, &idbuf)));
+
+	if (rec->buf.length < 8) {
+		tevent_req_error(req, EINVAL);
+		return;
+	}
+
+	state->rec_index = BVAL(rec->buf.data, 0);
+
+	status = dbwrap_unmarshall(state->db, rec->buf.data + 8,
+				   rec->buf.length - 8);
+	if (!NT_STATUS_IS_OK(status)) {
+		int err = map_errno_from_nt_status(status);
+		tevent_req_error(req, err);
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+static int notifyd_db_pull_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+				uint64_t *rec_index, struct db_context **db)
+{
+	struct notifyd_db_pull_state *state = tevent_req_data(
+		req, struct notifyd_db_pull_state);
+	int err;
+
+	if (tevent_req_is_unix_error(req, &err)) {
+		return err;
+	}
+	if (rec_index != NULL) {
+		*rec_index = state->rec_index;
+	}
+	if (db != NULL) {
+		*db = talloc_move(mem_ctx, &state->db);
+	}
+	return 0;
+}
+
+struct notifyd_broadcast_reclog_state {
+	struct tevent_context *ev;
+	struct server_id src;
+	struct messaging_reclog *log;
+};
+
+static void notifyd_broadcast_reclog_next(struct tevent_req *subreq);
+
+static struct tevent_req *notifyd_broadcast_reclog_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct server_id src,
+	struct messaging_reclog *log)
+{
+	struct tevent_req *req, *subreq;
+	struct notifyd_broadcast_reclog_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct notifyd_broadcast_reclog_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->src = src;
+	state->log = log;
+
+	subreq = tevent_wakeup_send(state, state->ev,
+				    timeval_current_ofs_msec(1000));
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_broadcast_reclog_next, req);
+	return req;
+}
+
+static void notifyd_broadcast_reclog_next(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct notifyd_broadcast_reclog_state *state = tevent_req_data(
+		req, struct notifyd_broadcast_reclog_state);
+	bool ok;
+
+	ok = tevent_wakeup_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (!ok) {
+		tevent_req_oom(req);
+		return;
+	}
+
+	notifyd_broadcast_reclog(state->src, state->log);
+
+	subreq = tevent_wakeup_send(state, state->ev,
+				    timeval_current_ofs_msec(1000));
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, notifyd_broadcast_reclog_next, req);
+}
+
+static int notifyd_broadcast_reclog_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+struct notifyd_peer_recv_state {
+	struct tevent_context *ev;
+	struct messaging_context *msg_ctx;
+	sys_notify_watch_fn sys_notify_watch;
+	struct sys_notify_context *sys_notify_ctx;
+	struct notifyd_peer *peer;
+};
+
+static void notifyd_peer_recv_got_db(struct tevent_req *subreq);
+static int notifyd_add_proxy_syswatches(struct db_record *rec,
+					void *private_data);
+static bool notifyd_peer_recv_filter_changes(struct messaging_rec *rec,
+					     void *private_data);
+static void notifyd_peer_recv_got_changes(struct tevent_req *subreq);
+
+static struct tevent_req *notifyd_peer_recv_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct messaging_context *msg_ctx,
+	sys_notify_watch_fn sys_notify_watch,
+	struct sys_notify_context *sys_notify_ctx,
+	struct notifyd_peer *peer)
+{
+	struct tevent_req *req, *subreq;
+	struct notifyd_peer_recv_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct notifyd_peer_recv_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->msg_ctx = msg_ctx;
+	state->sys_notify_watch = sys_notify_watch;
+	state->sys_notify_ctx = sys_notify_ctx;
+	state->peer = peer;
+
+	subreq = notifyd_db_pull_send(state, ev, msg_ctx, peer->vnn);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_peer_recv_got_db, req);
+
+	subreq = messaging_filtered_read_send(state, state->ev, state->msg_ctx,
+					      notifyd_peer_recv_filter_changes,
+					      peer);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+        tevent_req_set_callback(subreq, notifyd_peer_recv_got_changes, req);
+
+	return req;
+}
+
+static void notifyd_peer_recv_got_db(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct notifyd_peer_recv_state *state = tevent_req_data(
+		req, struct notifyd_peer_recv_state);
+	struct db_context *db = NULL;
+	uint64_t rec_index = 0;
+	int ret;
+
+	ret = notifyd_db_pull_recv(subreq, state->peer, &rec_index, &db);
+	TALLOC_FREE(subreq);
+	if (tevent_req_error(req, ret)) {
+		return;
+	}
+
+	dbwrap_traverse_read(db, notifyd_add_proxy_syswatches, state, NULL);
+
+	TALLOC_FREE(state->peer->db);
+	state->peer->db = db;
+	state->peer->rec_index = rec_index;
+}
+
+static int notifyd_add_proxy_syswatches(struct db_record *rec,
+					void *private_data)
+{
+	struct notifyd_peer_recv_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_peer_recv_state);
+	struct db_context *db = dbwrap_record_get_db(rec);
+	TDB_DATA key = dbwrap_record_get_key(rec);
+	TDB_DATA value = dbwrap_record_get_value(rec);
+	struct notifyd_entry *entry;
+	size_t i, num_instances;
+	char path[key.dsize+1];
+	bool ok;
+
+	memcpy(path, key.dptr, key.dsize);
+	path[key.dsize] = '\0';
+
+	ok = notifyd_parse_entry(value.dptr, value.dsize, &entry,
+				 &num_instances);
+	if (!ok) {
+		DEBUG(1, ("%s: Could not parse notifyd entry for %s\n",
+			  __func__, path));
+		return 0;
+	}
+
+	for (i=0; i<num_instances; i++) {
+		struct notifyd_instance *instance = &entry->instances[i];
+		uint32_t filter = instance->instance.filter;
+		uint32_t subdir_filter = instance->instance.subdir_filter;
+		int ret;
+
+		ret = state->sys_notify_watch(
+			db, state->sys_notify_ctx, path,
+			&filter, &subdir_filter,
+			notifyd_sys_callback, state->msg_ctx,
+			&instance->sys_watch);
+		if (ret != 0) {
+			DEBUG(1, ("%s: inotify_watch returned %s\n",
+				  __func__, strerror(errno)));
+		}
+	}
+
+	return 0;
+}
+
+static bool notifyd_peer_recv_filter_changes(struct messaging_rec *rec,
+					     void *private_data)
+{
+	struct notifyd_peer *peer = talloc_get_type_abort(
+		private_data, struct notifyd_peer);
+
+	if (rec->num_fds != 0) {
+		return false;
+	}
+	if (rec->msg_type != MSG_SMB_NOTIFY_REC_CHANGES) {
+		return false;
+	}
+	if (rec->src.vnn != peer->vnn) {
+		return false;
+	}
+	if (peer->db == NULL) {
+		/*
+		 * No db yet, we can't deal with the changes yet
+		 */
+		return false;
+	}
+	return true;
+}
+
+static void notifyd_peer_recv_got_changes(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct notifyd_peer_recv_state *state = tevent_req_data(
+		req, struct notifyd_peer_recv_state);
+	struct notifyd_peer *peer = state->peer;
+	struct messaging_rec *rec;
+	struct messaging_reclog *log;
+	struct server_id_buf idbuf;
+	enum ndr_err_code ndr_err;
+	uint32_t i;
+	int ret;
+
+	ret = messaging_filtered_read_recv(subreq, state, &rec);
+	TALLOC_FREE(subreq);
+	if (tevent_req_error(req, ret)) {
+		return;
+	}
+
+	subreq = messaging_filtered_read_send(state, state->ev, state->msg_ctx,
+					      notifyd_peer_recv_filter_changes,
+					      peer);
+	if (tevent_req_nomem(subreq, req)) {
+		goto done;
+	}
+        tevent_req_set_callback(subreq, notifyd_peer_recv_got_changes, req);
+
+	log = talloc(rec, struct messaging_reclog);
+	if (log == NULL) {
+		DEBUG(10, ("%s: talloc failed\n", __func__));
+		goto done;
+	}
+
+	ndr_err = ndr_pull_struct_blob_all(
+		&rec->buf, log, log,
+		(ndr_pull_flags_fn_t)ndr_pull_messaging_reclog);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_pull_messaging_reclog failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		goto done;
+	}
+
+	DEBUG(10, ("%s: Got %u recs index %ju from %s\n", __func__,
+		   (unsigned)log->num_recs, (uintmax_t)log->rec_index,
+		   server_id_str_buf(rec->src, &idbuf)));
+
+	if (log->rec_index != peer->rec_index) {
+		DEBUG(3, ("%s: Got rec index %ju from %s, expected %ju\n",
+			  __func__, (uintmax_t)log->rec_index,
+			  server_id_str_buf(rec->src, &idbuf),
+			  (uintmax_t)peer->rec_index));
+
+		subreq = notifyd_db_pull_send(state, state->ev, state->msg_ctx,
+					      peer->vnn);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, notifyd_peer_recv_got_db, req);
+		return;
+	}
+
+	for (i=0; i<log->num_recs; i++) {
+		struct messaging_rec *r = log->recs[i];
+		struct notify_rec_change_msg *chg;
+		size_t pathlen;
+		bool ok;
+
+		ok = notifyd_parse_rec_change(r->buf.data, r->buf.length,
+					      &chg, &pathlen);
+		if (!ok) {
+			tevent_req_error(req, EBADMSG);
+			return;
+		}
+
+		ok = notifyd_apply_rec_change(&r->src, chg->path, pathlen,
+					      &chg->instance, peer->db,
+					      state->sys_notify_watch,
+					      state->sys_notify_ctx,
+					      state->msg_ctx);
+		if (!ok) {
+			tevent_req_error(req, EBADMSG);
+			return;
+		}
+	}
+
+	peer->rec_index += 1;
+
+done:
+	TALLOC_FREE(rec);
+}
+
+static int notifyd_peer_recv_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+static struct notifyd_peer *notifyd_peer_new(
+	TALLOC_CTX *mem_ctx, struct messaging_context *msg_ctx,
+	uint32_t vnn)
+{
+	struct notifyd_peer *peer;
+
+	peer = talloc_zero(mem_ctx, struct notifyd_peer);
+	if (peer == NULL) {
+		return NULL;
+	}
+	peer->msg_ctx = msg_ctx;
+	peer->vnn = vnn;
+
+	return peer;
+}
+
+struct notifyd_snoop_peers_state {
+	struct tevent_context *ev;
+	struct messaging_context *msg_ctx;
+	sys_notify_watch_fn sys_notify_watch;
+	struct sys_notify_context *sys_notify_ctx;
+	struct notifyd_peers *peers;
+};
+
+static bool notifyd_snoop_peers_filter(struct messaging_rec *rec,
+				       void *private_data);
+static void notifyd_peer_recv_finished(struct tevent_req *subreq);
+static void notifyd_snoop_peers_done(struct tevent_req *subreq);
+
+static struct tevent_req *notifyd_snoop_peers_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct messaging_context *msg_ctx,
+	sys_notify_watch_fn sys_notify_watch,
+	struct sys_notify_context *sys_notify_ctx,
+	struct notifyd_peers *peers)
+{
+	struct tevent_req *req, *subreq;
+	struct notifyd_snoop_peers_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct notifyd_snoop_peers_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->msg_ctx = msg_ctx;
+	state->sys_notify_watch = sys_notify_watch;
+	state->sys_notify_ctx = sys_notify_ctx;
+	state->peers = peers;
+
+	subreq = messaging_filtered_read_send(
+		state, ev, msg_ctx, notifyd_snoop_peers_filter, state);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, notifyd_snoop_peers_done, req);
+	return req;
+}
+
+static bool notifyd_snoop_peers_filter(struct messaging_rec *rec,
+				       void *private_data)
+{
+	struct notifyd_snoop_peers_state *state = talloc_get_type_abort(
+		private_data, struct notifyd_snoop_peers_state);
+	struct server_id my_id = messaging_server_id(state->msg_ctx);
+	struct tevent_req *subreq;
+	struct notifyd_peers *peers = state->peers;
+	struct notifyd_peer *p, **tmp;
+	uint32_t i, vnn;
+
+	/*
+	 * The only point of this filter is to listen for new vnns
+	 * broadcasting MSG_SMB_NOTIFY_REC_CHANGES to us and then create a new
+	 * listener req. We are not interested in the changes yet, so always
+	 * return false here.
+	 */
+
+	if (rec->num_fds != 0) {
+		goto done;
+	}
+	if (rec->msg_type != MSG_SMB_NOTIFY_REC_CHANGES) {
+		goto done;
+	}
+	vnn = rec->src.vnn;
+
+	if (vnn == my_id.vnn) {
+		goto done;
+	}
+
+	for (i=0; i<peers->num_peers; i++) {
+		if (peers->peers[i]->vnn == vnn) {
+			goto done;
+		}
+	}
+
+	DEBUG(10, ("%s: Found unknown peer %u\n", __func__,
+		   (unsigned)vnn));
+
+	/*
+	 * We got a broadcast from a vnn we've not seen. Create a new peer.
+	 */
+
+	tmp = talloc_realloc(peers, peers->peers, struct notifyd_peer *,
+			     peers->num_peers+1);
+	if (tmp == NULL) {
+		DEBUG(10, ("%s: talloc_realloc failed\n", __func__));
+		goto done;
+	}
+	peers->peers = tmp;
+
+	p = notifyd_peer_new(peers->peers, state->msg_ctx, vnn);
+	if (p == NULL) {
+		DEBUG(10, ("%s: notifyd_peer_new failed\n", __func__));
+		goto done;
+	}
+
+	subreq = notifyd_peer_recv_send(p, state->ev, state->msg_ctx,
+					state->sys_notify_watch,
+					state->sys_notify_ctx, p);
+	if (subreq == NULL) {
+		DEBUG(10, ("%s: notifyd_peer_recv_send failed\n", __func__));
+		TALLOC_FREE(p);
+		goto done;
+	}
+	tevent_req_set_callback(subreq, notifyd_peer_recv_finished, p);
+
+	peers->peers[peers->num_peers] = p;
+	peers->num_peers += 1;
+
+	/*
+	 * "Fall through" to return false, we have all we need: A new peer
+	 */
+done:
+	return false;
+}
+
+static void notifyd_snoop_peers_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct messaging_rec *rec;
+	int ret;
+
+	/*
+	 * We'll never get here normally, our filter always returns
+	 * false. We're not interested in the record, so recv it as a child of
+	 * subreq, which we'll free immediately.
+	 */
+
+	ret = messaging_filtered_read_recv(subreq, subreq, &rec);
+	TALLOC_FREE(rec);
+	if (tevent_req_error(req, ret)) {
+		return;
+	}
+	tevent_req_done(req);
+}
+
+static int notifyd_snoop_peers_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
+static void notifyd_peer_recv_finished(struct tevent_req *subreq)
+{
+	struct notifyd_peer *peer = tevent_req_callback_data(
+		subreq, struct notifyd_peer);
+	int ret;
+
+	ret = notifyd_peer_recv_recv(subreq);
+	TALLOC_FREE(subreq);
+	DEBUG(10, ("%s: notifyd_peer_recv for peer %u returned %s\n", __func__,
+		   (unsigned)peer->vnn, strerror(ret)));
+}
diff --git a/source3/smbd/notifyd/notifyd.h b/source3/smbd/notifyd/notifyd.h
new file mode 100644
index 0000000..084ddf9
--- /dev/null
+++ b/source3/smbd/notifyd/notifyd.h
@@ -0,0 +1,142 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 __NOTIFYD_NOTIFYD_H__
+#define __NOTIFYD_NOTIFYD_H__
+
+#include "includes.h"
+#include "librpc/gen_ndr/notify.h"
+#include "librpc/gen_ndr/messaging.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "messages.h"
+#include "tdb.h"
+#include "util_tdb.h"
+
+/*
+ * Filechangenotify based on asynchronous messages
+ *
+ * smbds talk to local notify daemons to inform them about paths they are
+ * interested in. They also tell local notify daemons about changes they have
+ * done to the file system. There's two message types from smbd to
+ * notifyd. The first is used to inform notifyd about changes in notify
+ * interest. These are only sent from smbd to notifyd if the SMB client issues
+ * FileChangeNotify requests.
+ */
+
+/*
+ * The notifyd implementation is designed to cope with multiple daemons taking
+ * care of just a subset of smbds. The goal is to minimize the traffic between
+ * the notify daemons. The idea behind this is a samba/ctdb cluster, but it
+ * could also be used to spread the load of notifyd instances on a single
+ * node, should this become a bottleneck. The following diagram illustrates
+ * the setup. The numbers in the boxes are node:process ids.
+ *
+ *         +-----------+                  +-----------+
+ *         |notifyd 0:5|------------------|notifyd 1:6|
+ *         +-----------+                  +-----------+
+ *            / |  \                         /    \
+ *           /  |   \                       /      \
+ *   +--------+ | +--------+        +--------+   +--------+
+ *   |smbd 0:1| | |smbd 0:4|        |smbd 1:7|   |smbd 1:2|
+ *   +--------+ | +--------+        +--------+   +--------+
+ *              |
+ *     	   +---------+
+ *	   |smbd 0:20|
+ *	   +---------+
+ *
+ * Suppose 0:1 and 0:4 are interested in changes for /foo and 0:20 creates the
+ * file /foo/bar, if everything fully connected, 0:20 would have to send two
+ * local messages, one to 0:1 and one to 0:4. With the notifyd design, 0:20
+ * only has to send one message, it lets notifyd 0:5 do the hard work to
+ * multicast the change to 0:1 and 0:4.
+ *
+ * Now lets assume 1:7 on the other node creates /foo/baz. It tells its
+ * notifyd 1:6 about this change. All 1:6 will know about is that its peer
+ * notifyd 0:5 is interested in the change. Thus it forwards the event to 0:5,
+ * which sees it as if it came from just another local event creator. 0:5 will
+ * multicast the change to 0:1 and 0:4. To prevent notify loops, the message
+ * from 1:6 to 0:5 will carry a "proxied" flag, so that 0:5 will only forward
+ * the event to local clients.
+ */
+
+/*
+ * Data that notifyd maintains per smbd notify instance
+ */
+struct notify_instance {
+	struct timespec creation_time;
+	uint32_t filter;
+	uint32_t subdir_filter;
+	void *private_data;
+};
+
+/* MSG_SMB_NOTIFY_REC_CHANGE payload */
+struct notify_rec_change_msg {
+	struct notify_instance instance;
+	char path[];
+};
+
+/*
+ * The second message from smbd to notifyd is sent whenever an smbd makes a
+ * file system change. It tells notifyd to inform all interested parties about
+ * that change. This is the message that needs to be really fast in smbd
+ * because it is called a lot.
+ */
+
+/* MSG_SMB_NOTIFY_TRIGGER payload */
+struct notify_trigger_msg {
+	struct timespec when;
+	uint32_t action;
+	uint32_t filter;
+	char path[];
+};
+
+/*
+ * In response to a MSG_SMB_NOTIFY_TRIGGER message notifyd walks its database
+ * and sends out the following message to all interested clients
+ */
+
+/* MSG_PVFS_NOTIFY payload */
+struct notify_event_msg {
+	struct timespec when;
+	void *private_data;
+	uint32_t action;
+	char path[];
+};
+
+struct sys_notify_context;
+
+typedef int (*sys_notify_watch_fn)(TALLOC_CTX *mem_ctx,
+				   struct sys_notify_context *ctx,
+				   const char *path,
+				   uint32_t *filter,
+				   uint32_t *subdir_filter,
+				   void (*callback)(struct sys_notify_context *ctx,
+						    void *private_data,
+						    struct notify_event *ev),
+				   void *private_data,
+				   void *handle_p);
+
+struct tevent_req *notifyd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+				struct messaging_context *msg_ctx,
+				sys_notify_watch_fn sys_notify_watch,
+				struct sys_notify_context *sys_notify_ctx);
+int notifyd_recv(struct tevent_req *req);
+
+#endif
diff --git a/source3/smbd/notifyd/tests.c b/source3/smbd/notifyd/tests.c
new file mode 100644
index 0000000..a7f925a
--- /dev/null
+++ b/source3/smbd/notifyd/tests.c
@@ -0,0 +1,119 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 "notifyd.h"
+#include "messages.h"
+#include "lib/util/server_id_db.h"
+
+int main(int argc, const char *argv[])
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct tevent_context *ev;
+	struct messaging_context *msg_ctx;
+	struct server_id_db *names;
+	struct server_id notifyd;
+	struct tevent_req *req;
+	unsigned i;
+	bool ok;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s <smb.conf-file>\n", argv[0]);
+		exit(1);
+	}
+
+	load_case_tables();
+	setup_logging(argv[0], DEBUG_STDOUT);
+	lp_load_global(argv[1]);
+
+	ev = tevent_context_init(NULL);
+	if (ev == NULL) {
+		fprintf(stderr, "tevent_context_init failed\n");
+		exit(1);
+	}
+
+	msg_ctx = messaging_init(ev, ev);
+	if (msg_ctx == NULL) {
+		fprintf(stderr, "messaging_init failed\n");
+		exit(1);
+	}
+
+	names = messaging_names_db(msg_ctx);
+
+	ok = server_id_db_lookup_one(names, "notify-daemon", &notifyd);
+	if (!ok) {
+		fprintf(stderr, "no notifyd\n");
+		exit(1);
+	}
+
+	for (i=0; i<50000; i++) {
+		struct notify_rec_change_msg msg = {
+			.instance.filter = UINT32_MAX,
+			.instance.subdir_filter = UINT32_MAX
+		};
+		char path[64];
+		size_t len;
+		struct iovec iov[2];
+		NTSTATUS status;
+
+		len = snprintf(path, sizeof(path), "/tmp%u", i);
+
+		iov[0].iov_base = &msg;
+		iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
+		iov[1].iov_base = path;
+		iov[1].iov_len = len+1;
+
+		status = messaging_send_iov(
+			msg_ctx, notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+			iov, ARRAY_SIZE(iov), NULL, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			fprintf(stderr, "messaging_send_iov returned %s\n",
+				nt_errstr(status));
+			exit(1);
+		}
+
+		msg.instance.filter = 0;
+		msg.instance.subdir_filter = 0;
+
+		status = messaging_send_iov(
+			msg_ctx, notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+			iov, ARRAY_SIZE(iov), NULL, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			fprintf(stderr, "messaging_send_iov returned %s\n",
+				nt_errstr(status));
+			exit(1);
+		}
+	}
+
+	req = messaging_read_send(ev, ev, msg_ctx, MSG_PONG);
+	if (req == NULL) {
+		fprintf(stderr, "messaging_read_send failed\n");
+		exit(1);
+	}
+	messaging_send_buf(msg_ctx, notifyd, MSG_PING, NULL, 0);
+
+	ok = tevent_req_poll(req, ev);
+	if (!ok) {
+		fprintf(stderr, "tevent_req_poll failed\n");
+		exit(1);
+	}
+
+	TALLOC_FREE(frame);
+	return 0;
+}
diff --git a/source3/smbd/notifyd/wscript_build b/source3/smbd/notifyd/wscript_build
new file mode 100644
index 0000000..aa1ba3a
--- /dev/null
+++ b/source3/smbd/notifyd/wscript_build
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+bld.SAMBA3_SUBSYSTEM('notifyd',
+		     source='notifyd.c',
+                     deps='samba3core')
+
+bld.SAMBA3_BINARY('notifyd-tests',
+                  source='tests.c',
+                  install=False,
+                  deps='''
+                    param
+                  ''')
diff --git a/source3/wscript_build b/source3/wscript_build
index 73a6d57..ae80dbb 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1513,6 +1513,7 @@ bld.RECURSE('../examples/pdb')
 bld.RECURSE('../examples/VFS')
 bld.RECURSE('lib/netapi/tests')
 bld.RECURSE('lib/netapi/examples')
+bld.RECURSE('smbd/notifyd')
 
 bld.ENFORCE_GROUP_ORDERING()
 bld.CHECK_PROJECT_RULES()
-- 
1.9.1


From 7840c995852c832d08067cd14ba4f47de43f18f0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:55:25 +0100
Subject: [PATCH 07/15] smbd: Start the notify daemon

For this we need the kernel change notify stuff to be global: There's only one
notifyd and we have to pass over the kernel change notify watch function
---
 source3/smbd/server.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++
 source3/wscript_build |  1 +
 2 files changed, 95 insertions(+)

diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 8207bf1..f00df9d 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -48,6 +48,7 @@
 #include "lib/smbd_shim.h"
 #include "scavenger.h"
 #include "locking/leases_db.h"
+#include "smbd/notifyd/notifyd.h"
 
 struct smbd_open_socket;
 struct smbd_child_pid;
@@ -381,6 +382,96 @@ static void smb_tell_num_children(struct messaging_context *ctx, void *data,
 	}
 }
 
+static void notifyd_stopped(struct tevent_req *req);
+
+static struct tevent_req *notifyd_req(struct messaging_context *msg_ctx,
+				      struct tevent_context *ev)
+{
+	struct tevent_req *req;
+	sys_notify_watch_fn sys_notify_watch = NULL;
+	struct sys_notify_context *sys_notify_ctx = NULL;
+
+	if (lp_kernel_change_notify()) {
+
+#ifdef HAVE_INOTIFY
+		if (lp_parm_bool(-1, "notify", "inotify", true)) {
+			sys_notify_watch = inotify_watch;
+		}
+#endif
+
+#ifdef HAVE_FAM
+		if (lp_parm_bool(-1, "notify", "fam",
+				 (sys_notify_watch == NULL))) {
+			sys_notify_watch = fam_watch;
+		}
+#endif
+	}
+
+	if (sys_notify_watch != NULL) {
+		sys_notify_ctx = sys_notify_context_create(msg_ctx, ev);
+		if (sys_notify_ctx == NULL) {
+			return NULL;
+		}
+	}
+
+	req = notifyd_send(msg_ctx, ev, msg_ctx,
+			   sys_notify_watch, sys_notify_ctx);
+	if (req == NULL) {
+		TALLOC_FREE(sys_notify_ctx);
+		return NULL;
+	}
+	tevent_req_set_callback(req, notifyd_stopped, msg_ctx);
+
+	return req;
+}
+
+static void notifyd_stopped(struct tevent_req *req)
+{
+	int ret;
+
+	ret = notifyd_recv(req);
+	TALLOC_FREE(req);
+	DEBUG(1, ("notifyd stopped: %s\n", strerror(ret)));
+}
+
+static bool smbd_notifyd_init(struct messaging_context *msg, bool interactive)
+{
+	struct tevent_context *ev = messaging_tevent_context(msg);
+	struct tevent_req *req;
+	pid_t pid;
+	NTSTATUS status;
+
+	if (interactive) {
+		req = notifyd_req(msg, ev);
+		return (req != NULL);
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		DEBUG(1, ("%s: fork failed: %s\n", __func__,
+			  strerror(errno)));
+		return false;
+	}
+
+	if (pid != 0) {
+		return true;
+	}
+
+	status = reinit_after_fork(msg, ev, true);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("%s: reinit_after_fork failed: %s\n",
+			  __func__, nt_errstr(status)));
+		exit(1);
+	}
+
+	req = notifyd_req(msg, ev);
+	if (req == NULL) {
+		exit(1);
+	}
+	tevent_req_set_callback(req, notifyd_stopped, msg);
+	tevent_req_poll(req, ev);
+	return true;
+}
 
 /*
   at most every smbd:cleanuptime seconds (default 20), we scan the BRL
@@ -1460,6 +1551,9 @@ extern void build_options(bool screen);
 	if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
 		exit_daemon("Samba cannot init notification", EACCES);
 	}
+	if (!smbd_notifyd_init(msg_ctx, interactive)) {
+		exit_daemon("Samba cannot init notification", EACCES);
+	}
 
 	if (!messaging_parent_dgm_cleanup_init(msg_ctx)) {
 		exit(1);
diff --git a/source3/wscript_build b/source3/wscript_build
index ae80dbb..101267b 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -629,6 +629,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    NDR_SMB_ACL
                    netapi
                    NDR_IOCTL
+                   notifyd
                    ''' + bld.env['dmapi_lib']
                    + (bld.CONFIG_GET('SAMBA_FAM_LIBS')
                          if bld.CONFIG_SET('SAMBA_FAM_LIBS') else ''),
-- 
1.9.1


From 64780ec33ec0f1fde77799d1ad193364404b86cc Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:58:47 +0100
Subject: [PATCH 08/15] smbd: Don't start the notify cleanup anymore

We don't have a database to clean up anymore
---
 source3/smbd/server.c | 100 --------------------------------------------------
 1 file changed, 100 deletions(-)

diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index f00df9d..9203f82 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -257,103 +257,6 @@ static void smbd_parent_id_cache_delete(struct messaging_context *ctx,
 	messaging_send_to_children(ctx, msg_type, msg_data);
 }
 
-struct smbd_parent_notify_state {
-	struct tevent_context *ev;
-	struct messaging_context *msg;
-	uint32_t msgtype;
-	struct notify_context *notify;
-};
-
-static int smbd_parent_notify_cleanup(void *private_data);
-static void smbd_parent_notify_cleanup_done(struct tevent_req *req);
-static void smbd_parent_notify_proxy_done(struct tevent_req *req);
-
-static bool smbd_parent_notify_init(TALLOC_CTX *mem_ctx,
-				    struct messaging_context *msg,
-				    struct tevent_context *ev)
-{
-	struct smbd_parent_notify_state *state;
-	struct tevent_req *req;
-
-	state = talloc(mem_ctx, struct smbd_parent_notify_state);
-	if (state == NULL) {
-		return false;
-	}
-	state->msg = msg;
-	state->ev = ev;
-	state->msgtype = MSG_SMB_NOTIFY_CLEANUP;
-
-	state->notify = notify_init(state, msg, ev);
-	if (state->notify == NULL) {
-		goto fail;
-	}
-	req = background_job_send(
-		state, state->ev, state->msg, &state->msgtype, 1,
-		lp_parm_int(-1, "smbd", "notify cleanup interval", 60),
-		smbd_parent_notify_cleanup, state->notify);
-	if (req == NULL) {
-		goto fail;
-	}
-	tevent_req_set_callback(req, smbd_parent_notify_cleanup_done, state);
-
-	if (!lp_clustering()) {
-		return true;
-	}
-
-	req = notify_cluster_proxy_send(state, ev, state->notify);
-	if (req == NULL) {
-		goto fail;
-	}
-	tevent_req_set_callback(req, smbd_parent_notify_proxy_done, state);
-
-	return true;
-fail:
-	TALLOC_FREE(state);
-	return false;
-}
-
-static int smbd_parent_notify_cleanup(void *private_data)
-{
-	struct notify_context *notify = talloc_get_type_abort(
-		private_data, struct notify_context);
-	notify_cleanup(notify);
-	return lp_parm_int(-1, "smbd", "notify cleanup interval", 60);
-}
-
-static void smbd_parent_notify_cleanup_done(struct tevent_req *req)
-{
-	struct smbd_parent_notify_state *state = tevent_req_callback_data(
-		req, struct smbd_parent_notify_state);
-	NTSTATUS status;
-
-	status = background_job_recv(req);
-	TALLOC_FREE(req);
-	DEBUG(1, ("notify cleanup job ended with %s\n", nt_errstr(status)));
-
-	/*
-	 * Provide self-healing: Whatever the error condition was, it
-	 * will have printed it into log.smbd. Just retrying and
-	 * spamming log.smbd once a minute should be fine.
-	 */
-	req = background_job_send(
-		state, state->ev, state->msg, &state->msgtype, 1, 60,
-		smbd_parent_notify_cleanup, state->notify);
-	if (req == NULL) {
-		DEBUG(1, ("background_job_send failed\n"));
-		return;
-	}
-	tevent_req_set_callback(req, smbd_parent_notify_cleanup_done, state);
-}
-
-static void smbd_parent_notify_proxy_done(struct tevent_req *req)
-{
-	int ret;
-
-	ret = notify_cluster_proxy_recv(req);
-	TALLOC_FREE(req);
-	DEBUG(1, ("notify proxy job ended with %s\n", strerror(ret)));
-}
-
 static void add_child_pid(struct smbd_parent_context *parent,
 			  pid_t pid)
 {
@@ -1548,9 +1451,6 @@ extern void build_options(bool screen);
 		exit_daemon("Samba cannot init leases", EACCES);
 	}
 
-	if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
-		exit_daemon("Samba cannot init notification", EACCES);
-	}
 	if (!smbd_notifyd_init(msg_ctx, interactive)) {
 		exit_daemon("Samba cannot init notification", EACCES);
 	}
-- 
1.9.1


From bf3cd686623a33512e22912131f157b694964b7f Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 17:05:16 +0100
Subject: [PATCH 09/15] smbd: Replace the tdb-based notify_internal with
 notify_msg

---
 source3/smbd/notify_internal.c | 1272 ----------------------------------------
 source3/smbd/notify_msg.c      |  268 +++++++++
 source3/smbd/proto.h           |    5 -
 source3/wscript_build          |    4 +-
 4 files changed, 270 insertions(+), 1279 deletions(-)
 delete mode 100644 source3/smbd/notify_internal.c
 create mode 100644 source3/smbd/notify_msg.c

diff --git a/source3/smbd/notify_internal.c b/source3/smbd/notify_internal.c
deleted file mode 100644
index e612f16..0000000
--- a/source3/smbd/notify_internal.c
+++ /dev/null
@@ -1,1272 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-
-   Copyright (C) Andrew Tridgell 2006
-   Copyright (C) Volker Lendecke 2012
-
-   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/>.
-*/
-
-/*
-  this is the change notify database. It implements mechanisms for
-  storing current change notify waiters in a tdb, and checking if a
-  given event matches any of the stored notify waiters.
-*/
-
-#include "includes.h"
-#include "system/filesys.h"
-#include "librpc/gen_ndr/ndr_notify.h"
-#include "dbwrap/dbwrap.h"
-#include "dbwrap/dbwrap_open.h"
-#include "dbwrap/dbwrap_tdb.h"
-#include "smbd/smbd.h"
-#include "messages.h"
-#include "lib/tdb_wrap/tdb_wrap.h"
-#include "util_tdb.h"
-#include "lib/param/param.h"
-#include "lib/dbwrap/dbwrap_cache.h"
-#include "ctdb_srvids.h"
-#include "ctdbd_conn.h"
-#include "ctdb_conn.h"
-#include "lib/util/tevent_unix.h"
-
-struct notify_list {
-	struct notify_list *next, *prev;
-	const char *path;
-	void (*callback)(void *, struct timespec, const struct notify_event *);
-	void *private_data;
-};
-
-struct notify_context {
-	struct messaging_context *msg;
-	struct notify_list *list;
-
-	/*
-	 * The notify database is split up into two databases: One
-	 * relatively static index db and the real notify db with the
-	 * volatile entries.
-	 */
-
-	/*
-	 * "db_notify" is indexed by pathname. Per record it stores an
-	 * array of notify_db_entry structs. These represent the
-	 * notify records as requested by the smb client. This
-	 * database is always held locally, it is never clustered.
-	 */
-	struct db_context *db_notify;
-
-	/*
-	 * "db_index" is indexed by pathname. The records are an array
-	 * of VNNs which have any interest in notifies for this path
-	 * name.
-	 *
-	 * In the non-clustered case this database is cached in RAM by
-	 * means of db_cache_open, which maintains a cache per
-	 * process. Cache consistency is maintained by the tdb
-	 * sequence number.
-	 *
-	 * In the clustered case right now we can not use the tdb
-	 * sequence number, but by means of read only records we
-	 * should be able to avoid a lot of full migrations.
-	 *
-	 * In both cases, it is important to keep the update
-	 * operations to db_index to a minimum. This is achieved by
-	 * delayed deletion. When a db_notify is initially created,
-	 * the db_index record is also created. When more notifies are
-	 * added for a path, then only the db_notify record needs to be
-	 * modified, the db_index record is not touched. When the last
-	 * entry from the db_notify record is deleted, the db_index
-	 * record is not immediately deleted. Instead, the db_notify
-	 * record is replaced with a current timestamp. A regular
-	 * cleanup process will delete all db_index records that are
-	 * older than a minute.
-	 */
-	struct db_context *db_index;
-};
-
-static void notify_trigger_local(struct notify_context *notify,
-				 uint32_t action, uint32_t filter,
-				 const char *path, size_t path_len,
-				 bool recursive);
-static NTSTATUS notify_send(struct notify_context *notify,
-			    struct server_id *pid,
-			    const char *path, uint32_t action,
-			    void *private_data);
-static NTSTATUS notify_add_entry(struct db_record *rec,
-				 const struct notify_db_entry *e,
-				 bool *p_add_idx);
-static NTSTATUS notify_add_idx(struct db_record *rec, uint32_t vnn);
-
-static NTSTATUS notify_del_entry(struct db_record *rec,
-				 const struct server_id *pid,
-				 void *private_data);
-static NTSTATUS notify_del_idx(struct db_record *rec, uint32_t vnn);
-
-static int notify_context_destructor(struct notify_context *notify);
-
-static void notify_handler(struct messaging_context *msg_ctx,
-			   void *private_data, uint32_t msg_type,
-			   struct server_id server_id, DATA_BLOB *data);
-
-struct notify_context *notify_init(TALLOC_CTX *mem_ctx,
-				   struct messaging_context *msg,
-				   struct tevent_context *ev)
-{
-	struct loadparm_context *lp_ctx;
-	struct notify_context *notify;
-	char *db_path;
-
-	notify = talloc(mem_ctx, struct notify_context);
-	if (notify == NULL) {
-		goto fail;
-	}
-	notify->msg = msg;
-	notify->list = NULL;
-
-	lp_ctx = loadparm_init_s3(notify, loadparm_s3_helpers());
-
-	db_path = lock_path("notify.tdb");
-	if (db_path == NULL) {
-		goto fail;
-	}
-
-	notify->db_notify = db_open_tdb(
-		notify, lp_ctx, db_path,
-		0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
-		O_RDWR|O_CREAT, 0644, DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE);
-	talloc_unlink(notify, lp_ctx);
-	TALLOC_FREE(db_path);
-	if (notify->db_notify == NULL) {
-		goto fail;
-	}
-
-	db_path = lock_path("notify_index.tdb");
-	if (db_path == NULL) {
-		goto fail;
-	}
-
-	notify->db_index = db_open(
-		notify, db_path,
-		0, TDB_SEQNUM|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
-		O_RDWR|O_CREAT, 0644, DBWRAP_LOCK_ORDER_3, DBWRAP_FLAG_NONE);
-	TALLOC_FREE(db_path);
-	if (notify->db_index == NULL) {
-		goto fail;
-	}
-	if (!lp_clustering()) {
-		notify->db_index = db_open_cache(notify, notify->db_index);
-		if (notify->db_index == NULL) {
-			goto fail;
-		}
-	}
-
-	if (notify->msg != NULL) {
-		NTSTATUS status;
-
-		status = messaging_register(notify->msg, notify,
-					    MSG_PVFS_NOTIFY, notify_handler);
-		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(1, ("messaging_register returned %s\n",
-				  nt_errstr(status)));
-			goto fail;
-		}
-	}
-
-	talloc_set_destructor(notify, notify_context_destructor);
-
-	return notify;
-fail:
-	TALLOC_FREE(notify);
-	return NULL;
-}
-
-static int notify_context_destructor(struct notify_context *notify)
-{
-	DEBUG(10, ("notify_context_destructor called\n"));
-
-	if (notify->msg != NULL) {
-		messaging_deregister(notify->msg, MSG_PVFS_NOTIFY, notify);
-	}
-
-	while (notify->list != NULL) {
-		DEBUG(10, ("Removing private_data=%p\n",
-			   notify->list->private_data));
-		notify_remove(notify, notify->list->private_data);
-	}
-	return 0;
-}
-
-NTSTATUS notify_add(struct notify_context *notify,
-		    const char *path, uint32_t filter, uint32_t subdir_filter,
-		    void (*callback)(void *, struct timespec,
-				     const struct notify_event *),
-		    void *private_data)
-{
-	struct notify_db_entry e;
-	struct notify_list *listel;
-	struct db_record *notify_rec, *idx_rec;
-	bool add_idx;
-	NTSTATUS status;
-	TDB_DATA key, notify_copy;
-
-	if (notify == NULL) {
-		return NT_STATUS_NOT_IMPLEMENTED;
-	}
-
-	DEBUG(10, ("notify_add: path=[%s], private_data=%p\n", path,
-		   private_data));
-
-	listel = talloc(notify, struct notify_list);
-	if (listel == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	listel->callback = callback;
-	listel->private_data = private_data;
-	listel->path = talloc_strdup(listel, path);
-	if (listel->path == NULL) {
-		TALLOC_FREE(listel);
-		return NT_STATUS_NO_MEMORY;
-	}
-	DLIST_ADD(notify->list, listel);
-
-	ZERO_STRUCT(e);
-	e.filter = filter;
-	e.subdir_filter = subdir_filter;
-	e.server = messaging_server_id(notify->msg);
-	e.private_data = private_data;
-
-	key = string_tdb_data(path);
-
-	notify_rec = dbwrap_fetch_locked(notify->db_notify,
-					 talloc_tos(), key);
-	if (notify_rec == NULL) {
-		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
-		goto fail;
-	}
-
-	/*
-	 * Make a copy of the notify_rec for easy restore in case
-	 * updating the index_rec fails;
-	 */
-	notify_copy = dbwrap_record_get_value(notify_rec);
-	if (notify_copy.dsize != 0) {
-		notify_copy.dptr = (uint8_t *)talloc_memdup(
-			notify_rec, notify_copy.dptr,
-			notify_copy.dsize);
-		if (notify_copy.dptr == NULL) {
-			TALLOC_FREE(notify_rec);
-			status = NT_STATUS_NO_MEMORY;
-			goto fail;
-		}
-	}
-
-	if (DEBUGLEVEL >= 10) {
-		NDR_PRINT_DEBUG(notify_db_entry, &e);
-	}
-
-	status = notify_add_entry(notify_rec, &e, &add_idx);
-	if (!NT_STATUS_IS_OK(status)) {
-		goto fail;
-	}
-	if (!add_idx) {
-		/*
-		 * Someone else has added the idx entry already
-		 */
-		TALLOC_FREE(notify_rec);
-		return NT_STATUS_OK;
-	}
-
-	idx_rec = dbwrap_fetch_locked(notify->db_index,
-				      talloc_tos(), key);
-	if (idx_rec == NULL) {
-		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
-		goto restore_notify;
-	}
-	status = notify_add_idx(idx_rec, get_my_vnn());
-	if (!NT_STATUS_IS_OK(status)) {
-		goto restore_notify;
-	}
-
-	TALLOC_FREE(idx_rec);
-	TALLOC_FREE(notify_rec);
-	return NT_STATUS_OK;
-
-restore_notify:
-	if (notify_copy.dsize != 0) {
-		dbwrap_record_store(notify_rec, notify_copy, 0);
-	} else {
-		dbwrap_record_delete(notify_rec);
-	}
-	TALLOC_FREE(notify_rec);
-fail:
-	DLIST_REMOVE(notify->list, listel);
-	TALLOC_FREE(listel);
-	return status;
-}
-
-static NTSTATUS notify_add_entry(struct db_record *rec,
-				 const struct notify_db_entry *e,
-				 bool *p_add_idx)
-{
-	TDB_DATA value = dbwrap_record_get_value(rec);
-	struct notify_db_entry *entries;
-	size_t num_entries;
-	bool add_idx = true;
-	NTSTATUS status;
-
-	if (value.dsize == sizeof(time_t)) {
-		DEBUG(10, ("Re-using deleted entry\n"));
-		value.dsize = 0;
-		add_idx = false;
-	}
-
-	if ((value.dsize % sizeof(struct notify_db_entry)) != 0) {
-		DEBUG(1, ("Invalid value.dsize = %u\n",
-			  (unsigned)value.dsize));
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	num_entries = value.dsize / sizeof(struct notify_db_entry);
-
-	if (num_entries != 0) {
-		add_idx = false;
-	}
-
-	entries = talloc_array(rec, struct notify_db_entry, num_entries + 1);
-	if (entries == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	memcpy(entries, value.dptr, value.dsize);
-
-	entries[num_entries] = *e;
-	value = make_tdb_data((uint8_t *)entries, talloc_get_size(entries));
-	status = dbwrap_record_store(rec, value, 0);
-	TALLOC_FREE(entries);
-	if (!NT_STATUS_IS_OK(status)) {
-		return status;
-	}
-	*p_add_idx = add_idx;
-	return NT_STATUS_OK;
-}
-
-static NTSTATUS notify_add_idx(struct db_record *rec, uint32_t vnn)
-{
-	TDB_DATA value = dbwrap_record_get_value(rec);
-	uint32_t *vnns;
-	size_t i, num_vnns;
-	NTSTATUS status;
-
-	if ((value.dsize % sizeof(uint32_t)) != 0) {
-		DEBUG(1, ("Invalid value.dsize = %u\n",
-			  (unsigned)value.dsize));
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	num_vnns = value.dsize / sizeof(uint32_t);
-	vnns = (uint32_t *)value.dptr;
-
-	for (i=0; i<num_vnns; i++) {
-		if (vnns[i] == vnn) {
-			return NT_STATUS_OK;
-		}
-		if (vnns[i] > vnn) {
-			break;
-		}
-	}
-
-	value.dptr = (uint8_t *)talloc_realloc(
-		rec, value.dptr, uint32_t, num_vnns + 1);
-	if (value.dptr == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	value.dsize = talloc_get_size(value.dptr);
-
-	vnns = (uint32_t *)value.dptr;
-
-	memmove(&vnns[i+1], &vnns[i], sizeof(uint32_t) * (num_vnns - i));
-	vnns[i] = vnn;
-
-	status = dbwrap_record_store(rec, value, 0);
-	if (!NT_STATUS_IS_OK(status)) {
-		return status;
-	}
-	return NT_STATUS_OK;
-}
-
-NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
-{
-	struct server_id pid;
-	struct notify_list *listel;
-	struct db_record *notify_rec;
-	NTSTATUS status;
-
-	if ((notify == NULL) || (notify->msg == NULL)) {
-		return NT_STATUS_NOT_IMPLEMENTED;
-	}
-
-	DEBUG(10, ("notify_remove: private_data=%p\n", private_data));
-
-	pid = messaging_server_id(notify->msg);
-
-	for (listel=notify->list;listel;listel=listel->next) {
-		if (listel->private_data == private_data) {
-			DLIST_REMOVE(notify->list, listel);
-			break;
-		}
-	}
-	if (listel == NULL) {
-		DEBUG(10, ("%p not found\n", private_data));
-		return NT_STATUS_NOT_FOUND;
-	}
-	notify_rec = dbwrap_fetch_locked(notify->db_notify, talloc_tos(),
-					 string_tdb_data(listel->path));
-	TALLOC_FREE(listel);
-	if (notify_rec == NULL) {
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	status = notify_del_entry(notify_rec, &pid, private_data);
-	DEBUG(10, ("del_entry returned %s\n", nt_errstr(status)));
-	TALLOC_FREE(notify_rec);
-	return status;
-}
-
-static NTSTATUS notify_del_entry(struct db_record *rec,
-				 const struct server_id *pid,
-				 void *private_data)
-{
-	TDB_DATA value = dbwrap_record_get_value(rec);
-	struct notify_db_entry *entries;
-	size_t i, num_entries;
-	time_t now;
-
-	DEBUG(10, ("del_entry called for %s %p\n", procid_str_static(pid),
-		   private_data));
-
-	if ((value.dsize % sizeof(struct notify_db_entry)) != 0) {
-		DEBUG(1, ("Invalid value.dsize = %u\n",
-			  (unsigned)value.dsize));
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	num_entries = value.dsize / sizeof(struct notify_db_entry);
-	entries = (struct notify_db_entry *)value.dptr;
-
-	for (i=0; i<num_entries; i++) {
-		struct notify_db_entry *e = &entries[i];
-
-		if (DEBUGLEVEL >= 10) {
-			NDR_PRINT_DEBUG(notify_db_entry, e);
-		}
-
-		if (e->private_data != private_data) {
-			continue;
-		}
-		if (serverid_equal(&e->server, pid)) {
-			break;
-		}
-	}
-	if (i == num_entries) {
-		return NT_STATUS_NOT_FOUND;
-	}
-	entries[i] = entries[num_entries-1];
-	value.dsize -= sizeof(struct notify_db_entry);
-
-	if (value.dsize == 0) {
-		now = time(NULL);
-		value.dptr = (uint8_t *)&now;
-		value.dsize = sizeof(now);
-	}
-	return dbwrap_record_store(rec, value, 0);
-}
-
-struct notify_trigger_index_state {
-	TALLOC_CTX *mem_ctx;
-	uint32_t *vnns;
-	uint32_t my_vnn;
-	bool found_my_vnn;
-};
-
-static void notify_trigger_index_parser(TDB_DATA key, TDB_DATA data,
-					void *private_data)
-{
-	struct notify_trigger_index_state *state =
-		(struct notify_trigger_index_state *)private_data;
-	uint32_t *new_vnns;
-	size_t i, num_vnns, num_new_vnns, num_remote_vnns;
-
-	if ((data.dsize % sizeof(uint32_t)) != 0) {
-		DEBUG(1, ("Invalid record size in notify index db: %u\n",
-			  (unsigned)data.dsize));
-		return;
-	}
-	new_vnns = (uint32_t *)data.dptr;
-	num_new_vnns = data.dsize / sizeof(uint32_t);
-	num_remote_vnns = num_new_vnns;
-
-	for (i=0; i<num_new_vnns; i++) {
-		if (new_vnns[i] == state->my_vnn) {
-			state->found_my_vnn = true;
-			num_remote_vnns -= 1;
-		}
-	}
-	if (num_remote_vnns == 0) {
-		return;
-	}
-
-	num_vnns = talloc_array_length(state->vnns);
-	state->vnns = talloc_realloc(state->mem_ctx, state->vnns, uint32_t,
-				     num_vnns + num_remote_vnns);
-	if (state->vnns == NULL) {
-		DEBUG(1, ("talloc_realloc failed\n"));
-		return;
-	}
-
-	for (i=0; i<num_new_vnns; i++) {
-		if (new_vnns[i] != state->my_vnn) {
-			state->vnns[num_vnns] = new_vnns[i];
-			num_vnns += 1;
-		}
-	}
-}
-
-static int vnn_cmp(const void *p1, const void *p2)
-{
-	const uint32_t *vnn1 = (const uint32_t *)p1;
-	const uint32_t *vnn2 = (const uint32_t *)p2;
-
-	if (*vnn1 < *vnn2) {
-		return -1;
-	}
-	if (*vnn1 == *vnn2) {
-		return 0;
-	}
-	return 1;
-}
-
-static bool notify_push_remote_blob(TALLOC_CTX *mem_ctx, uint32_t action,
-				    uint32_t filter, const char *path,
-				    uint8_t **pblob, size_t *pblob_len)
-{
-	struct notify_remote_event ev;
-	DATA_BLOB data;
-	enum ndr_err_code ndr_err;
-
-	ev.action = action;
-	ev.filter = filter;
-	ev.path = path;
-
-	if (DEBUGLEVEL >= 10) {
-		NDR_PRINT_DEBUG(notify_remote_event, &ev);
-	}
-
-	ndr_err = ndr_push_struct_blob(
-		&data, mem_ctx, &ev,
-		(ndr_push_flags_fn_t)ndr_push_notify_remote_event);
-	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-		return false;
-	}
-	*pblob = data.data;
-	*pblob_len = data.length;
-	return true;
-}
-
-static bool notify_pull_remote_blob(TALLOC_CTX *mem_ctx,
-				    const uint8_t *blob, size_t blob_len,
-				    uint32_t *paction, uint32_t *pfilter,
-				    char **path)
-{
-	struct notify_remote_event *ev;
-	enum ndr_err_code ndr_err;
-	DATA_BLOB data;
-	char *p;
-
-	data.data = discard_const_p(uint8_t, blob);
-	data.length = blob_len;
-
-	ev = talloc(mem_ctx, struct notify_remote_event);
-	if (ev == NULL) {
-		return false;
-	}
-
-	ndr_err = ndr_pull_struct_blob(
-		&data, ev, ev,
-		(ndr_pull_flags_fn_t)ndr_pull_notify_remote_event);
-	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-		TALLOC_FREE(ev);
-		return false;
-	}
-	if (DEBUGLEVEL >= 10) {
-		NDR_PRINT_DEBUG(notify_remote_event, ev);
-	}
-	*paction = ev->action;
-	*pfilter = ev->filter;
-	p = discard_const_p(char, ev->path);
-	*path = talloc_move(mem_ctx, &p);
-
-	TALLOC_FREE(ev);
-	return true;
-}
-
-void notify_trigger(struct notify_context *notify,
-		    uint32_t action, uint32_t filter,
-		    const char *dir, const char *name)
-{
-	struct ctdbd_connection *ctdbd_conn;
-	struct notify_trigger_index_state idx_state;
-	const char *p, *next_p;
-	size_t i, num_vnns;
-	uint32_t last_vnn;
-	uint8_t *remote_blob = NULL;
-	size_t remote_blob_len = 0;
-	char *path, *to_free;
-	char tmpbuf[PATH_MAX];
-	ssize_t len;
-
-	DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
-		   "dir=%s, name=%s\n", (unsigned)action, (unsigned)filter,
-		   dir, name));
-
-	/* see if change notify is enabled at all */
-	if (notify == NULL) {
-		return;
-	}
-
-	if (dir[0] != '/') {
-		/*
-		 * The rest of this routine assumes an absolute path.
-		 */
-		return;
-	}
-
-	len = full_path_tos(dir, name, tmpbuf, sizeof(tmpbuf),
-			    &path, &to_free);
-	if (len == -1) {
-		DEBUG(1, ("full_path_tos failed\n"));
-		return;
-	}
-
-	idx_state.mem_ctx = talloc_tos();
-	idx_state.vnns = NULL;
-	idx_state.found_my_vnn = false;
-	idx_state.my_vnn = get_my_vnn();
-
-	for (p = strchr(path+1, '/'); p != NULL; p = next_p) {
-		ptrdiff_t path_len = p - path;
-		bool recursive;
-
-		next_p = strchr(p+1, '/');
-		recursive = (next_p != NULL);
-
-		dbwrap_parse_record(
-			notify->db_index,
-			make_tdb_data(discard_const_p(uint8_t, path), path_len),
-			notify_trigger_index_parser, &idx_state);
-
-		if (idx_state.found_my_vnn) {
-			notify_trigger_local(notify, action, filter,
-					     path, path_len, recursive);
-			idx_state.found_my_vnn = false;
-		}
-	}
-
-	if (idx_state.vnns == NULL) {
-		goto done;
-	}
-
-	ctdbd_conn = messaging_ctdbd_connection();
-	if (ctdbd_conn == NULL) {
-		goto done;
-	}
-
-	num_vnns = talloc_array_length(idx_state.vnns);
-	qsort(idx_state.vnns, num_vnns, sizeof(uint32_t), vnn_cmp);
-
-	last_vnn = 0xffffffff;
-
-	if (!notify_push_remote_blob(talloc_tos(), action, filter, path,
-				     &remote_blob, &remote_blob_len)) {
-		DEBUG(1, ("notify_push_remote_blob failed\n"));
-		goto done;
-	}
-
-	for (i=0; i<num_vnns; i++) {
-		uint32_t vnn = idx_state.vnns[i];
-		NTSTATUS status;
-
-		if (vnn == last_vnn) {
-			continue;
-		}
-
-		status = ctdbd_messaging_send_blob(
-			ctdbd_conn, vnn, CTDB_SRVID_SAMBA_NOTIFY_PROXY,
-			remote_blob, remote_blob_len);
-		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(10, ("ctdbd_messaging_send_blob to vnn %d "
-				   "returned %s, ignoring\n", (int)vnn,
-				   nt_errstr(status)));
-		}
-
-		last_vnn = vnn;
-	}
-
-done:
-	TALLOC_FREE(remote_blob);
-	TALLOC_FREE(idx_state.vnns);
-	TALLOC_FREE(to_free);
-}
-
-static void notify_trigger_local(struct notify_context *notify,
-				 uint32_t action, uint32_t filter,
-				 const char *path, size_t path_len,
-				 bool recursive)
-{
-	TDB_DATA data;
-	struct notify_db_entry *entries;
-	size_t i, num_entries;
-	NTSTATUS status;
-
-	DEBUG(10, ("notify_trigger_local called for %*s, path_len=%d, "
-		   "filter=%d\n", (int)path_len, path, (int)path_len,
-		   (int)filter));
-
-	status = dbwrap_fetch(
-		notify->db_notify, talloc_tos(),
-		make_tdb_data(discard_const_p(uint8_t, path), path_len), &data);
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(10, ("dbwrap_fetch returned %s\n",
-			   nt_errstr(status)));
-		return;
-	}
-	if (data.dsize == sizeof(time_t)) {
-		DEBUG(10, ("Got deleted record\n"));
-		goto done;
-	}
-	if ((data.dsize % sizeof(struct notify_db_entry)) != 0) {
-		DEBUG(1, ("Invalid data.dsize = %u\n",
-			  (unsigned)data.dsize));
-		goto done;
-	}
-
-	entries = (struct notify_db_entry *)data.dptr;
-	num_entries = data.dsize / sizeof(struct notify_db_entry);
-
-	DEBUG(10, ("recursive = %s pathlen=%d (%c)\n",
-		   recursive ? "true" : "false", (int)path_len,
-		   path[path_len]));
-
-	for (i=0; i<num_entries; i++) {
-		struct notify_db_entry *e = &entries[i];
-		uint32_t e_filter;
-
-		if (DEBUGLEVEL >= 10) {
-			NDR_PRINT_DEBUG(notify_db_entry, e);
-		}
-
-		e_filter = recursive ? e->subdir_filter : e->filter;
-
-		if ((filter & e_filter) == 0) {
-			continue;
-		}
-
-		if (!procid_is_local(&e->server)) {
-			DEBUG(1, ("internal error: Non-local pid %s in "
-				  "notify.tdb\n",
-				  procid_str_static(&e->server)));
-			continue;
-		}
-
-		status = notify_send(notify, &e->server, path + path_len + 1,
-				     action, e->private_data);
-		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(10, ("notify_send returned %s\n",
-				   nt_errstr(status)));
-		}
-	}
-
-done:
-	TALLOC_FREE(data.dptr);
-}
-
-struct notify_msg {
-	struct timespec when;
-	void *private_data;
-	uint32_t action;
-	char path[1];
-};
-
-static NTSTATUS notify_send(struct notify_context *notify,
-			    struct server_id *pid,
-			    const char *path, uint32_t action,
-			    void *private_data)
-{
-	struct notify_msg m = {};
-	struct iovec iov[2];
-
-	m.when = timespec_current();
-	m.private_data = private_data;
-	m.action = action;
-
-	iov[0].iov_base = &m;
-	iov[0].iov_len = offsetof(struct notify_msg, path);
-	iov[1].iov_base = discard_const_p(char, path);
-	iov[1].iov_len = strlen(path)+1;
-
-	return messaging_send_iov(notify->msg, *pid, MSG_PVFS_NOTIFY,
-				  iov, ARRAY_SIZE(iov), NULL, 0);
-}
-
-static void notify_handler(struct messaging_context *msg_ctx,
-			   void *private_data, uint32_t msg_type,
-			   struct server_id server_id, DATA_BLOB *data)
-{
-	struct notify_context *notify = talloc_get_type_abort(
-		private_data, struct notify_context);
-	struct notify_msg *m;
-	struct notify_event e;
-	struct notify_list *listel;
-
-	if (data->length == 0) {
-		DEBUG(1, ("%s: Got 0-sized MSG_PVFS_NOTIFY msg\n", __func__));
-		return;
-	}
-	if (data->data[data->length-1] != 0) {
-		DEBUG(1, ("%s: MSG_PVFS_NOTIFY path not 0-terminated\n",
-			  __func__));
-		return;
-	}
-
-	m = (struct notify_msg *)data->data;
-
-	e = (struct notify_event) {
-		.action = m->action,
-		.path = m->path,
-		.private_data = m->private_data,
-		.dir = discard_const_p(char, "")
-	};
-
-	for (listel=notify->list;listel;listel=listel->next) {
-		if (listel->private_data == m->private_data) {
-			listel->callback(listel->private_data, m->when, &e);
-			break;
-		}
-	}
-}
-
-struct notify_walk_idx_state {
-	void (*fn)(const char *path,
-		   uint32_t *vnns, size_t num_vnns,
-		   void *private_data);
-	void *private_data;
-};
-
-static int notify_walk_idx_fn(struct db_record *rec, void *private_data)
-{
-	struct notify_walk_idx_state *state =
-		(struct notify_walk_idx_state *)private_data;
-	TDB_DATA key, value;
-	char *path;
-
-	key = dbwrap_record_get_key(rec);
-	value = dbwrap_record_get_value(rec);
-
-	if ((value.dsize % sizeof(uint32_t)) != 0) {
-		DEBUG(1, ("invalid value size in notify index db: %u\n",
-			  (unsigned)(value.dsize)));
-		return 0;
-	}
-
-	path = talloc_strndup(talloc_tos(), (char *)key.dptr, key.dsize);
-	if (path == NULL) {
-		DEBUG(1, ("talloc_strndup failed\n"));
-		return 0;
-	}
-	state->fn(path, (uint32_t *)value.dptr, value.dsize/sizeof(uint32_t),
-		  state->private_data);
-	TALLOC_FREE(path);
-	return 0;
-}
-
-void notify_walk_idx(struct notify_context *notify,
-		     void (*fn)(const char *path,
-				uint32_t *vnns, size_t num_vnns,
-				void *private_data),
-		     void *private_data)
-{
-	struct notify_walk_idx_state state;
-	state.fn = fn;
-	state.private_data = private_data;
-	dbwrap_traverse_read(notify->db_index, notify_walk_idx_fn, &state,
-			     NULL);
-}
-
-struct notify_walk_state {
-	void (*fn)(const char *path,
-		   struct notify_db_entry *entries, size_t num_entries,
-		   time_t deleted_time, void *private_data);
-	void *private_data;
-};
-
-static int notify_walk_fn(struct db_record *rec, void *private_data)
-{
-	struct notify_walk_state *state =
-		(struct notify_walk_state *)private_data;
-	TDB_DATA key, value;
-	struct notify_db_entry *entries;
-	size_t num_entries;
-	time_t deleted_time;
-	char *path;
-
-	key = dbwrap_record_get_key(rec);
-	value = dbwrap_record_get_value(rec);
-
-	if (value.dsize == sizeof(deleted_time)) {
-		memcpy(&deleted_time, value.dptr, sizeof(deleted_time));
-		entries = NULL;
-		num_entries = 0;
-	} else {
-		if ((value.dsize % sizeof(struct notify_db_entry)) != 0) {
-			DEBUG(1, ("invalid value size in notify db: %u\n",
-				  (unsigned)(value.dsize)));
-			return 0;
-		}
-		entries = (struct notify_db_entry *)value.dptr;
-		num_entries = value.dsize / sizeof(struct notify_db_entry);
-		deleted_time = 0;
-	}
-
-	path = talloc_strndup(talloc_tos(), (char *)key.dptr, key.dsize);
-	if (path == NULL) {
-		DEBUG(1, ("talloc_strndup failed\n"));
-		return 0;
-	}
-	state->fn(path, entries, num_entries, deleted_time,
-		  state->private_data);
-	TALLOC_FREE(path);
-	return 0;
-}
-
-void notify_walk(struct notify_context *notify,
-		 void (*fn)(const char *path,
-			    struct notify_db_entry *entries,
-			    size_t num_entries,
-			    time_t deleted_time, void *private_data),
-		 void *private_data)
-{
-	struct notify_walk_state state;
-	state.fn = fn;
-	state.private_data = private_data;
-	dbwrap_traverse_read(notify->db_notify, notify_walk_fn, &state,
-			     NULL);
-}
-
-struct notify_cleanup_state {
-	TALLOC_CTX *mem_ctx;
-	time_t delete_before;
-	ssize_t array_size;
-	uint32_t num_paths;
-	char **paths;
-};
-
-static void notify_cleanup_collect(
-	const char *path, struct notify_db_entry *entries, size_t num_entries,
-	time_t deleted_time, void *private_data)
-{
-	struct notify_cleanup_state *state =
-		(struct notify_cleanup_state *)private_data;
-	char *p;
-
-	if (num_entries != 0) {
-		return;
-	}
-	if (deleted_time >= state->delete_before) {
-		return;
-	}
-
-	p = talloc_strdup(state->mem_ctx, path);
-	if (p == NULL) {
-		DEBUG(1, ("talloc_strdup failed\n"));
-		return;
-	}
-	add_to_large_array(state->mem_ctx, sizeof(p), (void *)&p,
-			   &state->paths, &state->num_paths,
-			   &state->array_size);
-	if (state->array_size == -1) {
-		TALLOC_FREE(p);
-	}
-}
-
-static bool notify_cleanup_path(struct notify_context *notify,
-			      const char *path, time_t delete_before);
-
-void notify_cleanup(struct notify_context *notify)
-{
-	struct notify_cleanup_state state;
-	uint32_t failure_pool;
-
-	ZERO_STRUCT(state);
-	state.mem_ctx = talloc_stackframe();
-
-	state.delete_before = time(NULL)
-		- lp_parm_int(-1, "smbd", "notify cleanup interval", 60);
-
-	notify_walk(notify, notify_cleanup_collect, &state);
-
-	failure_pool = state.num_paths;
-
-	while (state.num_paths != 0) {
-		size_t idx;
-
-		/*
-		 * This loop is designed to be as kind as possible to
-		 * ctdb. ctdb does not like it if many smbds hammer on a
-		 * single record. If on many nodes the cleanup process starts
-		 * running, it can happen that all of them need to clean up
-		 * records in the same order. This would generate a ctdb
-		 * migrate storm on these records. Randomizing the load across
-		 * multiple records reduces the load on the individual record.
-		 */
-
-		generate_random_buffer((uint8_t *)&idx, sizeof(idx));
-		idx = idx % state.num_paths;
-
-		if (!notify_cleanup_path(notify, state.paths[idx],
-					 state.delete_before)) {
-			/*
-			 * notify_cleanup_path failed, the most likely reason
-			 * is that dbwrap_try_fetch_locked failed due to
-			 * contention. We allow one failed attempt per deleted
-			 * path on average before we give up.
-			 */
-			failure_pool -= 1;
-			if (failure_pool == 0) {
-				/*
-				 * Too many failures. We will come back here,
-				 * maybe next time there is less contention.
-				 */
-				break;
-			}
-		}
-
-		TALLOC_FREE(state.paths[idx]);
-		state.paths[idx] = state.paths[state.num_paths-1];
-		state.num_paths -= 1;
-	}
-	TALLOC_FREE(state.mem_ctx);
-}
-
-static bool notify_cleanup_path(struct notify_context *notify,
-				const char *path, time_t delete_before)
-{
-	struct db_record *notify_rec = NULL;
-	struct db_record *idx_rec = NULL;
-	TDB_DATA key = string_tdb_data(path);
-	TDB_DATA value;
-	time_t deleted;
-	NTSTATUS status;
-
-	notify_rec = dbwrap_fetch_locked(notify->db_notify, talloc_tos(), key);
-	if (notify_rec == NULL) {
-		DEBUG(10, ("Could not fetch notify_rec\n"));
-		return false;
-	}
-	value = dbwrap_record_get_value(notify_rec);
-
-	if (value.dsize != sizeof(deleted)) {
-		DEBUG(10, ("record %s has been re-used\n", path));
-		goto done;
-	}
-	memcpy(&deleted, value.dptr, sizeof(deleted));
-
-	if (deleted >= delete_before) {
-		DEBUG(10, ("record %s too young\n", path));
-		goto done;
-	}
-
-	/*
-	 * Be kind to ctdb and only try one dmaster migration at most.
-	 */
-	idx_rec = dbwrap_try_fetch_locked(notify->db_index, talloc_tos(), key);
-	if (idx_rec == NULL) {
-		DEBUG(10, ("Could not fetch idx_rec\n"));
-		goto done;
-	}
-
-	status = dbwrap_record_delete(notify_rec);
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(10, ("Could not delete notify_rec: %s\n",
-			   nt_errstr(status)));
-	}
-
-	status = notify_del_idx(idx_rec, get_my_vnn());
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(10, ("Could not delete idx_rec: %s\n",
-			   nt_errstr(status)));
-	}
-
-done:
-	TALLOC_FREE(idx_rec);
-	TALLOC_FREE(notify_rec);
-	return true;
-}
-
-static NTSTATUS notify_del_idx(struct db_record *rec, uint32_t vnn)
-{
-	TDB_DATA value = dbwrap_record_get_value(rec);
-	uint32_t *vnns;
-	size_t i, num_vnns;
-
-	if ((value.dsize % sizeof(uint32_t)) != 0) {
-		DEBUG(1, ("Invalid value.dsize = %u\n",
-			  (unsigned)value.dsize));
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	num_vnns = value.dsize / sizeof(uint32_t);
-	vnns = (uint32_t *)value.dptr;
-
-	for (i=0; i<num_vnns; i++) {
-		if (vnns[i] == vnn) {
-			break;
-		}
-	}
-
-	if (i == num_vnns) {
-		/*
-		 * Not found. Should not happen, but okay...
-		 */
-		return NT_STATUS_OK;
-	}
-
-	memmove(&vnns[i], &vnns[i+1], sizeof(uint32_t) * (num_vnns - i - 1));
-	value.dsize -= sizeof(uint32_t);
-
-	if (value.dsize == 0) {
-		return dbwrap_record_delete(rec);
-	}
-	return dbwrap_record_store(rec, value, 0);
-}
-
-struct notify_cluster_proxy_state {
-	struct tevent_context *ev;
-	struct notify_context *notify;
-	struct ctdb_msg_channel *chan;
-};
-
-static void notify_cluster_proxy_got_chan(struct tevent_req *subreq);
-static void notify_cluster_proxy_got_msg(struct tevent_req *subreq);
-static void notify_cluster_proxy_trigger(struct notify_context *notify,
-					 uint32_t action, uint32_t filter,
-					 char *path);
-
-struct tevent_req *notify_cluster_proxy_send(
-	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-	struct notify_context *notify)
-{
-	struct tevent_req *req, *subreq;
-	struct notify_cluster_proxy_state *state;
-
-	req = tevent_req_create(mem_ctx, &state,
-				struct notify_cluster_proxy_state);
-	if (req == NULL) {
-		return NULL;
-	}
-	state->ev = ev;
-	state->notify = notify;
-
-	subreq = ctdb_msg_channel_init_send(
-		state, state->ev,  lp_ctdbd_socket(),
-		CTDB_SRVID_SAMBA_NOTIFY_PROXY);
-	if (tevent_req_nomem(subreq, req)) {
-		return tevent_req_post(req, ev);
-	}
-	tevent_req_set_callback(subreq, notify_cluster_proxy_got_chan, req);
-	return req;
-}
-
-static void notify_cluster_proxy_got_chan(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct notify_cluster_proxy_state *state = tevent_req_data(
-		req, struct notify_cluster_proxy_state);
-	int ret;
-
-	ret = ctdb_msg_channel_init_recv(subreq, state, &state->chan);
-	TALLOC_FREE(subreq);
-	if (ret != 0) {
-		tevent_req_error(req, ret);
-		return;
-	}
-	subreq = ctdb_msg_read_send(state, state->ev, state->chan);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, notify_cluster_proxy_got_msg, req);
-}
-
-static void notify_cluster_proxy_got_msg(struct tevent_req *subreq)
-{
-	struct tevent_req *req = tevent_req_callback_data(
-		subreq, struct tevent_req);
-	struct notify_cluster_proxy_state *state = tevent_req_data(
-		req, struct notify_cluster_proxy_state);
-	uint8_t *msg;
-	size_t msg_len;
-	uint32_t action, filter;
-	char *path;
-	int ret;
-	bool res;
-
-	ret = ctdb_msg_read_recv(subreq, talloc_tos(), &msg, &msg_len);
-	TALLOC_FREE(subreq);
-	if (ret != 0) {
-		tevent_req_error(req, ret);
-		return;
-	}
-
-	res = notify_pull_remote_blob(talloc_tos(), msg, msg_len,
-				      &action, &filter, &path);
-	TALLOC_FREE(msg);
-	if (!res) {
-		tevent_req_error(req, EIO);
-		return;
-	}
-	notify_cluster_proxy_trigger(state->notify, action, filter, path);
-	TALLOC_FREE(path);
-
-	subreq = ctdb_msg_read_send(state, state->ev, state->chan);
-	if (tevent_req_nomem(subreq, req)) {
-		return;
-	}
-	tevent_req_set_callback(subreq, notify_cluster_proxy_got_msg, req);
-}
-
-static void notify_cluster_proxy_trigger(struct notify_context *notify,
-					 uint32_t action, uint32_t filter,
-					 char *path)
-{
-	const char *p, *next_p;
-
-	for (p = path; p != NULL; p = next_p) {
-		ptrdiff_t path_len = p - path;
-		bool recursive;
-
-		next_p = strchr(p+1, '/');
-		recursive = (next_p != NULL);
-
-		notify_trigger_local(notify, action, filter,
-				     path, path_len, recursive);
-	}
-}
-
-int notify_cluster_proxy_recv(struct tevent_req *req)
-{
-	return tevent_req_simple_recv_unix(req);
-}
diff --git a/source3/smbd/notify_msg.c b/source3/smbd/notify_msg.c
new file mode 100644
index 0000000..b31cb57
--- /dev/null
+++ b/source3/smbd/notify_msg.c
@@ -0,0 +1,268 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 "includes.h"
+#include "librpc/gen_ndr/notify.h"
+#include "librpc/gen_ndr/messaging.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "messages.h"
+#include "proto.h"
+#include "globals.h"
+#include "tdb.h"
+#include "util_tdb.h"
+#include "lib/util/server_id_db.h"
+#include "smbd/notifyd/notifyd.h"
+
+struct notify_list {
+	struct notify_list *next, *prev;
+	void (*callback)(void *private_data, struct timespec when,
+			 const struct notify_event *ctx);
+	void *private_data;
+	char path[1];
+};
+
+struct notify_context {
+	struct server_id notifyd;
+	struct messaging_context *msg_ctx;
+	struct notify_list *list;
+};
+
+static void notify_handler(struct messaging_context *msg, void *private_data,
+			   uint32_t msg_type, struct server_id src,
+			   DATA_BLOB *data);
+
+struct notify_context *notify_init(TALLOC_CTX *mem_ctx,
+				   struct messaging_context *msg,
+				   struct tevent_context *ev)
+{
+	struct server_id_db *names_db;
+	struct notify_context *ctx;
+	NTSTATUS status;
+
+	ctx = talloc(mem_ctx, struct notify_context);
+	if (ctx == NULL) {
+		return NULL;
+	}
+	ctx->msg_ctx = msg;
+	ctx->list = NULL;
+
+	names_db = messaging_names_db(msg);
+	if (!server_id_db_lookup_one(names_db, "notify-daemon",
+				     &ctx->notifyd)) {
+		DEBUG(1, ("No notify daemon around\n"));
+		TALLOC_FREE(ctx);
+		return NULL;
+	}
+
+	status = messaging_register(msg, ctx, MSG_PVFS_NOTIFY, notify_handler);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("messaging_register failed: %s\n",
+			  nt_errstr(status)));
+		TALLOC_FREE(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+static void notify_handler(struct messaging_context *msg, void *private_data,
+			   uint32_t msg_type, struct server_id src,
+			   DATA_BLOB *data)
+{
+	struct notify_context *ctx = talloc_get_type_abort(
+		private_data, struct notify_context);
+	struct notify_event_msg *event_msg;
+	struct notify_event event;
+	struct notify_list *listel;
+
+	if (data->length < offsetof(struct notify_event_msg, path) + 1) {
+		DEBUG(1, ("message too short: %u\n", (unsigned)data->length));
+		return;
+	}
+	if (data->data[data->length-1] != 0) {
+		DEBUG(1, ("%s: path not 0-terminated\n", __func__));
+		return;
+	}
+
+	event_msg = (struct notify_event_msg *)data->data;
+
+	event.action = event_msg->action;
+	event.path = event_msg->path;
+	event.private_data = event_msg->private_data;
+
+	DEBUG(10, ("%s: Got notify_event action=%u, private_data=%p, "
+		   "path=%s\n", __func__, (unsigned)event.action,
+		   event.private_data, event.path));
+
+	for (listel = ctx->list; listel != NULL; listel = listel->next) {
+		if (listel->private_data == event.private_data) {
+			listel->callback(listel->private_data, event_msg->when,
+					 &event);
+			break;
+		}
+	}
+}
+
+NTSTATUS notify_add(struct notify_context *ctx,
+		    const char *path, uint32_t filter, uint32_t subdir_filter,
+		    void (*callback)(void *, struct timespec,
+				     const struct notify_event *),
+		    void *private_data)
+{
+	struct notify_list *listel;
+	struct notify_rec_change_msg msg = {};
+	struct iovec iov[2];
+	size_t pathlen;
+	NTSTATUS status;
+
+	if (ctx == NULL) {
+		return NT_STATUS_NOT_IMPLEMENTED;
+	}
+
+	DEBUG(10, ("%s: path=[%s], filter=%u, subdir_filter=%u, "
+		   "private_data=%p\n", __func__, path, (unsigned)filter,
+		   (unsigned)subdir_filter, private_data));
+
+	pathlen = strlen(path)+1;
+
+	listel = (struct notify_list *)talloc_size(
+		ctx, offsetof(struct notify_list, path) + pathlen);
+	if (listel == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	listel->callback = callback;
+	listel->private_data = private_data;
+	memcpy(listel->path, path, pathlen);
+
+	clock_gettime_mono(&msg.instance.creation_time);
+	msg.instance.filter = filter;
+	msg.instance.subdir_filter = subdir_filter;
+	msg.instance.private_data = private_data;
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
+	iov[1].iov_base = discard_const_p(char, path);
+	iov[1].iov_len = pathlen;
+
+	status =  messaging_send_iov(
+		ctx->msg_ctx, ctx->notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+		iov, ARRAY_SIZE(iov), NULL, 0);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(listel);
+		DEBUG(10, ("messaging_send_iov returned %s\n",
+			   nt_errstr(status)));
+		return status;
+	}
+
+	DLIST_ADD(ctx->list, listel);
+	return NT_STATUS_OK;
+}
+
+NTSTATUS notify_remove(struct notify_context *ctx, void *private_data)
+{
+	struct notify_list *listel;
+	struct notify_rec_change_msg msg = {};
+	struct iovec iov[2];
+	NTSTATUS status;
+
+	for (listel = ctx->list; listel != NULL; listel = listel->next) {
+		if (listel->private_data == private_data) {
+			DLIST_REMOVE(ctx->list, listel);
+			break;
+		}
+	}
+	if (listel == NULL) {
+		DEBUG(10, ("%p not found\n", private_data));
+		return NT_STATUS_NOT_FOUND;
+	}
+
+	msg.instance.private_data = private_data;
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_rec_change_msg, path);
+	iov[1].iov_base = discard_const_p(char, listel->path);
+	iov[1].iov_len = strlen(listel->path)+1;
+
+	status = messaging_send_iov(
+		ctx->msg_ctx, ctx->notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+		iov, ARRAY_SIZE(iov), NULL, 0);
+
+	TALLOC_FREE(listel);
+	return status;
+}
+
+void notify_trigger(struct notify_context *ctx,
+		    uint32_t action, uint32_t filter,
+		    const char *dir, const char *name)
+{
+	struct notify_trigger_msg msg;
+	struct iovec iov[4];
+	char slash = '/';
+
+	DEBUG(10, ("notify_trigger called action=0x%x, filter=0x%x, "
+		   "dir=%s, name=%s\n", (unsigned)action, (unsigned)filter,
+		   dir, name));
+
+	if (ctx == NULL) {
+		return;
+	}
+
+	msg.when = timespec_current();
+	msg.action = action;
+	msg.filter = filter;
+
+	iov[0].iov_base = &msg;
+	iov[0].iov_len = offsetof(struct notify_trigger_msg, path);
+	iov[1].iov_base = discard_const_p(char, dir);
+	iov[1].iov_len = strlen(dir);
+	iov[2].iov_base = &slash;
+	iov[2].iov_len = 1;
+	iov[3].iov_base = discard_const_p(char, name);
+	iov[3].iov_len = strlen(name)+1;
+
+	messaging_send_iov(
+		ctx->msg_ctx, ctx->notifyd, MSG_SMB_NOTIFY_TRIGGER,
+		iov, ARRAY_SIZE(iov), NULL, 0);
+}
+
+void notify_walk_idx(struct notify_context *notify,
+		     void (*fn)(const char *path,
+				uint32_t *vnns, size_t num_vnns,
+				void *private_data),
+		     void *private_data)
+{
+	return;
+}
+
+void notify_walk(struct notify_context *notify,
+		 void (*fn)(const char *path,
+			    struct notify_db_entry *entries,
+			    size_t num_entries,
+			    time_t deleted_time, void *private_data),
+		 void *private_data)
+{
+	return;
+}
+
+void notify_cleanup(struct notify_context *notify)
+{
+	return;
+}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 16e87f8..268c3d2 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -581,11 +581,6 @@ void notify_walk(struct notify_context *notify,
 		 void *private_data);
 void notify_cleanup(struct notify_context *notify);
 
-struct tevent_req *notify_cluster_proxy_send(
-	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
-	struct notify_context *notify);
-int notify_cluster_proxy_recv(struct tevent_req *req);
-
 /* The following definitions come from smbd/ntquotas.c  */
 
 int vfs_get_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, struct dom_sid *psid, SMB_NTQUOTA_STRUCT *qt);
diff --git a/source3/wscript_build b/source3/wscript_build
index 101267b..ab10159 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -598,7 +598,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    smbd/oplock_irix.c
                    smbd/oplock_linux.c
                    smbd/notify.c
-                   smbd/notify_internal.c
+                   smbd/notify_msg.c
                    smbd/build_options.c''' + NOTIFY_SOURCES,
                    deps='''
                    talloc
@@ -1187,7 +1187,7 @@ bld.SAMBA3_BINARY('smbta-util',
                  secrets3
                  param''')
 
-smbstatus_source = 'utils/status.c smbd/notify_internal.c'
+smbstatus_source = 'utils/status.c smbd/notify_msg.c'
 
 if bld.CONFIG_GET("WITH_PROFILE"):
     smbstatus_source += ' utils/status_profile.c'
-- 
1.9.1


From b9cef68623e9be3b6b5e65bb312b1da5e2939c84 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 17:23:18 +0100
Subject: [PATCH 10/15] smbd: Kernel change notify is done by notifyd

smbd itself does not need to call VFS_NOTIFY_WATCH anymore
---
 source3/smbd/notify.c | 22 ----------------------
 1 file changed, 22 deletions(-)

diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index 5ac8c0c..b916ff3 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -235,15 +235,6 @@ static void notify_callback(void *private_data, struct timespec when,
 	notify_fsp(fsp, when, e->action, e->path);
 }
 
-static void sys_notify_callback(struct sys_notify_context *ctx,
-				void *private_data,
-				struct notify_event *e)
-{
-	files_struct *fsp = (files_struct *)private_data;
-	DEBUG(10, ("sys_notify_callback called for %s\n", fsp_str_dbg(fsp)));
-	notify_fsp(fsp, timespec_current(), e->action, e->path);
-}
-
 NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter,
 			      bool recursive)
 {
@@ -283,19 +274,6 @@ NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter,
 
 	subdir_filter = recursive ? filter : 0;
 
-	if (fsp->conn->sconn->sys_notify_ctx != NULL) {
-		void *sys_notify_handle = NULL;
-
-		status = SMB_VFS_NOTIFY_WATCH(
-			fsp->conn, fsp->conn->sconn->sys_notify_ctx,
-			fullpath, &filter, &subdir_filter,
-			sys_notify_callback, fsp, &sys_notify_handle);
-
-		if (NT_STATUS_IS_OK(status)) {
-			talloc_steal(fsp->notify, sys_notify_handle);
-		}
-	}
-
 	if ((filter != 0) || (subdir_filter != 0)) {
 		status = notify_add(fsp->conn->sconn->notify_ctx,
 				    fullpath, filter, subdir_filter,
-- 
1.9.1


From e04787a87c25afec612aa17fd8dee9a9c3d88f6c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 17:28:02 +0100
Subject: [PATCH 11/15] smbd: Remove the notify_fam module

This has been moved to main smbd
---
 docs-xml/Samba3-HOWTO/manpages.xml     |   1 -
 docs-xml/manpages/vfs_notify_fam.8.xml |  70 --------
 docs-xml/wscript_build                 |   1 -
 source3/modules/vfs_notify_fam.c       | 316 ---------------------------------
 source3/modules/wscript_build          |   7 -
 5 files changed, 395 deletions(-)
 delete mode 100644 docs-xml/manpages/vfs_notify_fam.8.xml
 delete mode 100644 source3/modules/vfs_notify_fam.c

diff --git a/docs-xml/Samba3-HOWTO/manpages.xml b/docs-xml/Samba3-HOWTO/manpages.xml
index 577ac8b..ad23f49 100644
--- a/docs-xml/Samba3-HOWTO/manpages.xml
+++ b/docs-xml/Samba3-HOWTO/manpages.xml
@@ -58,7 +58,6 @@
 	<xi:include href="../manpages/vfs_full_audit.8.xml"/>
 	<xi:include href="../manpages/vfs_gpfs.8.xml"/>
 	<xi:include href="../manpages/vfs_netatalk.8.xml"/>
-	<xi:include href="../manpages/vfs_notify_fam.8.xml"/>
 	<xi:include href="../manpages/vfs_prealloc.8.xml"/>
 	<xi:include href="../manpages/vfs_readahead.8.xml"/>
 	<xi:include href="../manpages/vfs_readonly.8.xml"/>
diff --git a/docs-xml/manpages/vfs_notify_fam.8.xml b/docs-xml/manpages/vfs_notify_fam.8.xml
deleted file mode 100644
index 954aa37..0000000
--- a/docs-xml/manpages/vfs_notify_fam.8.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
-<refentry id="vfs_notify_fam.8">
-
-<refmeta>
-	<refentrytitle>vfs_notify_fam</refentrytitle>
-	<manvolnum>8</manvolnum>
-	<refmiscinfo class="source">Samba</refmiscinfo>
-	<refmiscinfo class="manual">System Administration tools</refmiscinfo>
-	<refmiscinfo class="version">4.2</refmiscinfo>
-</refmeta>
-
-
-<refnamediv>
-	<refname>vfs_notify_fam</refname>
-	<refpurpose>FAM support for file change notifications</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-	<cmdsynopsis>
-		<command>vfs objects = notify_fam</command>
-	</cmdsynopsis>
-</refsynopsisdiv>
-
-<refsect1>
-	<title>DESCRIPTION</title>
-
-	<para>This VFS module is part of the
-	<citerefentry><refentrytitle>samba</refentrytitle>
-	<manvolnum>7</manvolnum></citerefentry> suite.</para>
-
-	<para>The <command>vfs_notify_fam</command> module makes use of
-	the system FAM (File Alteration Monitor) daemon to implement
-	file change notifications for Windows clients. FAM is generally
-	present only on IRIX and some BSD systems.</para>
-
-	<para>This module is not stackable.</para>
-
-</refsect1>
-
-<refsect1>
-	<title>EXAMPLES</title>
-
-	<para>Support FAM notifications globally:</para>
-
-<programlisting>
-        <smbconfsection name="[global]"/>
-	<smbconfoption name="vfs objects">notify_fam</smbconfoption>
-</programlisting>
-
-</refsect1>
-
-<refsect1>
-	<title>VERSION</title>
-
-	<para>This man page is correct for version 3.0.25 of the Samba suite.
-	</para>
-</refsect1>
-
-<refsect1>
-	<title>AUTHOR</title>
-
-	<para>The original Samba software and related utilities
-	were created by Andrew Tridgell. Samba is now developed
-	by the Samba Team as an Open Source project similar
-	to the way the Linux kernel is developed.</para>
-
-</refsect1>
-
-</refentry>
diff --git a/docs-xml/wscript_build b/docs-xml/wscript_build
index 0bc3f54..3db391b 100644
--- a/docs-xml/wscript_build
+++ b/docs-xml/wscript_build
@@ -67,7 +67,6 @@ manpages='''
          manpages/vfs_linux_xfs_sgid.8
          manpages/vfs_media_harmony.8
          manpages/vfs_netatalk.8
-         manpages/vfs_notify_fam.8
          manpages/vfs_prealloc.8
          manpages/vfs_preopen.8
          manpages/vfs_readahead.8
diff --git a/source3/modules/vfs_notify_fam.c b/source3/modules/vfs_notify_fam.c
deleted file mode 100644
index 54df0e4..0000000
--- a/source3/modules/vfs_notify_fam.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * FAM file notification support.
- *
- * Copyright (c) James Peach 2005
- * Copyright (c) Volker Lendecke 2007
- *
- * 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 "includes.h"
-#include "smbd/smbd.h"
-#include "librpc/gen_ndr/notify.h"
-
-#include <fam.h>
-
-#if !defined(HAVE_FAM_H_FAMCODES_TYPEDEF)
-/* Gamin provides this typedef which means we can't use 'enum FAMCodes' as per
- * every other FAM implementation. Phooey.
- */
-typedef enum FAMCodes FAMCodes;
-#endif
-
-/* NOTE: There are multiple versions of FAM floating around the net, each with
- * slight differences from the original SGI FAM implementation. In this file,
- * we rely only on the SGI features and do not assume any extensions. For
- * example, we do not look at FAMErrno, because it is not set by the original
- * implementation.
- *
- * Random FAM links:
- *	http://oss.sgi.com/projects/fam/
- *	http://savannah.nongnu.org/projects/fam/
- *	http://sourceforge.net/projects/bsdfam/
- */
-
-/* ------------------------------------------------------------------------- */
-
-struct fam_watch_context {
-	struct fam_watch_context *prev, *next;
-	FAMConnection *fam_connection;
-	struct FAMRequest fr;
-	struct sys_notify_context *sys_ctx;
-	void (*callback)(struct sys_notify_context *ctx, 
-			 void *private_data,
-			 struct notify_event *ev);
-	void *private_data;
-	uint32_t mask; /* the inotify mask */
-	uint32_t filter; /* the windows completion filter */
-	const char *path;
-};
-
-
-/*
- * We want one FAM connection per smbd, not one per tcon.
- */
-static FAMConnection fam_connection;
-static bool fam_connection_initialized = False;
-
-static struct fam_watch_context *fam_notify_list;
-static void fam_handler(struct tevent_context *event_ctx,
-			struct tevent_fd *fd_event,
-			uint16 flags,
-			void *private_data);
-
-static NTSTATUS fam_open_connection(FAMConnection *fam_conn,
-				    struct tevent_context *event_ctx)
-{
-	int res;
-	char *name;
-
-	ZERO_STRUCTP(fam_conn);
-	FAMCONNECTION_GETFD(fam_conn) = -1;
-
-
-#ifdef HAVE_FAMNOEXISTS
-	/* We should honor outside setting of the GAM_CLIENT_ID. */
-	setenv("GAM_CLIENT_ID","SAMBA",0);
-#endif
-
-	if (asprintf(&name, "smbd (%lu)", (unsigned long)getpid()) == -1) {
-		DEBUG(0, ("No memory\n"));
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	res = FAMOpen2(fam_conn, name);
-
-#ifdef HAVE_FAMNOEXISTS
-	/*
-	 * This reduces the chatter between GAMIN and samba making the pair
-	 * much more reliable.
-	 */
-	FAMNoExists(fam_conn);
-#endif
-
-	SAFE_FREE(name);
-
-	if (res < 0) {
-		DEBUG(10, ("FAM file change notifications not available\n"));
-		/*
-		 * No idea how to get NT_STATUS from a FAM result
-		 */
-		FAMCONNECTION_GETFD(fam_conn) = -1;
-		return NT_STATUS_UNEXPECTED_IO_ERROR;
-	}
-
-	if (tevent_add_fd(event_ctx, event_ctx,
-			 FAMCONNECTION_GETFD(fam_conn),
-			 TEVENT_FD_READ, fam_handler,
-			 (void *)fam_conn) == NULL) {
-		DEBUG(0, ("event_add_fd failed\n"));
-		FAMClose(fam_conn);
-		FAMCONNECTION_GETFD(fam_conn) = -1;
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	return NT_STATUS_OK;
-}
-
-static void fam_reopen(FAMConnection *fam_conn,
-		       struct tevent_context *event_ctx,
-		       struct fam_watch_context *notify_list)
-{
-	struct fam_watch_context *ctx;
-
-	DEBUG(5, ("Re-opening FAM connection\n"));
-
-	FAMClose(fam_conn);
-
-	if (!NT_STATUS_IS_OK(fam_open_connection(fam_conn, event_ctx))) {
-		DEBUG(5, ("Re-opening fam connection failed\n"));
-		return;
-	}
-
-	for (ctx = notify_list; ctx; ctx = ctx->next) {
-		FAMMonitorDirectory(fam_conn, ctx->path, &ctx->fr, NULL);
-	}
-}
-
-static void fam_handler(struct tevent_context *event_ctx,
-			struct tevent_fd *fd_event,
-			uint16 flags,
-			void *private_data)
-{
-	FAMConnection *fam_conn = (FAMConnection *)private_data;
-	FAMEvent fam_event;
-	struct fam_watch_context *ctx;
-	struct notify_event ne;
-
-	if (FAMPending(fam_conn) == 0) {
-		DEBUG(10, ("fam_handler called but nothing pending\n"));
-		return;
-	}
-
-	if (FAMNextEvent(fam_conn, &fam_event) != 1) {
-		DEBUG(5, ("FAMNextEvent returned an error\n"));
-		TALLOC_FREE(fd_event);
-		fam_reopen(fam_conn, event_ctx, fam_notify_list);
-		return;
-	}
-
-	DEBUG(10, ("Got FAMCode %d for %s\n", fam_event.code,
-		   fam_event.filename));
-
-	switch (fam_event.code) {
-	case FAMChanged:
-		ne.action = NOTIFY_ACTION_MODIFIED;
-		break;
-	case FAMCreated:
-		ne.action = NOTIFY_ACTION_ADDED;
-		break;
-	case FAMDeleted:
-		ne.action = NOTIFY_ACTION_REMOVED;
-		break;
-	default:
-		DEBUG(10, ("Ignoring code FAMCode %d for file %s\n",
-			   (int)fam_event.code, fam_event.filename));
-		return;
-	}
-
-	for (ctx = fam_notify_list; ctx; ctx = ctx->next) {
-		if (memcmp(&fam_event.fr, &ctx->fr, sizeof(FAMRequest)) == 0) {
-			break;
-		}
-	}
-
-	if (ctx == NULL) {
-		DEBUG(5, ("Discarding event for file %s\n",
-			  fam_event.filename));
-		return;
-	}
-
-	if ((ne.path = strrchr_m(fam_event.filename, '\\')) == NULL) {
-		ne.path = fam_event.filename;
-	}
-
-	ctx->callback(ctx->sys_ctx, ctx->private_data, &ne);
-}
-
-static int fam_watch_context_destructor(struct fam_watch_context *ctx)
-{
-	if (FAMCONNECTION_GETFD(ctx->fam_connection) != -1) {
-		FAMCancelMonitor(&fam_connection, &ctx->fr);
-	}
-	DLIST_REMOVE(fam_notify_list, ctx);
-	return 0;
-}
-
-/*
-  add a watch. The watch is removed when the caller calls
-  talloc_free() on *handle
-*/
-static NTSTATUS fam_watch(vfs_handle_struct *vfs_handle,
-			  struct sys_notify_context *ctx,
-			  const char *path,
-			  uint32_t *filter,
-			  uint32_t *subdir_filter,
-			  void (*callback)(struct sys_notify_context *ctx, 
-					   void *private_data,
-					   struct notify_event *ev),
-			  void *private_data, 
-			  void *handle_p)
-{
-	const uint32 fam_mask = (FILE_NOTIFY_CHANGE_FILE_NAME|
-				 FILE_NOTIFY_CHANGE_DIR_NAME);
-	struct fam_watch_context *watch;
-	void **handle = (void **)handle_p;
-
-	if ((*filter & fam_mask) == 0) {
-		DEBUG(10, ("filter = %u, ignoring in FAM\n", *filter));
-		return NT_STATUS_OK;
-	}
-
-	if (!fam_connection_initialized) {
-		if (!NT_STATUS_IS_OK(fam_open_connection(&fam_connection,
-							 ctx->ev))) {
-			/*
-			 * Just let smbd do all the work itself
-			 */
-			return NT_STATUS_OK;
-		}
-		fam_connection_initialized = True;
-	}
-
-	if (!(watch = talloc(ctx, struct fam_watch_context))) {
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	watch->fam_connection = &fam_connection;
-
-	watch->callback = callback;
-	watch->private_data = private_data;
-	watch->sys_ctx = ctx;
-
-	watch->path = talloc_strdup(watch, path);
-	if (watch->path == NULL) {
-		DEBUG(0, ("talloc_asprintf failed\n"));
-		TALLOC_FREE(watch);
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	/*
-	 * The FAM module in this early state will only take care of
-	 * FAMCreated and FAMDeleted events, Leave the rest to
-	 * notify_internal.c
-	 */
-
-	watch->filter = fam_mask;
-	*filter &= ~fam_mask;
-
-	DLIST_ADD(fam_notify_list, watch);
-	talloc_set_destructor(watch, fam_watch_context_destructor);
-
-	/*
-	 * Only directories monitored so far
-	 */
-
-	if (FAMCONNECTION_GETFD(watch->fam_connection) != -1) {
-		FAMMonitorDirectory(watch->fam_connection, watch->path,
-				    &watch->fr, NULL);
-	}
-	else {
-		/*
-		 * If the re-open is successful, this will establish the
-		 * FAMMonitor from the list
-		 */
-		fam_reopen(watch->fam_connection, ctx->ev, fam_notify_list);
-	}
-
-	*handle = (void *)watch;
-
-	return NT_STATUS_OK;
-}
-
-/* VFS operations structure */
-
-static struct vfs_fn_pointers notify_fam_fns = {
-	.notify_watch_fn = fam_watch,
-};
-
-
-NTSTATUS vfs_notify_fam_init(void);
-NTSTATUS vfs_notify_fam_init(void)
-{
-	return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "notify_fam",
-				&notify_fam_fns);
-}
diff --git a/source3/modules/wscript_build b/source3/modules/wscript_build
index de4947b..48b4a87 100644
--- a/source3/modules/wscript_build
+++ b/source3/modules/wscript_build
@@ -284,13 +284,6 @@ bld.SAMBA3_MODULE('vfs_gpfs',
 vfs_notify_fam_deps='samba-util '
 if bld.CONFIG_SET('SAMBA_FAM_LIBS'):
    vfs_notify_fam_deps += bld.CONFIG_GET('SAMBA_FAM_LIBS')
-bld.SAMBA3_MODULE('vfs_notify_fam',
-                 subsystem='vfs',
-                 source='vfs_notify_fam.c',
-                 deps=vfs_notify_fam_deps,
-                 init_function='',
-                 internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_notify_fam'),
-                 enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_notify_fam'))
 
 bld.SAMBA3_MODULE('vfs_readahead',
                  subsystem='vfs',
-- 
1.9.1


From c0fb1667d8100dd9be4e205dbc26fc249577b034 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 9 Jan 2015 12:24:58 +0000
Subject: [PATCH 12/15] notifyd: Add notifyd_parse_db()

The database format notifyd is "private" to it. This makes it possible
for smbcontrol and others to inspect the notifyd's database without
having to know exactly what format it uses.
---
 source3/smbd/notifyd/notifyd.c | 71 ++++++++++++++++++++++++++++++++++++++++++
 source3/smbd/notifyd/notifyd.h | 13 ++++++++
 2 files changed, 84 insertions(+)

diff --git a/source3/smbd/notifyd/notifyd.c b/source3/smbd/notifyd/notifyd.c
index 8c4a4ae..4817ab1 100644
--- a/source3/smbd/notifyd/notifyd.c
+++ b/source3/smbd/notifyd/notifyd.c
@@ -1491,3 +1491,74 @@ static void notifyd_peer_recv_finished(struct tevent_req *subreq)
 	DEBUG(10, ("%s: notifyd_peer_recv for peer %u returned %s\n", __func__,
 		   (unsigned)peer->vnn, strerror(ret)));
 }
+
+struct notifyd_parse_db_state {
+	bool (*fn)(const char *path,
+		   struct server_id server,
+		   const struct notify_instance *instance,
+		   void *private_data);
+	void *private_data;
+};
+
+static bool notifyd_parse_db_parser(TDB_DATA key, TDB_DATA value,
+				    void *private_data)
+{
+	struct notifyd_parse_db_state *state = private_data;
+	char path[key.dsize+1];
+	struct notifyd_entry *entry = NULL;
+	size_t i, num_instances;
+	bool ok;
+
+	memcpy(path, key.dptr, key.dsize);
+	path[key.dsize] = 0;
+
+	ok = notifyd_parse_entry(value.dptr, value.dsize, &entry,
+				 &num_instances);
+	if (!ok) {
+		DEBUG(10, ("%s: Could not parse entry for path %s\n",
+			   __func__, path));
+		return true;
+	}
+
+	for (i=0; i<num_instances; i++) {
+		ok = state->fn(path, entry->instances[i].client,
+			       &entry->instances[i].instance,
+			       state->private_data);
+		if (!ok) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+int notifyd_parse_db(const uint8_t *buf, size_t buflen,
+		     uint64_t *log_index,
+		     bool (*fn)(const char *path,
+				struct server_id server,
+				const struct notify_instance *instance,
+				void *private_data),
+		     void *private_data)
+{
+	struct notifyd_parse_db_state state = {
+		.fn = fn, .private_data = private_data
+	};
+	NTSTATUS status;
+
+	if (buflen < 8) {
+		return EINVAL;
+	}
+	*log_index = BVAL(buf, 0);
+
+	buf += 8;
+	buflen -= 8;
+
+	status = dbwrap_parse_marshall_buf(
+		buf, buflen, notifyd_parse_db_parser, &state);
+	if (!NT_STATUS_IS_OK(status)) {
+		return map_errno_from_nt_status(status);
+	}
+
+	return 0;
+}
+
diff --git a/source3/smbd/notifyd/notifyd.h b/source3/smbd/notifyd/notifyd.h
index 084ddf9..707a835 100644
--- a/source3/smbd/notifyd/notifyd.h
+++ b/source3/smbd/notifyd/notifyd.h
@@ -139,4 +139,17 @@ struct tevent_req *notifyd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
 				struct sys_notify_context *sys_notify_ctx);
 int notifyd_recv(struct tevent_req *req);
 
+/*
+ * Parse a database received via the MSG_SMB_NOTIFY_[GET_]DB messages to the
+ * notify daemon
+ */
+int notifyd_parse_db(const uint8_t *buf, size_t buflen,
+		     uint64_t *log_index,
+		     bool (*fn)(const char *path,
+				struct server_id server,
+				const struct notify_instance *instance,
+				void *private_data),
+		     void *private_data);
+
+
 #endif
-- 
1.9.1


From 9712bd2ff529b17ee1c371b466f9df2324e831e4 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 9 Jan 2015 12:48:56 +0000
Subject: [PATCH 13/15] notify: Re-add notify_walk()

This used to be a tdb traverse wrapper. Now we get the notify db from
notifyd via messages.
---
 source3/smbd/notify_msg.c | 72 ++++++++++++++++++++++++++++++++++++++++++-----
 source3/smbd/proto.h      | 14 +++++----
 source3/utils/status.c    | 45 ++++++++++++++---------------
 3 files changed, 96 insertions(+), 35 deletions(-)

diff --git a/source3/smbd/notify_msg.c b/source3/smbd/notify_msg.c
index b31cb57..2fe4472 100644
--- a/source3/smbd/notify_msg.c
+++ b/source3/smbd/notify_msg.c
@@ -252,14 +252,72 @@ void notify_walk_idx(struct notify_context *notify,
 	return;
 }
 
-void notify_walk(struct notify_context *notify,
-		 void (*fn)(const char *path,
-			    struct notify_db_entry *entries,
-			    size_t num_entries,
-			    time_t deleted_time, void *private_data),
-		 void *private_data)
+NTSTATUS notify_walk(struct notify_context *notify,
+		     bool (*fn)(const char *path, struct server_id server,
+				const struct notify_instance *instance,
+				void *private_data),
+		     void *private_data)
 {
-	return;
+	struct tevent_context *ev;
+	struct tevent_req *req;
+	struct messaging_rec *rec;
+	uint64_t log_idx;
+	NTSTATUS status;
+	int ret;
+	bool ok;
+
+	ev = samba_tevent_context_init(notify);
+	if (ev == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	req = messaging_read_send(ev, ev, notify->msg_ctx, MSG_SMB_NOTIFY_DB);
+	if (req == NULL) {
+		TALLOC_FREE(ev);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	ok = tevent_req_set_endtime(req, ev, timeval_current_ofs(10, 0));
+	if (!ok) {
+		TALLOC_FREE(ev);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	status = messaging_send_buf(notify->msg_ctx, notify->notifyd,
+				    MSG_SMB_NOTIFY_GET_DB, NULL, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: messaging_send_buf failed\n",
+			   nt_errstr(status)));
+		TALLOC_FREE(ev);
+		return status;
+	}
+
+	ok = tevent_req_poll(req, ev);
+	if (!ok) {
+		DEBUG(10, ("%s: tevent_req_poll failed\n", __func__));
+		TALLOC_FREE(ev);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ret = messaging_read_recv(req, ev, &rec);
+	if (ret != 0) {
+		DEBUG(10, ("%s: messaging_read_recv failed: %s\n",
+			   __func__, strerror(ret)));
+		TALLOC_FREE(ev);
+		return map_nt_error_from_unix(ret);
+	}
+
+	ret = notifyd_parse_db(rec->buf.data, rec->buf.length, &log_idx,
+			       fn, private_data);
+	if (ret != 0) {
+		DEBUG(10, ("%s: notifyd_parse_db failed: %s\n",
+			   __func__, strerror(ret)));
+		TALLOC_FREE(ev);
+		return map_nt_error_from_unix(ret);
+	}
+
+	TALLOC_FREE(ev);
+	return NT_STATUS_OK;
 }
 
 void notify_cleanup(struct notify_context *notify)
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 268c3d2..31fab43 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -573,12 +573,14 @@ void notify_walk_idx(struct notify_context *notify,
 				uint32_t *vnns, size_t num_vnns,
 				void *private_data),
 		     void *private_data);
-void notify_walk(struct notify_context *notify,
-		 void (*fn)(const char *path,
-			    struct notify_db_entry *entries,
-			    size_t num_entries,
-			    time_t deleted_time, void *private_data),
-		 void *private_data);
+
+struct notify_instance;
+NTSTATUS notify_walk(struct notify_context *notify,
+		     bool (*fn)(const char *path, struct server_id server,
+				const struct notify_instance *instance,
+				void *private_data),
+		     void *private_data);
+
 void notify_cleanup(struct notify_context *notify);
 
 /* The following definitions come from smbd/ntquotas.c  */
diff --git a/source3/utils/status.c b/source3/utils/status.c
index 61efa93..e38f3a8 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -45,6 +45,7 @@
 #include "lib/conn_tdb.h"
 #include "serverid.h"
 #include "status_profile.h"
+#include "smbd/notifyd/notifyd.h"
 
 #define SMB_MAXPIDS		2048
 static uid_t 		Ucrit_uid = 0;               /* added by OH */
@@ -321,28 +322,17 @@ static int traverse_sessionid(const char *key, struct sessionid *session,
 }
 
 
-static void print_notify_recs(const char *path,
-			      struct notify_db_entry *entries,
-			      size_t num_entries,
-			      time_t deleted_time, void *private_data)
+static bool print_notify_rec(const char *path, struct server_id server,
+			     const struct notify_instance *instance,
+			     void *private_data)
 {
-	size_t i;
-	d_printf("%s\n", path);
+	struct server_id_buf idbuf;
 
-	if (num_entries == 0) {
-		d_printf("deleted %s\n", time_to_asc(deleted_time));
-	}
-
-	for (i=0; i<num_entries; i++) {
-		struct notify_db_entry *e = &entries[i];
-		char *str;
+	d_printf("%s\\%s\\%x\\%x\n", path, server_id_str_buf(server, &idbuf),
+		 (unsigned)instance->filter,
+		 (unsigned)instance->subdir_filter);
 
-		str = server_id_str(talloc_tos(), &e->server);
-		printf("%s %x %x\n", str, (unsigned)e->filter,
-		       (unsigned)e->subdir_filter);
-		TALLOC_FREE(str);
-	}
-	printf("\n");
+	return true;
 }
 
 int main(int argc, const char *argv[])
@@ -371,7 +361,7 @@ int main(int argc, const char *argv[])
 	};
 	TALLOC_CTX *frame = talloc_stackframe();
 	int ret = 0;
-	struct messaging_context *msg_ctx;
+	struct messaging_context *msg_ctx = NULL;
 	char *db_path;
 	bool ok;
 
@@ -582,11 +572,22 @@ int main(int argc, const char *argv[])
 	if (show_notify) {
 		struct notify_context *n;
 
-		n = notify_init(talloc_tos(), NULL, NULL);
+		if (msg_ctx == NULL) {
+			msg_ctx = messaging_init(
+				NULL, samba_tevent_context_init(NULL));
+			if (msg_ctx == NULL) {
+				fprintf(stderr, "messaging_init failed\n");
+				ret = -1;
+				goto done;
+			}
+		}
+
+		n = notify_init(talloc_tos(), msg_ctx,
+				messaging_tevent_context(msg_ctx));
 		if (n == NULL) {
 			goto done;
 		}
-		notify_walk(n, print_notify_recs, NULL);
+		notify_walk(n, print_notify_rec, NULL);
 		TALLOC_FREE(n);
 	}
 
-- 
1.9.1


From 754337822452b799d474dcab72fdc7145f3b8f5e Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 12 Dec 2014 15:37:30 +0100
Subject: [PATCH 14/15] smbd: Remove SMB_VFS_NOTIFY_WATCH

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 examples/VFS/skel_opaque.c       | 14 ------------
 examples/VFS/skel_transparent.c  | 16 --------------
 source3/include/vfs.h            | 19 -----------------
 source3/include/vfs_macros.h     |  5 -----
 source3/modules/vfs_ceph.c       | 18 ----------------
 source3/modules/vfs_default.c    | 46 ----------------------------------------
 source3/modules/vfs_full_audit.c | 24 ---------------------
 source3/modules/vfs_glusterfs.c  | 13 ------------
 source3/modules/vfs_time_audit.c | 29 -------------------------
 source3/smbd/vfs.c               | 16 --------------
 10 files changed, 200 deletions(-)

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index b52c381..215c395 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -463,19 +463,6 @@ static char *skel_realpath(vfs_handle_struct *handle, const char *path)
 	return NULL;
 }
 
-static NTSTATUS skel_notify_watch(struct vfs_handle_struct *handle,
-				  struct sys_notify_context *ctx,
-				  const char *path,
-				  uint32_t *filter,
-				  uint32_t *subdir_filter,
-				  void (*callback) (struct sys_notify_context *
-						    ctx, void *private_data,
-						    struct notify_event *ev),
-				  void *private_data, void *handle_p)
-{
-	return NT_STATUS_NOT_IMPLEMENTED;
-}
-
 static int skel_chflags(vfs_handle_struct *handle, const char *path,
 			uint flags)
 {
@@ -888,7 +875,6 @@ struct vfs_fn_pointers skel_opaque_fns = {
 	.link_fn = skel_link,
 	.mknod_fn = skel_mknod,
 	.realpath_fn = skel_realpath,
-	.notify_watch_fn = skel_notify_watch,
 	.chflags_fn = skel_chflags,
 	.file_id_create_fn = skel_file_id_create,
 	.copy_chunk_send_fn = skel_copy_chunk_send,
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index 925e520..6256654 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -551,21 +551,6 @@ static char *skel_realpath(vfs_handle_struct *handle, const char *path)
 	return SMB_VFS_NEXT_REALPATH(handle, path);
 }
 
-static NTSTATUS skel_notify_watch(struct vfs_handle_struct *handle,
-				  struct sys_notify_context *ctx,
-				  const char *path,
-				  uint32_t *filter,
-				  uint32_t *subdir_filter,
-				  void (*callback) (struct sys_notify_context *ctx,
-						    void *private_data,
-						    struct notify_event *ev),
-				  void *private_data, void *handle_p)
-{
-	return SMB_VFS_NEXT_NOTIFY_WATCH(handle, ctx, path,
-					 filter, subdir_filter, callback,
-					 private_data, handle_p);
-}
-
 static int skel_chflags(vfs_handle_struct *handle, const char *path,
 			uint flags)
 {
@@ -998,7 +983,6 @@ struct vfs_fn_pointers skel_transparent_fns = {
 	.link_fn = skel_link,
 	.mknod_fn = skel_mknod,
 	.realpath_fn = skel_realpath,
-	.notify_watch_fn = skel_notify_watch,
 	.chflags_fn = skel_chflags,
 	.file_id_create_fn = skel_file_id_create,
 	.copy_chunk_send_fn = skel_copy_chunk_send,
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index 1843ef4..798fb1d 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -620,16 +620,6 @@ struct vfs_fn_pointers {
 	int (*link_fn)(struct vfs_handle_struct *handle, const char *oldpath, const char *newpath);
 	int (*mknod_fn)(struct vfs_handle_struct *handle, const char *path, mode_t mode, SMB_DEV_T dev);
 	char *(*realpath_fn)(struct vfs_handle_struct *handle, const char *path);
-	NTSTATUS (*notify_watch_fn)(struct vfs_handle_struct *handle,
-				    struct sys_notify_context *ctx,
-				    const char *path,
-				    uint32_t *filter,
-				    uint32_t *subdir_filter,
-				    void (*callback)(struct sys_notify_context *ctx,
-						     void *private_data,
-						     struct notify_event *ev),
-				    void *private_data, 
-				    void *handle_p);
 	int (*chflags_fn)(struct vfs_handle_struct *handle, const char *path, unsigned int flags);
 	struct file_id (*file_id_create_fn)(struct vfs_handle_struct *handle,
 					    const SMB_STRUCT_STAT *sbuf);
@@ -1073,15 +1063,6 @@ int smb_vfs_call_link(struct vfs_handle_struct *handle, const char *oldpath,
 int smb_vfs_call_mknod(struct vfs_handle_struct *handle, const char *path,
 		       mode_t mode, SMB_DEV_T dev);
 char *smb_vfs_call_realpath(struct vfs_handle_struct *handle, const char *path);
-NTSTATUS smb_vfs_call_notify_watch(struct vfs_handle_struct *handle,
-				   struct sys_notify_context *ctx,
-				   const char *name,
-				   uint32_t *filter,
-				   uint32_t *subdir_filter,
-				   void (*callback)(struct sys_notify_context *ctx,
-						    void *private_data,
-						    struct notify_event *ev),
-				   void *private_data, void *handle_p);
 int smb_vfs_call_chflags(struct vfs_handle_struct *handle, const char *path,
 			 unsigned int flags);
 struct file_id smb_vfs_call_file_id_create(struct vfs_handle_struct *handle,
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index ef97b49..021b083 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -336,11 +336,6 @@
 #define SMB_VFS_NEXT_REALPATH(handle, path) \
 	smb_vfs_call_realpath((handle)->next, (path))
 
-#define SMB_VFS_NOTIFY_WATCH(conn, ctx, path, filter, subdir_filter, callback, private_data, handle_p) \
-	smb_vfs_call_notify_watch((conn)->vfs_handles, (ctx), (path), (filter), (subdir_filter), (callback), (private_data), (handle_p))
-#define SMB_VFS_NEXT_NOTIFY_WATCH(conn, ctx, path, filter, subdir_filter, callback, private_data, handle_p) \
-	smb_vfs_call_notify_watch((conn)->next, (ctx), (path), (filter), (subdir_filter), (callback), (private_data), (handle_p))
-
 #define SMB_VFS_CHFLAGS(conn, path, flags) \
 	smb_vfs_call_chflags((conn)->vfs_handles, (path), (flags))
 #define SMB_VFS_NEXT_CHFLAGS(handle, path, flags) \
diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c
index ec72312..ea5317b 100644
--- a/source3/modules/vfs_ceph.c
+++ b/source3/modules/vfs_ceph.c
@@ -1001,23 +1001,6 @@ static char *cephwrap_realpath(struct vfs_handle_struct *handle,  const char *pa
 	return result;
 }
 
-static NTSTATUS cephwrap_notify_watch(struct vfs_handle_struct *vfs_handle,
-				     struct sys_notify_context *ctx,
-				     const char *path,
-				     uint32_t *filter,
-				     uint32_t *subdir_filter,
-				     void (*callback)(struct sys_notify_context *ctx,
-						      void *private_data,
-						      struct notify_event *ev),
-				     void *private_data,
-				     void *handle_p)
-{
-	/*
-	 * We cannot call inotify on files the kernel does not know about
-	 */
-	return NT_STATUS_OK;
-}
-
 static int cephwrap_chflags(struct vfs_handle_struct *handle, const char *path,
 			   unsigned int flags)
 {
@@ -1235,7 +1218,6 @@ static struct vfs_fn_pointers ceph_fns = {
 	.link_fn = cephwrap_link,
 	.mknod_fn = cephwrap_mknod,
 	.realpath_fn = cephwrap_realpath,
-	.notify_watch_fn = cephwrap_notify_watch,
 	.chflags_fn = cephwrap_chflags,
 	.get_real_filename_fn = cephwrap_get_real_filename,
 	.connectpath_fn = cephwrap_connectpath,
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 69af305..47c76b0 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2099,51 +2099,6 @@ static char *vfswrap_realpath(vfs_handle_struct *handle, const char *path)
 	return result;
 }
 
-static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle,
-				     struct sys_notify_context *ctx,
-				     const char *path,
-				     uint32_t *filter,
-				     uint32_t *subdir_filter,
-				     void (*callback)(struct sys_notify_context *ctx, 
-						      void *private_data,
-						      struct notify_event *ev),
-				     void *private_data, void *handle)
-{
-	/*
-	 * So far inotify is the only supported default notify mechanism. If
-	 * another platform like the the BSD's or a proprietary Unix comes
-	 * along and wants another default, we can play the same trick we
-	 * played with Posix ACLs.
-	 *
-	 * Until that is the case, hard-code inotify here.
-	 */
-#ifdef HAVE_INOTIFY
-	if (lp_kernel_change_notify()) {
-		int ret;
-		if (!lp_parm_bool(-1, "notify", "inotify", True)) {
-			return NT_STATUS_INVALID_SYSTEM_SERVICE;
-		}
-		/*
-		 * "ctx->private_data" is not obvious as a talloc context
-		 * here. Without modifying the VFS we don't have a mem_ctx
-		 * available here, and ctx->private_data was used by
-		 * inotify_watch before it got a real talloc parent.
-		 */
-		ret = inotify_watch(ctx->private_data, ctx,
-				    path, filter, subdir_filter,
-				    callback, private_data, handle);
-		if (ret != 0) {
-			return map_nt_error_from_unix(ret);
-		}
-		return NT_STATUS_OK;
-	}
-#endif
-	/*
-	 * Do nothing, leave everything to notify_internal.c
-	 */
-	return NT_STATUS_OK;
-}
-
 static int vfswrap_chflags(vfs_handle_struct *handle, const char *path,
 			   unsigned int flags)
 {
@@ -2607,7 +2562,6 @@ static struct vfs_fn_pointers vfs_default_fns = {
 	.link_fn = vfswrap_link,
 	.mknod_fn = vfswrap_mknod,
 	.realpath_fn = vfswrap_realpath,
-	.notify_watch_fn = vfswrap_notify_watch,
 	.chflags_fn = vfswrap_chflags,
 	.file_id_create_fn = vfswrap_file_id_create,
 	.streaminfo_fn = vfswrap_streaminfo,
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index c5a9c0d..956e64a 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -155,7 +155,6 @@ typedef enum _vfs_op_type {
 	SMB_VFS_OP_LINK,
 	SMB_VFS_OP_MKNOD,
 	SMB_VFS_OP_REALPATH,
-	SMB_VFS_OP_NOTIFY_WATCH,
 	SMB_VFS_OP_CHFLAGS,
 	SMB_VFS_OP_FILE_ID_CREATE,
 	SMB_VFS_OP_STREAMINFO,
@@ -280,7 +279,6 @@ static struct {
 	{ SMB_VFS_OP_LINK,	"link" },
 	{ SMB_VFS_OP_MKNOD,	"mknod" },
 	{ SMB_VFS_OP_REALPATH,	"realpath" },
-	{ SMB_VFS_OP_NOTIFY_WATCH, "notify_watch" },
 	{ SMB_VFS_OP_CHFLAGS,	"chflags" },
 	{ SMB_VFS_OP_FILE_ID_CREATE,	"file_id_create" },
 	{ SMB_VFS_OP_STREAMINFO,	"streaminfo" },
@@ -1581,27 +1579,6 @@ static char *smb_full_audit_realpath(vfs_handle_struct *handle,
 	return result;
 }
 
-static NTSTATUS smb_full_audit_notify_watch(struct vfs_handle_struct *handle,
-			struct sys_notify_context *ctx,
-			const char *path,
-			uint32_t *filter,
-			uint32_t *subdir_filter,
-			void (*callback)(struct sys_notify_context *ctx,
-					void *private_data,
-					struct notify_event *ev),
-			void *private_data, void *handle_p)
-{
-	NTSTATUS result;
-
-	result = SMB_VFS_NEXT_NOTIFY_WATCH(handle, ctx, path,
-					   filter, subdir_filter, callback,
-					   private_data, handle_p);
-
-	do_log(SMB_VFS_OP_NOTIFY_WATCH, NT_STATUS_IS_OK(result), handle, "");
-
-	return result;
-}
-
 static int smb_full_audit_chflags(vfs_handle_struct *handle,
 			    const char *path, unsigned int flags)
 {
@@ -2253,7 +2230,6 @@ static struct vfs_fn_pointers vfs_full_audit_fns = {
 	.link_fn = smb_full_audit_link,
 	.mknod_fn = smb_full_audit_mknod,
 	.realpath_fn = smb_full_audit_realpath,
-	.notify_watch_fn = smb_full_audit_notify_watch,
 	.chflags_fn = smb_full_audit_chflags,
 	.file_id_create_fn = smb_full_audit_file_id_create,
 	.streaminfo_fn = smb_full_audit_streaminfo,
diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c
index 10c3a22..6ec403e 100644
--- a/source3/modules/vfs_glusterfs.c
+++ b/source3/modules/vfs_glusterfs.c
@@ -1077,18 +1077,6 @@ static int vfs_gluster_mknod(struct vfs_handle_struct *handle, const char *path,
 	return glfs_mknod(handle->data, path, mode, dev);
 }
 
-static NTSTATUS vfs_gluster_notify_watch(struct vfs_handle_struct *handle,
-					 struct sys_notify_context *ctx,
-					 const char *path, uint32_t *filter,
-					 uint32_t *subdir_filter,
-					 void (*callback) (struct sys_notify_context *ctx,
-							   void *private_data,
-							   struct notify_event *ev),
-					 void *private_data, void *handle_p)
-{
-	return NT_STATUS_NOT_IMPLEMENTED;
-}
-
 static int vfs_gluster_chflags(struct vfs_handle_struct *handle,
 			       const char *path, unsigned int flags)
 {
@@ -1798,7 +1786,6 @@ static struct vfs_fn_pointers glusterfs_fns = {
 	.link_fn = vfs_gluster_link,
 	.mknod_fn = vfs_gluster_mknod,
 	.realpath_fn = vfs_gluster_realpath,
-	.notify_watch_fn = vfs_gluster_notify_watch,
 	.chflags_fn = vfs_gluster_chflags,
 	.file_id_create_fn = NULL,
 	.copy_chunk_send_fn = NULL,
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index a1e825a..6fe109a 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -1416,34 +1416,6 @@ static char *smb_time_audit_realpath(vfs_handle_struct *handle,
 	return result;
 }
 
-static NTSTATUS smb_time_audit_notify_watch(struct vfs_handle_struct *handle,
-			struct sys_notify_context *ctx,
-			const char *path,
-			uint32_t *filter,
-			uint32_t *subdir_filter,
-			void (*callback)(struct sys_notify_context *ctx,
-					void *private_data,
-					struct notify_event *ev),
-			void *private_data, void *handle_p)
-{
-	NTSTATUS result;
-	struct timespec ts1,ts2;
-	double timediff;
-
-	clock_gettime_mono(&ts1);
-	result = SMB_VFS_NEXT_NOTIFY_WATCH(handle, ctx, path,
-					   filter, subdir_filter, callback,
-					   private_data, handle_p);
-	clock_gettime_mono(&ts2);
-	timediff = nsec_time_diff(&ts2,&ts1)*1.0e-9;
-
-	if (timediff > audit_timeout) {
-		smb_time_audit_log_fname("notify_watch", timediff, path);
-	}
-
-	return result;
-}
-
 static int smb_time_audit_chflags(vfs_handle_struct *handle,
 				  const char *path, unsigned int flags)
 {
@@ -2437,7 +2409,6 @@ static struct vfs_fn_pointers vfs_time_audit_fns = {
 	.link_fn = smb_time_audit_link,
 	.mknod_fn = smb_time_audit_mknod,
 	.realpath_fn = smb_time_audit_realpath,
-	.notify_watch_fn = smb_time_audit_notify_watch,
 	.chflags_fn = smb_time_audit_chflags,
 	.file_id_create_fn = smb_time_audit_file_id_create,
 	.streaminfo_fn = smb_time_audit_streaminfo,
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index d10e0d6..72aac52 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -2084,22 +2084,6 @@ char *smb_vfs_call_realpath(struct vfs_handle_struct *handle, const char *path)
 	return handle->fns->realpath_fn(handle, path);
 }
 
-NTSTATUS smb_vfs_call_notify_watch(struct vfs_handle_struct *handle,
-				   struct sys_notify_context *ctx,
-				   const char *path,
-				   uint32_t *filter,
-				   uint32_t *subdir_filter,
-				   void (*callback)(struct sys_notify_context *ctx,
-						    void *private_data,
-						    struct notify_event *ev),
-				   void *private_data, void *handle_p)
-{
-	VFS_FIND(notify_watch);
-	return handle->fns->notify_watch_fn(handle, ctx, path,
-					    filter, subdir_filter, callback,
-					    private_data, handle_p);
-}
-
 int smb_vfs_call_chflags(struct vfs_handle_struct *handle, const char *path,
 			 unsigned int flags)
 {
-- 
1.9.1


From 76be616d2fd1609d52521001c5061c2d278991a3 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 9 Jan 2015 12:59:46 +0000
Subject: [PATCH 15/15] notify: Remove two now unused stubs

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_msg.c | 14 --------------
 source3/smbd/proto.h      |  7 -------
 2 files changed, 21 deletions(-)

diff --git a/source3/smbd/notify_msg.c b/source3/smbd/notify_msg.c
index 2fe4472..8641983 100644
--- a/source3/smbd/notify_msg.c
+++ b/source3/smbd/notify_msg.c
@@ -243,15 +243,6 @@ void notify_trigger(struct notify_context *ctx,
 		iov, ARRAY_SIZE(iov), NULL, 0);
 }
 
-void notify_walk_idx(struct notify_context *notify,
-		     void (*fn)(const char *path,
-				uint32_t *vnns, size_t num_vnns,
-				void *private_data),
-		     void *private_data)
-{
-	return;
-}
-
 NTSTATUS notify_walk(struct notify_context *notify,
 		     bool (*fn)(const char *path, struct server_id server,
 				const struct notify_instance *instance,
@@ -319,8 +310,3 @@ NTSTATUS notify_walk(struct notify_context *notify,
 	TALLOC_FREE(ev);
 	return NT_STATUS_OK;
 }
-
-void notify_cleanup(struct notify_context *notify)
-{
-	return;
-}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 31fab43..0f348fe 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -568,11 +568,6 @@ NTSTATUS notify_remove(struct notify_context *notify, void *private_data);
 void notify_trigger(struct notify_context *notify,
 		    uint32_t action, uint32_t filter,
 		    const char *dir, const char *path);
-void notify_walk_idx(struct notify_context *notify,
-		     void (*fn)(const char *path,
-				uint32_t *vnns, size_t num_vnns,
-				void *private_data),
-		     void *private_data);
 
 struct notify_instance;
 NTSTATUS notify_walk(struct notify_context *notify,
@@ -581,8 +576,6 @@ NTSTATUS notify_walk(struct notify_context *notify,
 				void *private_data),
 		     void *private_data);
 
-void notify_cleanup(struct notify_context *notify);
-
 /* The following definitions come from smbd/ntquotas.c  */
 
 int vfs_get_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, struct dom_sid *psid, SMB_NTQUOTA_STRUCT *qt);
-- 
1.9.1



More information about the samba-technical mailing list