notifyd

Jeremy Allison jra at samba.org
Fri Jan 9 11:46:48 MST 2015


On Fri, Jan 09, 2015 at 05:56:44PM +0100, Volker Lendecke wrote:
> 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 :-)

Wow - this looks some seriously nice code !

The only thing I'd ask for is some more comments
inside smbd_notifyd_init() to point out we're
starting a new daemon here.

I'm assuming I can just push the first two patches...

What do you need to get patches 3-15 in master ?

Jeremy.

> 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

> 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