[PATCH] current notify work

Volker Lendecke Volker.Lendecke at SerNet.DE
Mon Nov 24 08:11:55 MST 2014


Hi!

Attached find my current patch series for the new messaging
based notify infrastructure. A lot of those patches are
preparatory and already carry a Signed-off-by me. I'd
appreciate if someone took the time to take a look and
potentially push some of them.

The big one is patch 35. It adds the notify daemon as a
tevent_req based engine that can be forked or run as part of
any tevent loop with a messaging context.

If you take a look at the "+" lines of patch 39 you see why
I'm doing this: All the hard work in notify_trigger is
replaced with one single messaging_send_iov call which boils
down to one single sendmsg call to the notify daemon. In
particular in a cluster environment the dbwrap_parse calls
really, really hurt performance.

Also we use just one single inotify listener inside the
notify daemon, the individual smbds don't do that anymore.
The architecture is such that we get clusterwide notifies
working correctly cross-protocol. I'm not aware of a cluster
file system giving us clusterwide inotify, but with this
code you can smb-mount on node a, nfs mount on node b, and a
new file coming in via nfs shows up on the smb-mount via
notify.

It would be trivial to add a simple protocol listener
available for NFS servers to enable recursive notifies
cross-protocol, something which inotify won't give us.
Ganesha could send us a unix dgm message whenever it changes
something and we sort out the recursive side of things.

There is one little downside of the code: "kernel change
notify" and "change notify" become global parameters. Also,
"notify:inotify" becomes a global choice, as does
"notify:fam". notifyd does not see shares at all, it only
deals with path names.

There are very few loose ends, smbstatus -N does not work
yet, and I did not yet implement the timestamp based
fallback that I've talked about at SambaXP and SDC. But the
basic engine works and survives autobuild.

The whole thing is

 75 files changed, 4051 insertions(+), 2259 deletions(-)

so there's quite a bit to read.

Comments?

Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de, mailto:kontakt at sernet.de
-------------- next part --------------
From 34f1a8fa1864004c3c4981f01513d38b67c6cec0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 13:33:06 +0000
Subject: [PATCH 01/41] lib: Split out sys_[read|write] & friends

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/include/proto.h                      |   5 --
 source3/lib/recvfile.c                       |   1 +
 source3/lib/sys_rw.c                         | 101 +++++++++++++++++++++++++++
 source3/lib/sys_rw.h                         |  36 ++++++++++
 source3/lib/system.c                         |  90 ------------------------
 source3/lib/util.c                           |   1 +
 source3/lib/util_file.c                      |   1 +
 source3/lib/util_sock.c                      |   1 +
 source3/lib/util_transfer_file.c             |   1 +
 source3/libsmb/unexpected.c                  |   1 +
 source3/modules/vfs_aio_fork.c               |   1 +
 source3/modules/vfs_aio_linux.c              |   1 +
 source3/modules/vfs_aio_posix.c              |   1 +
 source3/modules/vfs_default.c                |   1 +
 source3/modules/vfs_fruit.c                  |   1 +
 source3/printing/print_cups.c                |   1 +
 source3/rpc_server/samr/srv_samr_chgpasswd.c |   1 +
 source3/smbd/scavenger.c                     |   1 +
 source3/winbindd/winbindd_dual.c             |   1 +
 source3/wscript_build                        |   7 +-
 20 files changed, 158 insertions(+), 96 deletions(-)
 create mode 100644 source3/lib/sys_rw.c
 create mode 100644 source3/lib/sys_rw.h

diff --git a/source3/include/proto.h b/source3/include/proto.h
index ce23289..671f491 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -242,11 +242,6 @@ int sys_set_nfs_quota(const char *path, const char *bdev,
 
 /* The following definitions come from lib/system.c  */
 
-ssize_t sys_read(int fd, void *buf, size_t count);
-ssize_t sys_write(int fd, const void *buf, size_t count);
-ssize_t sys_writev(int fd, const struct iovec *iov, int iovcnt);
-ssize_t sys_pread(int fd, void *buf, size_t count, off_t off);
-ssize_t sys_pwrite(int fd, const void *buf, size_t count, off_t off);
 ssize_t sys_send(int s, const void *msg, size_t len, int flags);
 ssize_t sys_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
 int sys_fcntl_ptr(int fd, int cmd, void *arg);
diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c
index 273c51f..403d5e8 100644
--- a/source3/lib/recvfile.c
+++ b/source3/lib/recvfile.c
@@ -25,6 +25,7 @@
 
 #include "includes.h"
 #include "system/filesys.h"
+#include "lib/sys_rw.h"
 
 /* Do this on our own in TRANSFER_BUF_SIZE chunks.
  * It's safe to make direct syscalls to lseek/write here
diff --git a/source3/lib/sys_rw.c b/source3/lib/sys_rw.c
new file mode 100644
index 0000000..6d8f149
--- /dev/null
+++ b/source3/lib/sys_rw.c
@@ -0,0 +1,101 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Samba system utilities
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Jeremy Allison  1998-2005
+ * Copyright (C) Timur Bakeyev        2005
+ * Copyright (C) Bjoern Jacke    2006-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 "replace.h"
+#include "system/filesys.h"
+#include "lib/sys_rw.h"
+
+/*******************************************************************
+A read wrapper that will deal with EINTR/EWOULDBLOCK
+********************************************************************/
+
+ssize_t sys_read(int fd, void *buf, size_t count)
+{
+	ssize_t ret;
+
+	do {
+		ret = read(fd, buf, count);
+	} while (ret == -1 && (errno == EINTR || errno == EAGAIN ||
+			       errno == EWOULDBLOCK));
+
+	return ret;
+}
+
+/*******************************************************************
+A write wrapper that will deal with EINTR/EWOULDBLOCK.
+********************************************************************/
+
+ssize_t sys_write(int fd, const void *buf, size_t count)
+{
+	ssize_t ret;
+
+	do {
+		ret = write(fd, buf, count);
+	} while (ret == -1 && (errno == EINTR || errno == EAGAIN ||
+			       errno == EWOULDBLOCK));
+
+	return ret;
+}
+
+/*******************************************************************
+A writev wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_writev(int fd, const struct iovec *iov, int iovcnt)
+{
+	ssize_t ret;
+
+	do {
+		ret = writev(fd, iov, iovcnt);
+	} while (ret == -1 && (errno == EINTR || errno == EAGAIN ||
+			       errno == EWOULDBLOCK));
+
+	return ret;
+}
+
+/*******************************************************************
+A pread wrapper that will deal with EINTR
+********************************************************************/
+
+ssize_t sys_pread(int fd, void *buf, size_t count, off_t off)
+{
+	ssize_t ret;
+
+	do {
+		ret = pread(fd, buf, count, off);
+	} while (ret == -1 && errno == EINTR);
+	return ret;
+}
+
+/*******************************************************************
+A write wrapper that will deal with EINTR
+********************************************************************/
+
+ssize_t sys_pwrite(int fd, const void *buf, size_t count, off_t off)
+{
+	ssize_t ret;
+
+	do {
+		ret = pwrite(fd, buf, count, off);
+	} while (ret == -1 && errno == EINTR);
+	return ret;
+}
diff --git a/source3/lib/sys_rw.h b/source3/lib/sys_rw.h
new file mode 100644
index 0000000..ee1584e
--- /dev/null
+++ b/source3/lib/sys_rw.h
@@ -0,0 +1,36 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Samba system utilities
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Jeremy Allison  1998-2005
+ * Copyright (C) Timur Bakeyev        2005
+ * Copyright (C) Bjoern Jacke    2006-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/>.
+ */
+
+#ifndef __LIB_SYS_RW_H__
+#define __LIB_SYS_RW_H__
+
+#include <unistd.h>
+
+struct iovec;
+
+ssize_t sys_read(int fd, void *buf, size_t count);
+ssize_t sys_write(int fd, const void *buf, size_t count);
+ssize_t sys_writev(int fd, const struct iovec *iov, int iovcnt);
+ssize_t sys_pread(int fd, void *buf, size_t count, off_t off);
+ssize_t sys_pwrite(int fd, const void *buf, size_t count, off_t off);
+
+#endif
diff --git a/source3/lib/system.c b/source3/lib/system.c
index 6478e6f..7531d77 100644
--- a/source3/lib/system.c
+++ b/source3/lib/system.c
@@ -50,96 +50,6 @@
      expansions/etc make sense to the OS should be acceptable to Samba.
 */
 
-
-
-/*******************************************************************
-A read wrapper that will deal with EINTR.
-********************************************************************/
-
-ssize_t sys_read(int fd, void *buf, size_t count)
-{
-	ssize_t ret;
-
-	do {
-		ret = read(fd, buf, count);
-	} while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
-
-	return ret;
-}
-
-/*******************************************************************
-A write wrapper that will deal with EINTR.
-********************************************************************/
-
-ssize_t sys_write(int fd, const void *buf, size_t count)
-{
-	ssize_t ret;
-
-	do {
-		ret = write(fd, buf, count);
-	} while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
-
-	return ret;
-}
-
-/*******************************************************************
-A writev wrapper that will deal with EINTR.
-********************************************************************/
-
-ssize_t sys_writev(int fd, const struct iovec *iov, int iovcnt)
-{
-	ssize_t ret;
-
-#if 0
-	/* Try to confuse write_data_iov a bit */
-	if ((random() % 5) == 0) {
-		return sys_write(fd, iov[0].iov_base, iov[0].iov_len);
-	}
-	if (iov[0].iov_len > 1) {
-		return sys_write(fd, iov[0].iov_base,
-				 (random() % (iov[0].iov_len-1)) + 1);
-	}
-#endif
-
-	do {
-		ret = writev(fd, iov, iovcnt);
-	} while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
-
-	return ret;
-}
-
-/*******************************************************************
-A pread wrapper that will deal with EINTR
-********************************************************************/
-
-#if defined(HAVE_PREAD)
-ssize_t sys_pread(int fd, void *buf, size_t count, off_t off)
-{
-	ssize_t ret;
-
-	do {
-		ret = pread(fd, buf, count, off);
-	} while (ret == -1 && errno == EINTR);
-	return ret;
-}
-#endif
-
-/*******************************************************************
-A write wrapper that will deal with EINTR
-********************************************************************/
-
-#if defined(HAVE_PWRITE)
-ssize_t sys_pwrite(int fd, const void *buf, size_t count, off_t off)
-{
-	ssize_t ret;
-
-	do {
-		ret = pwrite(fd, buf, count, off);
-	} while (ret == -1 && errno == EINTR);
-	return ret;
-}
-#endif
-
 /*******************************************************************
 A send wrapper that will deal with EINTR or EAGAIN or EWOULDBLOCK.
 ********************************************************************/
diff --git a/source3/lib/util.c b/source3/lib/util.c
index 7b2afa8..49eef50 100644
--- a/source3/lib/util.c
+++ b/source3/lib/util.c
@@ -31,6 +31,7 @@
 #include <ccan/hash/hash.h>
 #include "libcli/security/security.h"
 #include "serverid.h"
+#include "lib/sys_rw.h"
 
 #ifdef HAVE_SYS_PRCTL_H
 #include <sys/prctl.h>
diff --git a/source3/lib/util_file.c b/source3/lib/util_file.c
index 8319f04..27a078f 100644
--- a/source3/lib/util_file.c
+++ b/source3/lib/util_file.c
@@ -18,6 +18,7 @@
  */
 
 #include "includes.h"
+#include "lib/sys_rw.h"
 
 /**
  Load from a pipe into memory.
diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c
index d865ffb..2bed9a9 100644
--- a/source3/lib/util_sock.c
+++ b/source3/lib/util_sock.c
@@ -28,6 +28,7 @@
 #include "../lib/util/tevent_unix.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "../lib/tsocket/tsocket.h"
+#include "lib/sys_rw.h"
 
 const char *client_addr(int fd, char *addr, size_t addrlen)
 {
diff --git a/source3/lib/util_transfer_file.c b/source3/lib/util_transfer_file.c
index 00a2c9d..d415d7f 100644
--- a/source3/lib/util_transfer_file.c
+++ b/source3/lib/util_transfer_file.c
@@ -22,6 +22,7 @@
 
 #include <includes.h>
 #include "transfer_file.h"
+#include "lib/sys_rw.h"
 
 /****************************************************************************
  Transfer some data between two fd's.
diff --git a/source3/libsmb/unexpected.c b/source3/libsmb/unexpected.c
index 2c01bb7..ee1c360 100644
--- a/source3/libsmb/unexpected.c
+++ b/source3/libsmb/unexpected.c
@@ -22,6 +22,7 @@
 #include "../lib/util/tevent_ntstatus.h"
 #include "lib/async_req/async_sock.h"
 #include "libsmb/nmblib.h"
+#include "lib/sys_rw.h"
 
 static const char *nmbd_socket_dir(void)
 {
diff --git a/source3/modules/vfs_aio_fork.c b/source3/modules/vfs_aio_fork.c
index 12e6f80..c2148a1 100644
--- a/source3/modules/vfs_aio_fork.c
+++ b/source3/modules/vfs_aio_fork.c
@@ -26,6 +26,7 @@
 #include "smbd/globals.h"
 #include "lib/async_req/async_sock.h"
 #include "lib/util/tevent_unix.h"
+#include "lib/sys_rw.h"
 
 #if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && !defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
 # error Can not pass file descriptors
diff --git a/source3/modules/vfs_aio_linux.c b/source3/modules/vfs_aio_linux.c
index 6188975..6c97592 100644
--- a/source3/modules/vfs_aio_linux.c
+++ b/source3/modules/vfs_aio_linux.c
@@ -24,6 +24,7 @@
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "lib/util/tevent_unix.h"
+#include "lib/sys_rw.h"
 #include <sys/eventfd.h>
 #include <libaio.h>
 
diff --git a/source3/modules/vfs_aio_posix.c b/source3/modules/vfs_aio_posix.c
index 3629541..ef5f706 100644
--- a/source3/modules/vfs_aio_posix.c
+++ b/source3/modules/vfs_aio_posix.c
@@ -24,6 +24,7 @@
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "lib/util/tevent_unix.h"
+#include "lib/sys_rw.h"
 #include <aio.h>
 
 /* The signal we'll use to signify aio done. */
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 2ac7100..ab2ba89 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -32,6 +32,7 @@
 #include "lib/util/tevent_unix.h"
 #include "lib/asys/asys.h"
 #include "lib/util/tevent_ntstatus.h"
+#include "lib/sys_rw.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index c1555f0..492019c 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -28,6 +28,7 @@
 #include "smbd/globals.h"
 #include "messages.h"
 #include "libcli/security/security.h"
+#include "lib/sys_rw.h"
 
 /*
  * Enhanced OS X and Netatalk compatibility
diff --git a/source3/printing/print_cups.c b/source3/printing/print_cups.c
index 0ec71ab..68f367c 100644
--- a/source3/printing/print_cups.c
+++ b/source3/printing/print_cups.c
@@ -26,6 +26,7 @@
 #include "printing.h"
 #include "printing/pcap.h"
 #include "librpc/gen_ndr/ndr_printcap.h"
+#include "lib/sys_rw.h"
 
 #ifdef HAVE_CUPS
 #include <cups/cups.h>
diff --git a/source3/rpc_server/samr/srv_samr_chgpasswd.c b/source3/rpc_server/samr/srv_samr_chgpasswd.c
index 684ccee..e899306 100644
--- a/source3/rpc_server/samr/srv_samr_chgpasswd.c
+++ b/source3/rpc_server/samr/srv_samr_chgpasswd.c
@@ -54,6 +54,7 @@
 #include "rpc_server/samr/srv_samr_util.h"
 #include "passdb.h"
 #include "auth.h"
+#include "lib/sys_rw.h"
 
 #ifndef ALLOW_CHANGE_PASSWORD
 #if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
diff --git a/source3/smbd/scavenger.c b/source3/smbd/scavenger.c
index 122305e..013b4d2 100644
--- a/source3/smbd/scavenger.c
+++ b/source3/smbd/scavenger.c
@@ -26,6 +26,7 @@
 #include "smbd/scavenger.h"
 #include "locking/proto.h"
 #include "lib/util/util_process.h"
+#include "lib/sys_rw.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_SCAVENGER
diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c
index f71d111..b9c110f 100644
--- a/source3/winbindd/winbindd_dual.c
+++ b/source3/winbindd/winbindd_dual.c
@@ -38,6 +38,7 @@
 #include "messages.h"
 #include "../lib/util/tevent_unix.h"
 #include "lib/param/loadparm.h"
+#include "lib/sys_rw.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
diff --git a/source3/wscript_build b/source3/wscript_build
index 56c1a98..e628b75 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -260,6 +260,11 @@ bld.SAMBA3_SUBSYSTEM('KRBCLIENT',
                      allow_warnings=True,
                      public_deps='krb5samba k5crypto gssapi LIBTSOCKET CLDAP LIBNMB')
 
+bld.SAMBA3_LIBRARY('sys_rw',
+                   source='lib/sys_rw.c',
+                   deps='replace',
+                   private_library=True)
+
 bld.SAMBA3_SUBSYSTEM('samba3util',
                    source='''lib/system.c
                    lib/sendfile.c
@@ -271,7 +276,7 @@ bld.SAMBA3_SUBSYSTEM('samba3util',
                    lib/util_sock.c
                    lib/util_transfer_file.c
                    lib/sock_exec.c''',
-                   deps='ndr samba-security NDR_SECURITY samba-util util_tdb ccan-hash')
+                   deps='ndr samba-security NDR_SECURITY samba-util util_tdb ccan-hash sys_rw')
 
 if bld.CONFIG_GET("CTDB_CFLAGS") and bld.CONFIG_GET("CTDB_INCLUDE"):
     SAMBA_CLUSTER_SUPPORT_SOURCES='''
-- 
1.9.1


From 7671fe8f0273405c595550d6d3cef67ddf968dd4 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 14:06:49 +0000
Subject: [PATCH 02/41] lib: read_data->read_data_ntstatus

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/include/proto.h          | 2 +-
 source3/lib/ctdbd_conn.c         | 6 +++---
 source3/lib/util_sock.c          | 2 +-
 source3/nmbd/asyncdns.c          | 4 ++--
 source3/smbd/notify_inotify.c    | 2 +-
 source3/smbd/reply.c             | 3 ++-
 source3/winbindd/winbindd_dual.c | 5 +++--
 7 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/source3/include/proto.h b/source3/include/proto.h
index 671f491..821d232 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -565,7 +565,7 @@ NTSTATUS read_fd_with_timeout(int fd, char *buf,
 				  size_t mincnt, size_t maxcnt,
 				  unsigned int time_out,
 				  size_t *size_ret);
-NTSTATUS read_data(int fd, char *buffer, size_t N);
+NTSTATUS read_data_ntstatus(int fd, char *buffer, size_t N);
 ssize_t write_data(int fd, const char *buffer, size_t N);
 ssize_t iov_buflen(const struct iovec *iov, int iovlen);
 uint8_t *iov_buf(TALLOC_CTX *mem_ctx, const struct iovec *iov, int iovcnt);
diff --git a/source3/lib/ctdbd_conn.c b/source3/lib/ctdbd_conn.c
index a26f410..619b7fa 100644
--- a/source3/lib/ctdbd_conn.c
+++ b/source3/lib/ctdbd_conn.c
@@ -345,7 +345,7 @@ static NTSTATUS ctdb_read_packet(int fd, TALLOC_CTX *mem_ctx,
 		}
 	}
 
-	status = read_data(fd, (char *)&msglen, sizeof(msglen));
+	status = read_data_ntstatus(fd, (char *)&msglen, sizeof(msglen));
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
@@ -362,8 +362,8 @@ static NTSTATUS ctdb_read_packet(int fd, TALLOC_CTX *mem_ctx,
 
 	req->length = msglen;
 
-	status = read_data(fd, ((char *)req) + sizeof(msglen),
-			   msglen - sizeof(msglen));
+	status = read_data_ntstatus(fd, ((char *)req) + sizeof(msglen),
+				    msglen - sizeof(msglen));
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c
index 2bed9a9..d93e22d 100644
--- a/source3/lib/util_sock.c
+++ b/source3/lib/util_sock.c
@@ -197,7 +197,7 @@ NTSTATUS read_fd_with_timeout(int fd, char *buf,
  on socket calls.
 ****************************************************************************/
 
-NTSTATUS read_data(int fd, char *buffer, size_t N)
+NTSTATUS read_data_ntstatus(int fd, char *buffer, size_t N)
 {
 	return read_fd_with_timeout(fd, buffer, N, N, 0, NULL);
 }
diff --git a/source3/nmbd/asyncdns.c b/source3/nmbd/asyncdns.c
index 90340ef..4468c7b 100644
--- a/source3/nmbd/asyncdns.c
+++ b/source3/nmbd/asyncdns.c
@@ -90,7 +90,7 @@ static void asyncdns_process(void)
 	while (1) {
 		NTSTATUS status;
 
-		status = read_data(fd_in, (char *)&r, sizeof(r));
+		status = read_data_ntstatus(fd_in, (char *)&r, sizeof(r));
 
 		if (!NT_STATUS_IS_OK(status)) {
 			break;
@@ -219,7 +219,7 @@ void run_dns_queue(struct messaging_context *msg)
 		start_async_dns(msg);
 	}
 
-	status = read_data(fd_in, (char *)&r, sizeof(r));
+	status = read_data_ntstatus(fd_in, (char *)&r, sizeof(r));
 
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0, ("read from child failed: %s\n", nt_errstr(status)));
diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index efb659f..5ec6178 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -216,7 +216,7 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
 	if (e == NULL) return;
 	((uint8_t *)e)[bufsize] = '\0';
 
-	status = read_data(in->fd, (char *)e0, bufsize);
+	status = read_data_ntstatus(in->fd, (char *)e0, bufsize);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("Failed to read all inotify data - %s\n",
 			nt_errstr(status)));
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index 9b3ed65..83369f8 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -4361,7 +4361,8 @@ void reply_writebraw(struct smb_request *req)
 				(int)tcount,(int)nwritten,(int)numtowrite));
 		}
 
-		status = read_data(xconn->transport.sock, buf+4, numtowrite);
+		status = read_data_ntstatus(xconn->transport.sock, buf+4,
+					    numtowrite);
 
 		if (!NT_STATUS_IS_OK(status)) {
 			/* Try and give an error message
diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c
index b9c110f..43a27b3 100644
--- a/source3/winbindd/winbindd_dual.c
+++ b/source3/winbindd/winbindd_dual.c
@@ -53,7 +53,7 @@ static NTSTATUS child_read_request(int sock, struct winbindd_request *wreq)
 {
 	NTSTATUS status;
 
-	status = read_data(sock, (char *)wreq, sizeof(*wreq));
+	status = read_data_ntstatus(sock, (char *)wreq, sizeof(*wreq));
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(3, ("child_read_request: read_data failed: %s\n",
 			  nt_errstr(status)));
@@ -76,7 +76,8 @@ static NTSTATUS child_read_request(int sock, struct winbindd_request *wreq)
 	/* Ensure null termination */
 	wreq->extra_data.data[wreq->extra_len] = '\0';
 
-	status = read_data(sock, wreq->extra_data.data, wreq->extra_len);
+	status = read_data_ntstatus(sock, wreq->extra_data.data,
+				    wreq->extra_len);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0, ("Could not read extra data: %s\n",
 			  nt_errstr(status)));
-- 
1.9.1


From 5fab12f7f8a347a4c2b22e66bb40d9c3877933ef Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 14:21:17 +0000
Subject: [PATCH 03/41] lib: Split out iov_buf[len]

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/include/proto.h      |  3 +-
 source3/lib/iov_buf.c        | 65 ++++++++++++++++++++++++++++++++++++++++++++
 source3/lib/iov_buf.h        | 29 ++++++++++++++++++++
 source3/lib/messages.c       |  1 +
 source3/lib/messages_ctdbd.c |  1 +
 source3/lib/util_sock.c      | 44 +-----------------------------
 source3/wscript_build        |  7 ++++-
 7 files changed, 104 insertions(+), 46 deletions(-)
 create mode 100644 source3/lib/iov_buf.c
 create mode 100644 source3/lib/iov_buf.h

diff --git a/source3/include/proto.h b/source3/include/proto.h
index 821d232..4f9fec8 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -566,9 +566,8 @@ NTSTATUS read_fd_with_timeout(int fd, char *buf,
 				  unsigned int time_out,
 				  size_t *size_ret);
 NTSTATUS read_data_ntstatus(int fd, char *buffer, size_t N);
+
 ssize_t write_data(int fd, const char *buffer, size_t N);
-ssize_t iov_buflen(const struct iovec *iov, int iovlen);
-uint8_t *iov_buf(TALLOC_CTX *mem_ctx, const struct iovec *iov, int iovcnt);
 ssize_t write_data_iov(int fd, const struct iovec *orig_iov, int iovcnt);
 bool send_keepalive(int client);
 NTSTATUS read_smb_length_return_keepalive(int fd, char *inbuf,
diff --git a/source3/lib/iov_buf.c b/source3/lib/iov_buf.c
new file mode 100644
index 0000000..dd99da3
--- /dev/null
+++ b/source3/lib/iov_buf.c
@@ -0,0 +1,65 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Samba system utilities
+ * 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 "system/filesys.h"
+#include "iov_buf.h"
+
+ssize_t iov_buflen(const struct iovec *iov, int iovcnt)
+{
+	size_t buflen = 0;
+	int i;
+
+	for (i=0; i<iovcnt; i++) {
+		size_t thislen = iov[i].iov_len;
+		size_t tmp = buflen + thislen;
+
+		if ((tmp < buflen) || (tmp < thislen)) {
+			/* overflow */
+			return -1;
+		}
+		buflen = tmp;
+	}
+	return buflen;
+}
+
+uint8_t *iov_buf(TALLOC_CTX *mem_ctx, const struct iovec *iov, int iovcnt)
+{
+	int i;
+	ssize_t buflen;
+	uint8_t *buf, *p;
+
+	buflen = iov_buflen(iov, iovcnt);
+	if (buflen == -1) {
+		return NULL;
+	}
+	buf = talloc_array(mem_ctx, uint8_t, buflen);
+	if (buf == NULL) {
+		return NULL;
+	}
+
+	p = buf;
+	for (i=0; i<iovcnt; i++) {
+		size_t len = iov[i].iov_len;
+
+		memcpy(p, iov[i].iov_base, len);
+		p += len;
+	}
+	return buf;
+}
diff --git a/source3/lib/iov_buf.h b/source3/lib/iov_buf.h
new file mode 100644
index 0000000..a884bdb
--- /dev/null
+++ b/source3/lib/iov_buf.h
@@ -0,0 +1,29 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Samba system utilities
+ * 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 __LIB_IOV_BUF_H__
+#define __LIB_IOV_BUF_H__
+
+#include <unistd.h>
+#include <talloc.h>
+
+ssize_t iov_buflen(const struct iovec *iov, int iovlen);
+uint8_t *iov_buf(TALLOC_CTX *mem_ctx, const struct iovec *iov, int iovcnt);
+
+#endif
diff --git a/source3/lib/messages.c b/source3/lib/messages.c
index d4c580f..5b4daa2 100644
--- a/source3/lib/messages.c
+++ b/source3/lib/messages.c
@@ -52,6 +52,7 @@
 #include "lib/util/tevent_unix.h"
 #include "lib/background.h"
 #include "lib/messages_dgm.h"
+#include "lib/iov_buf.h"
 
 struct messaging_callback {
 	struct messaging_callback *prev, *next;
diff --git a/source3/lib/messages_ctdbd.c b/source3/lib/messages_ctdbd.c
index eb7e929..59f5976 100644
--- a/source3/lib/messages_ctdbd.c
+++ b/source3/lib/messages_ctdbd.c
@@ -20,6 +20,7 @@
 #include "includes.h"
 #include "messages.h"
 #include "util_tdb.h"
+#include "lib/iov_buf.h"
 
 /*
  * It is not possible to include ctdb.h and tdb_compat.h (included via
diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c
index d93e22d..163045a 100644
--- a/source3/lib/util_sock.c
+++ b/source3/lib/util_sock.c
@@ -29,6 +29,7 @@
 #include "../lib/util/tevent_ntstatus.h"
 #include "../lib/tsocket/tsocket.h"
 #include "lib/sys_rw.h"
+#include "lib/iov_buf.h"
 
 const char *client_addr(int fd, char *addr, size_t addrlen)
 {
@@ -202,49 +203,6 @@ NTSTATUS read_data_ntstatus(int fd, char *buffer, size_t N)
 	return read_fd_with_timeout(fd, buffer, N, N, 0, NULL);
 }
 
-ssize_t iov_buflen(const struct iovec *iov, int iovcnt)
-{
-	size_t buflen = 0;
-	int i;
-
-	for (i=0; i<iovcnt; i++) {
-		size_t thislen = iov[i].iov_len;
-		size_t tmp = buflen + thislen;
-
-		if ((tmp < buflen) || (tmp < thislen)) {
-			/* overflow */
-			return -1;
-		}
-		buflen = tmp;
-	}
-	return buflen;
-}
-
-uint8_t *iov_buf(TALLOC_CTX *mem_ctx, const struct iovec *iov, int iovcnt)
-{
-	int i;
-	ssize_t buflen;
-	uint8_t *buf, *p;
-
-	buflen = iov_buflen(iov, iovcnt);
-	if (buflen == -1) {
-		return NULL;
-	}
-	buf = talloc_array(mem_ctx, uint8_t, buflen);
-	if (buf == NULL) {
-		return NULL;
-	}
-
-	p = buf;
-	for (i=0; i<iovcnt; i++) {
-		size_t len = iov[i].iov_len;
-
-		memcpy(p, iov[i].iov_base, len);
-		p += len;
-	}
-	return buf;
-}
-
 /****************************************************************************
  Write all data from an iov array
  NB. This can be called with a non-socket fd, don't add dependencies
diff --git a/source3/wscript_build b/source3/wscript_build
index e628b75..851b6ac 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -265,6 +265,11 @@ bld.SAMBA3_LIBRARY('sys_rw',
                    deps='replace',
                    private_library=True)
 
+bld.SAMBA3_LIBRARY('iov_buf',
+                   source='lib/iov_buf.c',
+                   deps='replace talloc',
+                   private_library=True)
+
 bld.SAMBA3_SUBSYSTEM('samba3util',
                    source='''lib/system.c
                    lib/sendfile.c
@@ -276,7 +281,7 @@ bld.SAMBA3_SUBSYSTEM('samba3util',
                    lib/util_sock.c
                    lib/util_transfer_file.c
                    lib/sock_exec.c''',
-                   deps='ndr samba-security NDR_SECURITY samba-util util_tdb ccan-hash sys_rw')
+                   deps='ndr samba-security NDR_SECURITY samba-util util_tdb ccan-hash sys_rw iov_buf')
 
 if bld.CONFIG_GET("CTDB_CFLAGS") and bld.CONFIG_GET("CTDB_INCLUDE"):
     SAMBA_CLUSTER_SUPPORT_SOURCES='''
-- 
1.9.1


From 365cf1e049ae62740f04895a7cf67676883176ae Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 14:25:56 +0000
Subject: [PATCH 04/41] lib: Split out write_data[_iov]

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/include/proto.h                    |   2 -
 source3/lib/ctdbd_conn.c                   |   1 +
 source3/lib/sys_rw_data.c                  | 107 +++++++++++++++++++++++++++++
 source3/lib/sys_rw_data.h                  |  33 +++++++++
 source3/lib/util.c                         |   1 +
 source3/lib/util_sock.c                    |  82 +---------------------
 source3/modules/vfs_aio_fork.c             |   1 +
 source3/modules/vfs_preopen.c              |   1 +
 source3/modules/vfs_smb_traffic_analyzer.c |   1 +
 source3/nmbd/asyncdns.c                    |   1 +
 source3/printing/printing.c                |   1 +
 source3/smbd/process.c                     |   1 +
 source3/smbd/reply.c                       |   1 +
 source3/smbd/smb2_read.c                   |   1 +
 source3/torture/torture.c                  |   1 +
 source3/utils/smbfilter.c                  |   1 +
 source3/winbindd/winbindd_dual.c           |   1 +
 source3/wscript_build                      |   4 +-
 18 files changed, 156 insertions(+), 85 deletions(-)
 create mode 100644 source3/lib/sys_rw_data.c
 create mode 100644 source3/lib/sys_rw_data.h

diff --git a/source3/include/proto.h b/source3/include/proto.h
index 4f9fec8..94a0636 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -567,8 +567,6 @@ NTSTATUS read_fd_with_timeout(int fd, char *buf,
 				  size_t *size_ret);
 NTSTATUS read_data_ntstatus(int fd, char *buffer, size_t N);
 
-ssize_t write_data(int fd, const char *buffer, size_t N);
-ssize_t write_data_iov(int fd, const struct iovec *orig_iov, int iovcnt);
 bool send_keepalive(int client);
 NTSTATUS read_smb_length_return_keepalive(int fd, char *inbuf,
 					  unsigned int timeout,
diff --git a/source3/lib/ctdbd_conn.c b/source3/lib/ctdbd_conn.c
index 619b7fa..5a9d155 100644
--- a/source3/lib/ctdbd_conn.c
+++ b/source3/lib/ctdbd_conn.c
@@ -23,6 +23,7 @@
 #include "serverid.h"
 #include "ctdbd_conn.h"
 #include "system/select.h"
+#include "lib/sys_rw_data.h"
 
 #include "messages.h"
 
diff --git a/source3/lib/sys_rw_data.c b/source3/lib/sys_rw_data.c
new file mode 100644
index 0000000..f7bedb3
--- /dev/null
+++ b/source3/lib/sys_rw_data.c
@@ -0,0 +1,107 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Samba system utilities
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Jeremy Allison  1998-2005
+ * Copyright (C) Timur Bakeyev        2005
+ * Copyright (C) Bjoern Jacke    2006-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 "replace.h"
+#include "system/filesys.h"
+#include "lib/sys_rw_data.h"
+#include "lib/sys_rw.h"
+#include "lib/iov_buf.h"
+
+/****************************************************************************
+ Write all data from an iov array
+ NB. This can be called with a non-socket fd, don't add dependencies
+ on socket calls.
+****************************************************************************/
+
+ssize_t write_data_iov(int fd, const struct iovec *orig_iov, int iovcnt)
+{
+	ssize_t to_send;
+	ssize_t thistime;
+	size_t sent;
+	struct iovec iov_copy[iovcnt];
+	struct iovec *iov;
+
+	to_send = iov_buflen(orig_iov, iovcnt);
+	if (to_send == -1) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	thistime = sys_writev(fd, orig_iov, iovcnt);
+	if ((thistime <= 0) || (thistime == to_send)) {
+		return thistime;
+	}
+	sent = thistime;
+
+	/*
+	 * We could not send everything in one call. Make a copy of iov that
+	 * we can mess with. We keep a copy of the array start in iov_copy for
+	 * the TALLOC_FREE, because we're going to modify iov later on,
+	 * discarding elements.
+	 */
+
+	memcpy(iov_copy, orig_iov, sizeof(struct iovec) * iovcnt);
+	iov = iov_copy;
+
+	while (sent < to_send) {
+		/*
+		 * We have to discard "thistime" bytes from the beginning
+		 * iov array, "thistime" contains the number of bytes sent
+		 * via writev last.
+		 */
+		while (thistime > 0) {
+			if (thistime < iov[0].iov_len) {
+				char *new_base =
+					(char *)iov[0].iov_base + thistime;
+				iov[0].iov_base = (void *)new_base;
+				iov[0].iov_len -= thistime;
+				break;
+			}
+			thistime -= iov[0].iov_len;
+			iov += 1;
+			iovcnt -= 1;
+		}
+
+		thistime = sys_writev(fd, iov, iovcnt);
+		if (thistime <= 0) {
+			break;
+		}
+		sent += thistime;
+	}
+
+	return sent;
+}
+
+/****************************************************************************
+ Write data to a fd.
+ NB. This can be called with a non-socket fd, don't add dependencies
+ on socket calls.
+****************************************************************************/
+
+ssize_t write_data(int fd, const char *buffer, size_t n)
+{
+	struct iovec iov;
+
+	iov.iov_base = discard_const_p(void, buffer);
+	iov.iov_len = n;
+	return write_data_iov(fd, &iov, 1);
+}
diff --git a/source3/lib/sys_rw_data.h b/source3/lib/sys_rw_data.h
new file mode 100644
index 0000000..fc97573
--- /dev/null
+++ b/source3/lib/sys_rw_data.h
@@ -0,0 +1,33 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Samba system utilities
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Jeremy Allison  1998-2005
+ * Copyright (C) Timur Bakeyev        2005
+ * Copyright (C) Bjoern Jacke    2006-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/>.
+ */
+
+#ifndef __LIB_SYS_RW_DATA_H__
+#define __LIB_SYS_RW_DATA_H__
+
+#include <unistd.h>
+
+struct iovec;
+
+ssize_t write_data_iov(int fd, const struct iovec *iov, int iovcnt);
+ssize_t write_data(int fd, const char *buffer, size_t n);
+
+#endif
diff --git a/source3/lib/util.c b/source3/lib/util.c
index 49eef50..b64b32b 100644
--- a/source3/lib/util.c
+++ b/source3/lib/util.c
@@ -32,6 +32,7 @@
 #include "libcli/security/security.h"
 #include "serverid.h"
 #include "lib/sys_rw.h"
+#include "lib/sys_rw_data.h"
 
 #ifdef HAVE_SYS_PRCTL_H
 #include <sys/prctl.h>
diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c
index 163045a..682d964 100644
--- a/source3/lib/util_sock.c
+++ b/source3/lib/util_sock.c
@@ -29,7 +29,7 @@
 #include "../lib/util/tevent_ntstatus.h"
 #include "../lib/tsocket/tsocket.h"
 #include "lib/sys_rw.h"
-#include "lib/iov_buf.h"
+#include "lib/sys_rw_data.h"
 
 const char *client_addr(int fd, char *addr, size_t addrlen)
 {
@@ -204,86 +204,6 @@ NTSTATUS read_data_ntstatus(int fd, char *buffer, size_t N)
 }
 
 /****************************************************************************
- Write all data from an iov array
- NB. This can be called with a non-socket fd, don't add dependencies
- on socket calls.
-****************************************************************************/
-
-ssize_t write_data_iov(int fd, const struct iovec *orig_iov, int iovcnt)
-{
-	ssize_t to_send;
-	ssize_t thistime;
-	size_t sent;
-	struct iovec iov_copy[iovcnt];
-	struct iovec *iov;
-
-	to_send = iov_buflen(orig_iov, iovcnt);
-	if (to_send == -1) {
-		errno = EINVAL;
-		return -1;
-	}
-
-	thistime = sys_writev(fd, orig_iov, iovcnt);
-	if ((thistime <= 0) || (thistime == to_send)) {
-		return thistime;
-	}
-	sent = thistime;
-
-	/*
-	 * We could not send everything in one call. Make a copy of iov that
-	 * we can mess with. We keep a copy of the array start in iov_copy for
-	 * the TALLOC_FREE, because we're going to modify iov later on,
-	 * discarding elements.
-	 */
-
-	memcpy(iov_copy, orig_iov, sizeof(struct iovec) * iovcnt);
-	iov = iov_copy;
-
-	while (sent < to_send) {
-		/*
-		 * We have to discard "thistime" bytes from the beginning
-		 * iov array, "thistime" contains the number of bytes sent
-		 * via writev last.
-		 */
-		while (thistime > 0) {
-			if (thistime < iov[0].iov_len) {
-				char *new_base =
-					(char *)iov[0].iov_base + thistime;
-				iov[0].iov_base = (void *)new_base;
-				iov[0].iov_len -= thistime;
-				break;
-			}
-			thistime -= iov[0].iov_len;
-			iov += 1;
-			iovcnt -= 1;
-		}
-
-		thistime = sys_writev(fd, iov, iovcnt);
-		if (thistime <= 0) {
-			break;
-		}
-		sent += thistime;
-	}
-
-	return sent;
-}
-
-/****************************************************************************
- Write data to a fd.
- NB. This can be called with a non-socket fd, don't add dependencies
- on socket calls.
-****************************************************************************/
-
-ssize_t write_data(int fd, const char *buffer, size_t N)
-{
-	struct iovec iov;
-
-	iov.iov_base = discard_const_p(void, buffer);
-	iov.iov_len = N;
-	return write_data_iov(fd, &iov, 1);
-}
-
-/****************************************************************************
  Send a keepalive packet (rfc1002).
 ****************************************************************************/
 
diff --git a/source3/modules/vfs_aio_fork.c b/source3/modules/vfs_aio_fork.c
index c2148a1..39334bc 100644
--- a/source3/modules/vfs_aio_fork.c
+++ b/source3/modules/vfs_aio_fork.c
@@ -27,6 +27,7 @@
 #include "lib/async_req/async_sock.h"
 #include "lib/util/tevent_unix.h"
 #include "lib/sys_rw.h"
+#include "lib/sys_rw_data.h"
 
 #if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && !defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
 # error Can not pass file descriptors
diff --git a/source3/modules/vfs_preopen.c b/source3/modules/vfs_preopen.c
index 612b025..cc38a90 100644
--- a/source3/modules/vfs_preopen.c
+++ b/source3/modules/vfs_preopen.c
@@ -21,6 +21,7 @@
 #include "includes.h"
 #include "system/filesys.h"
 #include "smbd/smbd.h"
+#include "lib/sys_rw_data.h"
 
 struct preopen_state;
 
diff --git a/source3/modules/vfs_smb_traffic_analyzer.c b/source3/modules/vfs_smb_traffic_analyzer.c
index 92a1762..06ff1f6 100644
--- a/source3/modules/vfs_smb_traffic_analyzer.c
+++ b/source3/modules/vfs_smb_traffic_analyzer.c
@@ -29,6 +29,7 @@
 #include "../librpc/gen_ndr/ndr_netlogon.h"
 #include "auth.h"
 #include "../lib/tsocket/tsocket.h"
+#include "lib/sys_rw_data.h"
 
 /* abstraction for the send_over_network function */
 enum sock_type {INTERNET_SOCKET = 0, UNIX_DOMAIN_SOCKET};
diff --git a/source3/nmbd/asyncdns.c b/source3/nmbd/asyncdns.c
index 4468c7b..5973c8e 100644
--- a/source3/nmbd/asyncdns.c
+++ b/source3/nmbd/asyncdns.c
@@ -19,6 +19,7 @@
 
 #include "includes.h"
 #include "nmbd/nmbd.h"
+#include "lib/sys_rw_data.h"
 
 /***************************************************************************
   Add a DNS result to the name cache.
diff --git a/source3/printing/printing.c b/source3/printing/printing.c
index 5d053cc..61afa28 100644
--- a/source3/printing/printing.c
+++ b/source3/printing/printing.c
@@ -36,6 +36,7 @@
 #include "messages.h"
 #include "util_tdb.h"
 #include "lib/param/loadparm.h"
+#include "lib/sys_rw_data.h"
 
 extern struct current_user current_user;
 extern userdom_struct current_user_info;
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index c7f0e9a..a761669 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -39,6 +39,7 @@
 #include "../libcli/security/dom_sid.h"
 #include "../libcli/security/security_token.h"
 #include "lib/id_cache.h"
+#include "lib/sys_rw_data.h"
 #include "serverid.h"
 #include "system/threads.h"
 
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index 83369f8..e7b12a5 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -43,6 +43,7 @@
 #include "../lib/tsocket/tsocket.h"
 #include "lib/tevent_wait.h"
 #include "libcli/smb/smb_signing.h"
+#include "lib/sys_rw_data.h"
 
 /****************************************************************************
  Ensure we check the path in *exactly* the same way as W2K for a findfirst/findnext
diff --git a/source3/smbd/smb2_read.c b/source3/smbd/smb2_read.c
index 470e496..4e974a2 100644
--- a/source3/smbd/smb2_read.c
+++ b/source3/smbd/smb2_read.c
@@ -26,6 +26,7 @@
 #include "libcli/security/security.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "rpc_server/srv_pipe_hnd.h"
+#include "lib/sys_rw_data.h"
 
 static struct tevent_req *smbd_smb2_read_send(TALLOC_CTX *mem_ctx,
 					      struct tevent_context *ev,
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index 2cd63e1..ebc8844 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -41,6 +41,7 @@
 #include "util_tdb.h"
 #include "../libcli/smb/read_smb.h"
 #include "../libcli/smb/smbXcli_base.h"
+#include "lib/sys_rw_data.h"
 
 extern char *optarg;
 extern int optind;
diff --git a/source3/utils/smbfilter.c b/source3/utils/smbfilter.c
index e06fee6..ff966a8 100644
--- a/source3/utils/smbfilter.c
+++ b/source3/utils/smbfilter.c
@@ -22,6 +22,7 @@
 #include "system/select.h"
 #include "../lib/util/select.h"
 #include "libsmb/nmblib.h"
+#include "lib/sys_rw_data.h"
 
 #define SECURITY_MASK 0
 #define SECURITY_SET  0
diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c
index 43a27b3..35838e6 100644
--- a/source3/winbindd/winbindd_dual.c
+++ b/source3/winbindd/winbindd_dual.c
@@ -39,6 +39,7 @@
 #include "../lib/util/tevent_unix.h"
 #include "lib/param/loadparm.h"
 #include "lib/sys_rw.h"
+#include "lib/sys_rw_data.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
diff --git a/source3/wscript_build b/source3/wscript_build
index 851b6ac..ade269b 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -261,8 +261,8 @@ bld.SAMBA3_SUBSYSTEM('KRBCLIENT',
                      public_deps='krb5samba k5crypto gssapi LIBTSOCKET CLDAP LIBNMB')
 
 bld.SAMBA3_LIBRARY('sys_rw',
-                   source='lib/sys_rw.c',
-                   deps='replace',
+                   source='lib/sys_rw.c lib/sys_rw_data.c',
+                   deps='replace iov_buf',
                    private_library=True)
 
 bld.SAMBA3_LIBRARY('iov_buf',
-- 
1.9.1


From 4c6b5676d4a331e8cbbc097107f1fbdeada4182d Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 14:29:26 +0000
Subject: [PATCH 05/41] lib: Make write_data take a const void *

This aligns it with write(2)

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/sys_rw_data.c | 2 +-
 source3/lib/sys_rw_data.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/source3/lib/sys_rw_data.c b/source3/lib/sys_rw_data.c
index f7bedb3..e4f9a16 100644
--- a/source3/lib/sys_rw_data.c
+++ b/source3/lib/sys_rw_data.c
@@ -97,7 +97,7 @@ ssize_t write_data_iov(int fd, const struct iovec *orig_iov, int iovcnt)
  on socket calls.
 ****************************************************************************/
 
-ssize_t write_data(int fd, const char *buffer, size_t n)
+ssize_t write_data(int fd, const void *buffer, size_t n)
 {
 	struct iovec iov;
 
diff --git a/source3/lib/sys_rw_data.h b/source3/lib/sys_rw_data.h
index fc97573..5d1995b 100644
--- a/source3/lib/sys_rw_data.h
+++ b/source3/lib/sys_rw_data.h
@@ -28,6 +28,6 @@
 struct iovec;
 
 ssize_t write_data_iov(int fd, const struct iovec *iov, int iovcnt);
-ssize_t write_data(int fd, const char *buffer, size_t n);
+ssize_t write_data(int fd, const void *buffer, size_t n);
 
 #endif
-- 
1.9.1


From d411627877abb4270473b39d52a6be4f22d87858 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 14:35:14 +0000
Subject: [PATCH 06/41] lib: Add a simple read_data call without NTSTATUS

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/sys_rw_data.c | 22 ++++++++++++++++++++++
 source3/lib/sys_rw_data.h |  1 +
 2 files changed, 23 insertions(+)

diff --git a/source3/lib/sys_rw_data.c b/source3/lib/sys_rw_data.c
index e4f9a16..353dbe7 100644
--- a/source3/lib/sys_rw_data.c
+++ b/source3/lib/sys_rw_data.c
@@ -105,3 +105,25 @@ ssize_t write_data(int fd, const void *buffer, size_t n)
 	iov.iov_len = n;
 	return write_data_iov(fd, &iov, 1);
 }
+
+/*
+ * Blocking read n bytes from a fd
+ */
+
+ssize_t read_data(int fd, void *buffer, size_t n)
+{
+	ssize_t nread;
+
+	nread = 0;
+
+	while (nread < n) {
+		ssize_t ret;
+		ret = sys_read(fd, ((char *)buffer) + nread, n - nread);
+		if (ret <= 0) {
+			return ret;
+		}
+		nread += ret;
+	}
+
+	return nread;
+}
diff --git a/source3/lib/sys_rw_data.h b/source3/lib/sys_rw_data.h
index 5d1995b..bda3795 100644
--- a/source3/lib/sys_rw_data.h
+++ b/source3/lib/sys_rw_data.h
@@ -29,5 +29,6 @@ struct iovec;
 
 ssize_t write_data_iov(int fd, const struct iovec *iov, int iovcnt);
 ssize_t write_data(int fd, const void *buffer, size_t n);
+ssize_t read_data(int fd, void *buffer, size_t n);
 
 #endif
-- 
1.9.1


From dcce64d02431f541e534c1cba86d7840a80973f8 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 14:41:42 +0000
Subject: [PATCH 07/41] smbd: Use read_data() in notify_inotify

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_inotify.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index 5ec6178..4b8d4a4 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -24,6 +24,7 @@
 #include "includes.h"
 #include "../librpc/gen_ndr/notify.h"
 #include "smbd/smbd.h"
+#include "lib/sys_rw_data.h"
 
 #ifdef HAVE_INOTIFY
 
@@ -198,7 +199,7 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
 	int bufsize = 0;
 	struct inotify_event *e0, *e;
 	uint32_t prev_cookie=0;
-	NTSTATUS status;
+	ssize_t ret;
 
 	/*
 	  we must use FIONREAD as we cannot predict the length of the
@@ -216,10 +217,10 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
 	if (e == NULL) return;
 	((uint8_t *)e)[bufsize] = '\0';
 
-	status = read_data_ntstatus(in->fd, (char *)e0, bufsize);
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(0,("Failed to read all inotify data - %s\n",
-			nt_errstr(status)));
+	ret = read_data(in->fd, e0, bufsize);
+	if (ret != bufsize) {
+		DEBUG(0, ("Failed to read all inotify data - %s\n",
+			  strerror(errno)));
 		talloc_free(e0);
 		/* the inotify fd will now be out of sync,
 		 * can't keep reading data off it */
-- 
1.9.1


From 1137c10464f04b821c07512afd9868441a5832df Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 15:55:25 +0000
Subject: [PATCH 08/41] libsmb: Remove an unused variable

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/libsmb/cliconnect.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 789a85d..2fdec37 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -1732,7 +1732,6 @@ static struct tevent_req *cli_session_setup_spnego_send(
 	char *OIDs[ASN1_MAX_OIDS];
 	int i;
 	const DATA_BLOB *server_blob;
-	NTSTATUS status;
 
 	req = tevent_req_create(mem_ctx, &state,
 				struct cli_session_setup_spnego_state);
-- 
1.9.1


From 159d17cc2b2cb70d876a426039a8448f9f8fd820 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 19 Nov 2014 15:55:25 +0000
Subject: [PATCH 09/41] libsmb: Remove an unused variable

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

diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 2fdec37..2b1e2ec 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -3370,8 +3370,6 @@ static void cli_full_connection_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
-	struct cli_full_connection_state *state = tevent_req_data(
-		req, struct cli_full_connection_state);
 	NTSTATUS status;
 
 	status = cli_tree_connect_recv(subreq);
-- 
1.9.1


From 1628b845adeef69c8741ef6e24c153cc0c92812b Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 26 Oct 2014 09:13:41 +0000
Subject: [PATCH 10/41] notify_inotify: Simplify filter_match

Early returns make code simpler

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_inotify.c | 39 +++++++++++++++++++--------------------
 1 file changed, 19 insertions(+), 20 deletions(-)

diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index 4b8d4a4..dcdf68c 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -75,6 +75,8 @@ static int inotify_destructor(struct inotify_private *in)
 static bool filter_match(struct inotify_watch_context *w,
 			 struct inotify_event *e)
 {
+	bool ok;
+
 	DEBUG(10, ("filter_match: e->mask=%x, w->mask=%x, w->filter=%x\n",
 		   e->mask, w->mask, w->filter));
 
@@ -86,28 +88,25 @@ static bool filter_match(struct inotify_watch_context *w,
 
 	/* SMB separates the filters for files and directories */
 	if (e->mask & IN_ISDIR) {
-		if ((w->filter & FILE_NOTIFY_CHANGE_DIR_NAME) == 0) {
-			return False;
-		}
-	} else {
-		if ((e->mask & IN_ATTRIB) &&
-		    (w->filter & (FILE_NOTIFY_CHANGE_ATTRIBUTES|
-				  FILE_NOTIFY_CHANGE_LAST_WRITE|
-				  FILE_NOTIFY_CHANGE_LAST_ACCESS|
-				  FILE_NOTIFY_CHANGE_EA|
-				  FILE_NOTIFY_CHANGE_SECURITY))) {
-			return True;
-		}
-		if ((e->mask & IN_MODIFY) && 
-		    (w->filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)) {
-			return True;
-		}
-		if ((w->filter & FILE_NOTIFY_CHANGE_FILE_NAME) == 0) {
-			return False;
-		}
+		ok = ((w->filter & FILE_NOTIFY_CHANGE_DIR_NAME) != 0);
+		return ok;
+	}
+
+	if ((e->mask & IN_ATTRIB) &&
+	    (w->filter & (FILE_NOTIFY_CHANGE_ATTRIBUTES|
+			  FILE_NOTIFY_CHANGE_LAST_WRITE|
+			  FILE_NOTIFY_CHANGE_LAST_ACCESS|
+			  FILE_NOTIFY_CHANGE_EA|
+			  FILE_NOTIFY_CHANGE_SECURITY))) {
+		return True;
+	}
+	if ((e->mask & IN_MODIFY) &&
+	    (w->filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)) {
+		return True;
 	}
 
-	return True;
+	ok = ((w->filter & FILE_NOTIFY_CHANGE_FILE_NAME) != 0);
+	return ok;
 }
 
 
-- 
1.9.1


From d044ca419890b957f76580906375177059ca5ac7 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 27 Oct 2014 10:26:46 +0000
Subject: [PATCH 11/41] smbd: Move lp_parm_bool out of notify_inotify.c

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/modules/vfs_default.c | 3 +++
 source3/smbd/notify_inotify.c | 4 ----
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index ab2ba89..482c974 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2102,6 +2102,9 @@ static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle,
 	 */
 #ifdef HAVE_INOTIFY
 	if (lp_kernel_change_notify(vfs_handle->conn->params)) {
+		if (!lp_parm_bool(-1, "notify", "inotify", True)) {
+			return NT_STATUS_INVALID_SYSTEM_SERVICE;
+		}
 		return inotify_watch(ctx, path, filter, subdir_filter,
 				     callback, private_data, handle);
 	}
diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index dcdf68c..adcf9fd 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -250,10 +250,6 @@ static NTSTATUS inotify_setup(struct sys_notify_context *ctx)
 {
 	struct inotify_private *in;
 
-	if (!lp_parm_bool(-1, "notify", "inotify", True)) {
-		return NT_STATUS_INVALID_SYSTEM_SERVICE;
-	}
-
 	in = talloc(ctx, struct inotify_private);
 	NT_STATUS_HAVE_NO_MEMORY(in);
 	in->fd = inotify_init();
-- 
1.9.1


From 7e102d3681426dd0ac0241121231117fff17fca2 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 27 Oct 2014 13:07:03 +0000
Subject: [PATCH 12/41] notify_inotify: Add a NULL check

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

diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index adcf9fd..e194b91 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -249,6 +249,7 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
 static NTSTATUS inotify_setup(struct sys_notify_context *ctx)
 {
 	struct inotify_private *in;
+	struct tevent_fd *fde;
 
 	in = talloc(ctx, struct inotify_private);
 	NT_STATUS_HAVE_NO_MEMORY(in);
@@ -265,7 +266,13 @@ static NTSTATUS inotify_setup(struct sys_notify_context *ctx)
 	talloc_set_destructor(in, inotify_destructor);
 
 	/* add a event waiting for the inotify fd to be readable */
-	tevent_add_fd(ctx->ev, in, in->fd, TEVENT_FD_READ, inotify_handler, in);
+	fde = tevent_add_fd(ctx->ev, in, in->fd, TEVENT_FD_READ,
+			    inotify_handler, in);
+	if (fde == NULL) {
+		ctx->private_data = NULL;
+		TALLOC_FREE(in);
+		return NT_STATUS_NO_MEMORY;
+	}
 
 	return NT_STATUS_OK;
 }
-- 
1.9.1


From 8a55c047e282dbe5b3a5516c298fe62315c3e506 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 27 Oct 2014 13:09:44 +0000
Subject: [PATCH 13/41] notify_inotify: Make inotify_setup return 0/errno

This gets rid of one NT_STATUS_HAVE_NO_MEMORY :-)

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_inotify.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index e194b91..3ec9708 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -246,18 +246,22 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
   setup the inotify handle - called the first time a watch is added on
   this context
 */
-static NTSTATUS inotify_setup(struct sys_notify_context *ctx)
+static int inotify_setup(struct sys_notify_context *ctx)
 {
 	struct inotify_private *in;
 	struct tevent_fd *fde;
 
 	in = talloc(ctx, struct inotify_private);
-	NT_STATUS_HAVE_NO_MEMORY(in);
+	if (in == NULL) {
+		return ENOMEM;
+	}
+
 	in->fd = inotify_init();
 	if (in->fd == -1) {
-		DEBUG(0,("Failed to init inotify - %s\n", strerror(errno)));
+		int ret = errno;
+		DEBUG(0, ("Failed to init inotify - %s\n", strerror(ret)));
 		talloc_free(in);
-		return map_nt_error_from_unix(errno);
+		return ret;
 	}
 	in->ctx = ctx;
 	in->watches = NULL;
@@ -271,10 +275,9 @@ static NTSTATUS inotify_setup(struct sys_notify_context *ctx)
 	if (fde == NULL) {
 		ctx->private_data = NULL;
 		TALLOC_FREE(in);
-		return NT_STATUS_NO_MEMORY;
+		return ENOMEM;
 	}
-
-	return NT_STATUS_OK;
+	return 0;
 }
 
 
@@ -356,9 +359,11 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 
 	/* maybe setup the inotify fd */
 	if (ctx->private_data == NULL) {
-		NTSTATUS status;
-		status = inotify_setup(ctx);
-		NT_STATUS_NOT_OK_RETURN(status);
+		int ret;
+		ret = inotify_setup(ctx);
+		if (ret != 0) {
+			return map_nt_error_from_unix(ret);
+		}
 	}
 
 	in = talloc_get_type(ctx->private_data, struct inotify_private);
-- 
1.9.1


From e8a3b3c23e00e6f1b2e95983f7e53732c30a652e Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 27 Oct 2014 13:15:12 +0000
Subject: [PATCH 14/41] notify_inotify: Slightly simplify inotify_watch

tallocing first avoids having to call inotify_rm_watch

This even fixes a real error: We share inotifies between different instances,
so the rm_watch in the error paths destroys other legitimate watches

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_inotify.c | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index 3ec9708..be3a2a8 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -351,7 +351,6 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 		       void *handle_p)
 {
 	struct inotify_private *in;
-	int wd;
 	uint32_t mask;
 	struct inotify_watch_context *w;
 	uint32_t orig_filter = *filter;
@@ -378,38 +377,37 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 	   watch descriptor for multiple watches on the same path */
 	mask |= (IN_MASK_ADD | IN_ONLYDIR);
 
-	/* get a new watch descriptor for this path */
-	wd = inotify_add_watch(in->fd, path, mask);
-	if (wd == -1) {
-		*filter = orig_filter;
-		DEBUG(1, ("inotify_add_watch returned %s\n", strerror(errno)));
-		return map_nt_error_from_unix(errno);
-	}
-
-	DEBUG(10, ("inotify_add_watch for %s mask %x returned wd %d\n",
-		   path, mask, wd));
-
 	w = talloc(in, struct inotify_watch_context);
 	if (w == NULL) {
-		inotify_rm_watch(in->fd, wd);
 		*filter = orig_filter;
 		return NT_STATUS_NO_MEMORY;
 	}
 
 	w->in = in;
-	w->wd = wd;
 	w->callback = callback;
 	w->private_data = private_data;
 	w->mask = mask;
 	w->filter = orig_filter;
 	w->path = talloc_strdup(w, path);
 	if (w->path == NULL) {
-		inotify_rm_watch(in->fd, wd);
 		*filter = orig_filter;
 		TALLOC_FREE(w);
 		return NT_STATUS_NO_MEMORY;
 	}
 
+	/* get a new watch descriptor for this path */
+	w->wd = inotify_add_watch(in->fd, path, mask);
+	if (w->wd == -1) {
+		int err = errno;
+		*filter = orig_filter;
+		TALLOC_FREE(w);
+		DEBUG(1, ("inotify_add_watch returned %s\n", strerror(err)));
+		return map_nt_error_from_unix(err);
+	}
+
+	DEBUG(10, ("inotify_add_watch for %s mask %x returned wd %d\n",
+		   path, mask, w->wd));
+
 	(*handle) = w;
 
 	DLIST_ADD(in->watches, w);
-- 
1.9.1


From 7befbd9bf8abb66e5e9176787f292f44889e83e4 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 27 Oct 2014 13:20:15 +0000
Subject: [PATCH 15/41] notify_inotify: Slightly simplify watch_destructor

Another case of an early return

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify_inotify.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index be3a2a8..2ffbef0 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -320,17 +320,19 @@ static int watch_destructor(struct inotify_watch_context *w)
 	int wd = w->wd;
 	DLIST_REMOVE(w->in->watches, w);
 
-	/* only rm the watch if its the last one with this wd */
 	for (w=in->watches;w;w=w->next) {
-		if (w->wd == wd) break;
-	}
-	if (w == NULL) {
-		DEBUG(10, ("Deleting inotify watch %d\n", wd));
-		if (inotify_rm_watch(in->fd, wd) == -1) {
-			DEBUG(1, ("inotify_rm_watch returned %s\n",
-				  strerror(errno)));
+		if (w->wd == wd) {
+			/*
+			 * Another inotify_watch_context listens on this path,
+			 * leave the kernel level watch in place
+			 */
+			return 0;
 		}
+	}
 
+	DEBUG(10, ("Deleting inotify watch %d\n", wd));
+	if (inotify_rm_watch(in->fd, wd) == -1) {
+		DEBUG(1, ("inotify_rm_watch returned %s\n", strerror(errno)));
 	}
 	return 0;
 }
-- 
1.9.1


From ac238815af0a3a1a86fb885c3b3a6c2c15921ca1 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 27 Oct 2014 13:26:35 +0000
Subject: [PATCH 16/41] notify_inotify: Make inotify_watch return 0/errno

More like a cleanup, but I want to use inotify_watch in another context
in the future where I don't want to have NTSTATUS around.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/modules/vfs_default.c |  9 +++++++--
 source3/smbd/notify_inotify.c | 30 +++++++++++++++---------------
 source3/smbd/proto.h          | 18 +++++++++---------
 3 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 482c974..64509ff 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2102,11 +2102,16 @@ static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle,
 	 */
 #ifdef HAVE_INOTIFY
 	if (lp_kernel_change_notify(vfs_handle->conn->params)) {
+		int ret;
 		if (!lp_parm_bool(-1, "notify", "inotify", True)) {
 			return NT_STATUS_INVALID_SYSTEM_SERVICE;
 		}
-		return inotify_watch(ctx, path, filter, subdir_filter,
-				     callback, private_data, handle);
+		ret = inotify_watch(ctx, path, filter, subdir_filter,
+				    callback, private_data, handle);
+		if (ret != 0) {
+			return map_nt_error_from_unix(ret);
+		}
+		return NT_STATUS_OK;
 	}
 #endif
 	/*
diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index 2ffbef0..8606f9a 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -342,15 +342,15 @@ static int watch_destructor(struct inotify_watch_context *w)
   add a watch. The watch is removed when the caller calls
   talloc_free() on *handle
 */
-NTSTATUS inotify_watch(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 inotify_watch(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 inotify_private *in;
 	uint32_t mask;
@@ -363,7 +363,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 		int ret;
 		ret = inotify_setup(ctx);
 		if (ret != 0) {
-			return map_nt_error_from_unix(ret);
+			return ret;
 		}
 	}
 
@@ -372,7 +372,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 	mask = inotify_map(filter);
 	if (mask == 0) {
 		/* this filter can't be handled by inotify */
-		return NT_STATUS_INVALID_PARAMETER;
+		return EINVAL;
 	}
 
 	/* using IN_MASK_ADD allows us to cope with inotify() returning the same
@@ -382,7 +382,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 	w = talloc(in, struct inotify_watch_context);
 	if (w == NULL) {
 		*filter = orig_filter;
-		return NT_STATUS_NO_MEMORY;
+		return ENOMEM;
 	}
 
 	w->in = in;
@@ -394,7 +394,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 	if (w->path == NULL) {
 		*filter = orig_filter;
 		TALLOC_FREE(w);
-		return NT_STATUS_NO_MEMORY;
+		return ENOMEM;
 	}
 
 	/* get a new watch descriptor for this path */
@@ -404,7 +404,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 		*filter = orig_filter;
 		TALLOC_FREE(w);
 		DEBUG(1, ("inotify_add_watch returned %s\n", strerror(err)));
-		return map_nt_error_from_unix(err);
+		return err;
 	}
 
 	DEBUG(10, ("inotify_add_watch for %s mask %x returned wd %d\n",
@@ -417,7 +417,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx,
 	/* the caller frees the handle to stop watching */
 	talloc_set_destructor(w, watch_destructor);
 
-	return NT_STATUS_OK;
+	return 0;
 }
 
 #endif
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 1080895..3a8e5ea 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -525,15 +525,15 @@ struct sys_notify_context *sys_notify_context_create(TALLOC_CTX *mem_ctx,
 
 /* The following definitions come from smbd/notify_inotify.c  */
 
-NTSTATUS inotify_watch(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 inotify_watch(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  */
 
-- 
1.9.1


From dd67fd416bb7766f703a24c4ae132a3a49c21845 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 5 Nov 2014 11:44:42 +0000
Subject: [PATCH 17/41] notify: Add "dir" to notify_event

"notify_event" only reports names relative to some path that is only
implicitly known via "private_data". Right now "private_data" is the fsp
of the directory holding this notify. I want to use inotify_watch in a
notify-daemon that does not have a fsp available and has more problems
getting the path right out of "private_data". notify_inotify has the
directory under which the event happened available, so make it known to
the callback. Right now no caller uses it yet.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 librpc/idl/notify.idl          |  1 +
 source3/smbd/notify_inotify.c  |  2 ++
 source3/smbd/notify_internal.c | 10 +++++++---
 source4/ntvfs/common/notify.c  |  1 +
 source4/ntvfs/sysdep/inotify.c |  2 ++
 5 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/librpc/idl/notify.idl b/librpc/idl/notify.idl
index ec81e8c..66422ec 100644
--- a/librpc/idl/notify.idl
+++ b/librpc/idl/notify.idl
@@ -65,6 +65,7 @@ interface notify
 	/* structure sent between servers in notify messages */
 	typedef [public] struct {
 		uint32 action;
+		utf8string dir;
 		utf8string path;
 		pointer private_data;
 	} notify_event;
diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index 8606f9a..ba3cc45 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -163,6 +163,7 @@ static void inotify_dispatch(struct inotify_private *in,
 	for (w=in->watches;w;w=next) {
 		next = w->next;
 		if (w->wd == e->wd && filter_match(w, e)) {
+			ne.dir = w->path;
 			w->callback(in->ctx, w->private_data, &ne);
 		}
 	}
@@ -182,6 +183,7 @@ static void inotify_dispatch(struct inotify_private *in,
 		next = w->next;
 		if (w->wd == e->wd && filter_match(w, e) &&
 		    !(w->filter & FILE_NOTIFY_CHANGE_CREATION)) {
+			ne.dir = w->path;
 			w->callback(in->ctx, w->private_data, &ne);
 		}
 	}
diff --git a/source3/smbd/notify_internal.c b/source3/smbd/notify_internal.c
index 9d54891..9c0b190 100644
--- a/source3/smbd/notify_internal.c
+++ b/source3/smbd/notify_internal.c
@@ -831,9 +831,13 @@ static void notify_handler(struct messaging_context *msg_ctx,
 	}
 
 	m = (struct notify_msg *)data->data;
-	e.action = m->action;
-	e.path = m->path;
-	e.private_data = m->private_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) {
diff --git a/source4/ntvfs/common/notify.c b/source4/ntvfs/common/notify.c
index 57142c7..61da0b8 100644
--- a/source4/ntvfs/common/notify.c
+++ b/source4/ntvfs/common/notify.c
@@ -550,6 +550,7 @@ static void notify_send(struct notify_context *notify, struct notify_entry *e,
 	TALLOC_CTX *tmp_ctx;
 
 	ev.action = action;
+	ev.dir = discard_const_p(char, "");
 	ev.path = path;
 	ev.private_data = e->private_data;
 
diff --git a/source4/ntvfs/sysdep/inotify.c b/source4/ntvfs/sysdep/inotify.c
index 0680b4b..091ca1d 100644
--- a/source4/ntvfs/sysdep/inotify.c
+++ b/source4/ntvfs/sysdep/inotify.c
@@ -144,6 +144,7 @@ static void inotify_dispatch(struct inotify_private *in,
 	for (w=in->watches;w;w=next) {
 		next = w->next;
 		if (w->wd == e->wd && filter_match(w, e)) {
+			ne.dir = w->path;
 			w->callback(in->ctx, w->private_data, &ne);
 		}
 	}
@@ -163,6 +164,7 @@ static void inotify_dispatch(struct inotify_private *in,
 		next = w->next;
 		if (w->wd == e->wd && filter_match(w, e) &&
 		    !(w->filter & FILE_NOTIFY_CHANGE_CREATION)) {
+			ne.dir = w->path;
 			w->callback(in->ctx, w->private_data, &ne);
 		}
 	}
-- 
1.9.1


From 00e10834d1ea20c88a002948f3cb68933b6fe206 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 5 Nov 2014 12:18:31 +0000
Subject: [PATCH 18/41] notify_inotify: inotify_watch now takes a mem_ctx

This will make it easier to integrate into proper memory hierarchies.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/modules/vfs_default.c | 9 ++++++++-
 source3/smbd/notify_inotify.c | 5 +++--
 source3/smbd/proto.h          | 3 ++-
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 64509ff..6bd888a 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2106,7 +2106,14 @@ static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle,
 		if (!lp_parm_bool(-1, "notify", "inotify", True)) {
 			return NT_STATUS_INVALID_SYSTEM_SERVICE;
 		}
-		ret = inotify_watch(ctx, path, filter, subdir_filter,
+		/*
+		 * "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);
diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index ba3cc45..d000efe 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -344,7 +344,8 @@ static int watch_destructor(struct inotify_watch_context *w)
   add a watch. The watch is removed when the caller calls
   talloc_free() on *handle
 */
-int inotify_watch(struct sys_notify_context *ctx,
+int inotify_watch(TALLOC_CTX *mem_ctx,
+		  struct sys_notify_context *ctx,
 		  const char *path,
 		  uint32_t *filter,
 		  uint32_t *subdir_filter,
@@ -381,7 +382,7 @@ int inotify_watch(struct sys_notify_context *ctx,
 	   watch descriptor for multiple watches on the same path */
 	mask |= (IN_MASK_ADD | IN_ONLYDIR);
 
-	w = talloc(in, struct inotify_watch_context);
+	w = talloc(mem_ctx, struct inotify_watch_context);
 	if (w == NULL) {
 		*filter = orig_filter;
 		return ENOMEM;
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 3a8e5ea..e2e2895 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -525,7 +525,8 @@ struct sys_notify_context *sys_notify_context_create(TALLOC_CTX *mem_ctx,
 
 /* The following definitions come from smbd/notify_inotify.c  */
 
-int inotify_watch(struct sys_notify_context *ctx,
+int inotify_watch(TALLOC_CTX *mem_ctx,
+		  struct sys_notify_context *ctx,
 		  const char *path,
 		  uint32_t *filter,
 		  uint32_t *subdir_filter,
-- 
1.9.1


From 9c91422d654fad0b59facfaf4fd761198dbdf405 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 18 Nov 2014 11:28:20 +0000
Subject: [PATCH 19/41] smbd: Compile notify_inotify only if available

---
 source3/smbd/notify_inotify.c | 4 ----
 source3/wscript_build         | 8 ++++++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index d000efe..abe3124 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -26,8 +26,6 @@
 #include "smbd/smbd.h"
 #include "lib/sys_rw_data.h"
 
-#ifdef HAVE_INOTIFY
-
 #include <sys/inotify.h>
 
 /* glibc < 2.5 headers don't have these defines */
@@ -422,5 +420,3 @@ int inotify_watch(TALLOC_CTX *mem_ctx,
 
 	return 0;
 }
-
-#endif
diff --git a/source3/wscript_build b/source3/wscript_build
index ade269b..069d49e 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -514,6 +514,11 @@ bld.SAMBA3_SUBSYSTEM('sysquotas',
                      allow_warnings=True,
                      deps='samba3-util samba-util')
 
+NOTIFY_SOURCES=''
+
+if bld.CONFIG_SET("HAVE_INOTIFY"):
+    NOTIFY_SOURCES += ' smbd/notify_inotify.c'
+
 bld.SAMBA3_LIBRARY('smbd_base',
                    source='''
                    smbd/server_reload.c
@@ -603,9 +608,8 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    smbd/oplock_irix.c
                    smbd/oplock_linux.c
                    smbd/notify.c
-                   smbd/notify_inotify.c
                    smbd/notify_internal.c
-                   smbd/build_options.c''',
+                   smbd/build_options.c''' + NOTIFY_SOURCES,
                    deps='''
                    talloc
                    tevent
-- 
1.9.1


From 21e7a173fb8f0cce56ac532cae88dd5d27891c60 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Sun, 14 Sep 2014 13:10:58 +0200
Subject: [PATCH 20/41] lib: Add map_unix_error_from_tdb

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/util/util_tdb.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/util/util_tdb.h |  7 ++++++-
 2 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/lib/util/util_tdb.c b/lib/util/util_tdb.c
index 811c2a4..f84ab33 100644
--- a/lib/util/util_tdb.c
+++ b/lib/util/util_tdb.c
@@ -426,3 +426,60 @@ NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err)
 	};
 	return result;
 }
+
+int map_unix_error_from_tdb(enum TDB_ERROR err)
+{
+	int result = EINVAL;
+
+	switch (err) {
+	case TDB_SUCCESS:
+		result = 0;
+		break;
+	case TDB_ERR_CORRUPT:
+		result = EILSEQ;
+		break;
+	case TDB_ERR_IO:
+		result = EIO;
+		break;
+	case TDB_ERR_OOM:
+		result = ENOMEM;
+		break;
+	case TDB_ERR_EXISTS:
+		result = EEXIST;
+		break;
+
+	case TDB_ERR_LOCK:
+		/*
+		 * TDB_ERR_LOCK is very broad, we could for example
+		 * distinguish between fcntl locks and invalid lock
+		 * sequences. EWOULDBLOCK is wrong, but there is no real
+		 * generic lock error code in errno.h
+		 */
+		result = EWOULDBLOCK;
+		break;
+
+	case TDB_ERR_NOLOCK:
+	case TDB_ERR_LOCK_TIMEOUT:
+		/*
+		 * These two ones in the enum are not actually used
+		 */
+		result = ENOLCK;
+		break;
+	case TDB_ERR_NOEXIST:
+		result = ENOENT;
+		break;
+	case TDB_ERR_EINVAL:
+		result = EINVAL;
+		break;
+	case TDB_ERR_RDONLY:
+		result = EROFS;
+		break;
+	case TDB_ERR_NESTING:
+		/*
+		 * Well, this db is already busy...
+		 */
+		result = EBUSY;
+		break;
+	};
+	return result;
+}
diff --git a/lib/util/util_tdb.h b/lib/util/util_tdb.h
index 12c472c..7a457f7 100644
--- a/lib/util/util_tdb.h
+++ b/lib/util/util_tdb.h
@@ -139,5 +139,10 @@ int tdb_traverse_delete_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA d
 
 NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err);
 
-#endif /* _____LIB_UTIL_UTIL_TDB_H__ */
+/****************************************************************************
+ Return an errno from a TDB_ERROR
+****************************************************************************/
 
+int map_unix_error_from_tdb(enum TDB_ERROR err);
+
+#endif /* _____LIB_UTIL_UTIL_TDB_H__ */
-- 
1.9.1


From d140ae8d46e3ee33c1e1f1342dbc7d496db13fb6 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 21 Oct 2014 10:39:53 +0000
Subject: [PATCH 21/41] lib: Add tdb_fetch_talloc

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/util/util_tdb.c | 33 +++++++++++++++++++++++++++++++++
 lib/util/util_tdb.h |  3 +++
 2 files changed, 36 insertions(+)

diff --git a/lib/util/util_tdb.c b/lib/util/util_tdb.c
index f84ab33..9bf18dc 100644
--- a/lib/util/util_tdb.c
+++ b/lib/util/util_tdb.c
@@ -483,3 +483,36 @@ int map_unix_error_from_tdb(enum TDB_ERROR err)
 	};
 	return result;
 }
+
+struct tdb_fetch_talloc_state {
+	TALLOC_CTX *mem_ctx;
+	uint8_t *buf;
+};
+
+static int tdb_fetch_talloc_parser(TDB_DATA key, TDB_DATA data,
+                                   void *private_data)
+{
+	struct tdb_fetch_talloc_state *state = private_data;
+	state->buf = talloc_memdup(state->mem_ctx, data.dptr, data.dsize);
+	return 0;
+}
+
+int tdb_fetch_talloc(struct tdb_context *tdb, TDB_DATA key,
+		     TALLOC_CTX *mem_ctx, uint8_t **buf)
+{
+	struct tdb_fetch_talloc_state state = { .mem_ctx = mem_ctx };
+	int ret;
+
+	ret = tdb_parse_record(tdb, key, tdb_fetch_talloc_parser, &state);
+	if (ret == -1) {
+		enum TDB_ERROR err = tdb_error(tdb);
+		return map_unix_error_from_tdb(err);
+	}
+
+	if (state.buf == NULL) {
+		return ENOMEM;
+	}
+
+	*buf = state.buf;
+	return 0;
+}
diff --git a/lib/util/util_tdb.h b/lib/util/util_tdb.h
index 7a457f7..3b50789 100644
--- a/lib/util/util_tdb.h
+++ b/lib/util/util_tdb.h
@@ -145,4 +145,7 @@ NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err);
 
 int map_unix_error_from_tdb(enum TDB_ERROR err);
 
+int tdb_fetch_talloc(struct tdb_context *tdb, TDB_DATA key,
+		     TALLOC_CTX *mem_ctx, uint8_t **buf);
+
 #endif /* _____LIB_UTIL_UTIL_TDB_H__ */
-- 
1.9.1


From 3371604fe65762207b31ea67e0621597e6aba88b Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 15 Oct 2014 13:26:51 +0000
Subject: [PATCH 22/41] lib: Add "strv" string handling routines

This is a little set of routines designed after the glibc argz
routines. It is supposed to eventually replace our inefficient string_list
routines. A talloc blob is an array of strings separated by the \0
character. See argz(3) on a Linux system for the ideas where this came
from. Based on talloc strv is simpler because talloc knows about the
size of the blob, so we don't have to explicitly maintain it.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/util/strv.c        | 141 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/util/strv.h        |  32 +++++++++++
 lib/util/wscript_build |   5 ++
 3 files changed, 178 insertions(+)
 create mode 100644 lib/util/strv.c
 create mode 100644 lib/util/strv.h

diff --git a/lib/util/strv.c b/lib/util/strv.c
new file mode 100644
index 0000000..2acd145
--- /dev/null
+++ b/lib/util/strv.c
@@ -0,0 +1,141 @@
+/*
+ * String Vector functions modeled after glibc argv_* functions
+ *
+ * Copyright Volker Lendecke <vl at samba.org> 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 "strv.h"
+#include "talloc.h"
+#include <string.h>
+
+static int _strv_append(TALLOC_CTX *mem_ctx, char **dst, const char *src,
+			size_t srclen)
+{
+	size_t dstlen = talloc_array_length(*dst);
+	size_t newlen = dstlen + srclen;
+	char *new_dst;
+
+	if ((newlen < srclen) || (newlen < dstlen)) {
+		return ERANGE;
+	}
+
+	new_dst = talloc_realloc(mem_ctx, *dst, char, newlen);
+	if (new_dst == NULL) {
+		return ENOMEM;
+	}
+	memcpy(&new_dst[dstlen], src, srclen);
+
+	*dst = new_dst;
+	return 0;
+}
+
+int strv_add(TALLOC_CTX *mem_ctx, char **strv, const char *string)
+{
+	return _strv_append(mem_ctx, strv, string, strlen(string)+1);
+}
+
+int strv_append(TALLOC_CTX *mem_ctx, char **strv, const char *src)
+{
+	return _strv_append(mem_ctx, strv, src, talloc_array_length(src));
+}
+
+static bool strv_valid_entry(const char *strv, const char *entry,
+			     size_t *strv_len, size_t *entry_len)
+{
+	size_t len;
+
+	len = talloc_array_length(strv);
+	if (len == 0) {
+		return false;
+	}
+	if (strv[len-1] != '\0') {
+		return false;
+	}
+
+	if (entry < strv) {
+		return false;
+	}
+	if (entry >= (strv+len)) {
+		return false;
+	}
+
+	*strv_len = len;
+	*entry_len = strlen(entry);
+
+	return true;
+}
+
+char *strv_next(char *strv, const char *entry)
+{
+	size_t len, entry_len;
+	char *result;
+
+	if (!strv_valid_entry(strv, entry, &len, &entry_len)) {
+		return NULL;
+	}
+	result = &strv[entry - strv]; /* avoid const problems with this stmt */
+	result += entry_len + 1;
+
+	if (result >= (strv + len)) {
+		return NULL;
+	}
+	return result;
+}
+
+size_t strv_count(char *strv)
+{
+	char *entry;
+	size_t count = 0;
+
+	for (entry = strv; entry != NULL; entry = strv_next(strv, entry)) {
+		count += 1;
+	}
+
+	return count;
+}
+
+char *strv_find(char *strv, const char *entry)
+{
+	char *e = NULL;
+
+	while ((e = strv_next(strv, e)) != NULL) {
+		if (strcmp(e, entry) == 0) {
+			return e;
+		}
+	}
+
+	return NULL;
+}
+
+void strv_delete(char **strv, char *entry)
+{
+	size_t len, entry_len;
+
+	if (entry == NULL) {
+		return;
+	}
+
+	if (!strv_valid_entry(*strv, entry, &len, &entry_len)) {
+		return;
+	}
+	entry_len += 1;
+
+	memmove(entry, entry+entry_len,
+		len - entry_len - (entry - *strv));
+
+	*strv = talloc_realloc(NULL, *strv, char, len - entry_len);
+}
diff --git a/lib/util/strv.h b/lib/util/strv.h
new file mode 100644
index 0000000..843ce5c
--- /dev/null
+++ b/lib/util/strv.h
@@ -0,0 +1,32 @@
+/*
+ * String Vector functions modeled after glibc argv_* functions
+ *
+ * Copyright Volker Lendecke <vl at samba.org> 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 _STRV_H_
+#define _STRV_H_
+
+#include "talloc.h"
+
+int strv_add(TALLOC_CTX *mem_ctx, char **strv, const char *string);
+int strv_append(TALLOC_CTX *mem_ctx, char **strv, const char *src);
+char *strv_next(char *strv, const char *entry);
+char *strv_find(char *strv, const char *entry);
+size_t strv_count(char *strv);
+void strv_delete(char **strv, char *entry);
+
+#endif
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index cd23231..83d0bf1 100755
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -15,6 +15,11 @@ bld.SAMBA_SUBSYSTEM('tiniparser',
                     deps='tini',
                     local_include=False)
 
+bld.SAMBA_SUBSYSTEM('strv',
+                    source='strv.c',
+                    deps='talloc',
+                    local_include=False)
+
 bld.SAMBA_SUBSYSTEM('close-low-fd',
                     source='close_low_fd.c',
                     deps='replace',
-- 
1.9.1


From e6068aa619f971e5a3af2e15d7b12e0d7b5f1f10 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 15 Oct 2014 16:14:42 +0000
Subject: [PATCH 23/41] lib: Add server_id_db

This is a mapping from names to server_ids. In the future, this will
replace the namedb in source4/messaging.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/util/server_id_db.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/util/server_id_db.h |  47 ++++++++
 lib/util/wscript_build  |   5 +
 3 files changed, 360 insertions(+)
 create mode 100644 lib/util/server_id_db.c
 create mode 100644 lib/util/server_id_db.h

diff --git a/lib/util/server_id_db.c b/lib/util/server_id_db.c
new file mode 100644
index 0000000..7f5b055
--- /dev/null
+++ b/lib/util/server_id_db.c
@@ -0,0 +1,308 @@
+/*
+ * Map names to server_ids
+ *
+ * Copyright Volker Lendecke <vl at samba.org> 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 "system/filesys.h"
+#include "lib/util/server_id_db.h"
+#include "lib/tdb_wrap/tdb_wrap.h"
+#include "lib/util/strv.h"
+#include "lib/util/util_tdb.h"
+#include "lib/util/samba_util.h"
+
+static TDB_DATA talloc_tdb_data(void *ptr)
+{
+	return (TDB_DATA) { .dptr = ptr, .dsize = talloc_get_size(ptr) };
+}
+
+struct server_id_db {
+	struct server_id pid;
+	struct tdb_wrap *tdb;
+	char *names;
+};
+
+static int server_id_db_destructor(struct server_id_db *db);
+
+struct server_id_db *server_id_db_init(TALLOC_CTX *mem_ctx,
+				       struct server_id pid,
+				       const char *base_path,
+				       int hash_size, int tdb_flags)
+{
+	struct server_id_db *db;
+	size_t pathlen = strlen(base_path) + 11;
+	char path[pathlen];
+
+	db = talloc(mem_ctx, struct server_id_db);
+	if (db == NULL) {
+		return NULL;
+	}
+	db->pid = pid;
+	db->names = NULL;
+
+	snprintf(path, pathlen, "%s/names.tdb", base_path);
+
+	db->tdb = tdb_wrap_open(db, path, hash_size, tdb_flags,
+				O_RDWR|O_CREAT, 0660);
+	if (db->tdb == NULL) {
+		TALLOC_FREE(db);
+		return NULL;
+	}
+
+	talloc_set_destructor(db, server_id_db_destructor);
+
+	return db;
+}
+
+void server_id_db_reinit(struct server_id_db *db, struct server_id pid)
+{
+	db->pid = pid;
+	TALLOC_FREE(db->names);
+}
+
+static int server_id_db_destructor(struct server_id_db *db)
+{
+	char *name = NULL;
+
+	while ((name = strv_next(db->names, name)) != NULL) {
+		server_id_db_remove(db, name);
+	}
+
+	return 0;
+}
+
+int server_id_db_add(struct server_id_db *db, const char *name)
+{
+	struct tdb_context *tdb = db->tdb->tdb;
+	struct server_id_buf buf;
+	TDB_DATA key, data;
+	char *n;
+	int ret;
+
+	n = strv_find(db->names, name);
+	if (n != NULL) {
+		return EEXIST;
+	}
+
+	ret = strv_add(db, &db->names, name);
+	if (ret != 0) {
+		return ret;
+	}
+
+	key = string_term_tdb_data(name);
+
+	server_id_str_buf(db->pid, &buf);
+	data = string_term_tdb_data(buf.buf);
+
+	ret = tdb_append(tdb, key, data);
+	if (ret != 0) {
+		enum TDB_ERROR err = tdb_error(tdb);
+		strv_delete(&db->names, strv_find(db->names, name));
+		return map_unix_error_from_tdb(err);
+	}
+
+	return 0;
+}
+
+int server_id_db_remove(struct server_id_db *db, const char *name)
+{
+	struct tdb_context *tdb = db->tdb->tdb;
+	struct server_id_buf buf;
+	TDB_DATA key;
+	uint8_t *data;
+	char *ids, *n, *id;
+	int ret;
+
+	n = strv_find(db->names, name);
+	if (n == NULL) {
+		return ENOENT;
+	}
+
+	key = string_term_tdb_data(name);
+	server_id_str_buf(db->pid, &buf);
+
+	ret = tdb_chainlock(tdb, key);
+	if (ret == -1) {
+		enum TDB_ERROR err = tdb_error(tdb);
+		return map_unix_error_from_tdb(err);
+	}
+
+	ret = tdb_fetch_talloc(tdb, key, db, &data);
+	if (ret != 0) {
+		tdb_chainunlock(tdb, key);
+		return ret;
+	}
+
+	ids = (char *)data;
+
+	id = strv_find(ids, buf.buf);
+	if (id == NULL) {
+		tdb_chainunlock(tdb, key);
+		TALLOC_FREE(data);
+		return ENOENT;
+	}
+
+	strv_delete(&ids, id);
+	ret = tdb_store(tdb, key, talloc_tdb_data(ids), TDB_MODIFY);
+	TALLOC_FREE(data);
+
+	tdb_chainunlock(tdb, key);
+
+	if (ret == -1) {
+		enum TDB_ERROR err = tdb_error(tdb);
+		return map_unix_error_from_tdb(err);
+	}
+
+	strv_delete(&db->names, n);
+	return 0;
+}
+
+int server_id_db_lookup(struct server_id_db *db, const char *name,
+			TALLOC_CTX *mem_ctx, unsigned *pnum_servers,
+			struct server_id **pservers)
+{
+	struct tdb_context *tdb = db->tdb->tdb;
+	TDB_DATA key;
+	uint8_t *data;
+	char *ids, *id;
+	unsigned num_servers;
+	struct server_id *servers;
+	int i, ret;
+
+	key = string_term_tdb_data(name);
+
+	ret = tdb_fetch_talloc(tdb, key, mem_ctx, &data);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ids = (char *)data;
+	num_servers = strv_count(ids);
+
+	servers = talloc_array(mem_ctx, struct server_id, num_servers);
+	if (servers == NULL) {
+		TALLOC_FREE(data);
+		return ENOMEM;
+	}
+
+	i = 0;
+
+	for (id = ids; id != NULL; id = strv_next(ids, id)) {
+		servers[i++] = server_id_from_string(NONCLUSTER_VNN, id);
+	}
+
+	TALLOC_FREE(data);
+
+	*pnum_servers = num_servers;
+	*pservers = servers;
+
+	return 0;
+}
+
+bool server_id_db_lookup_one(struct server_id_db *db, const char *name,
+			     struct server_id *server)
+{
+	int ret;
+	unsigned num_servers;
+	struct server_id *servers;
+
+	ret = server_id_db_lookup(db, name, db, &num_servers, &servers);
+	if (ret != 0) {
+		return false;
+	}
+	if (num_servers == 0) {
+		TALLOC_FREE(servers);
+		return false;
+	}
+	*server = servers[0];
+	TALLOC_FREE(servers);
+	return true;
+}
+
+struct server_id_db_traverse_state {
+	TALLOC_CTX *mem_ctx;
+	int (*fn)(const char *name,
+		  unsigned num_servers,
+		  const struct server_id *servers,
+		  void *private_data);
+	void *private_data;
+};
+
+static int server_id_db_traverse_fn(struct tdb_context *tdb,
+				    TDB_DATA key, TDB_DATA data,
+				    void *private_data)
+{
+	struct server_id_db_traverse_state *state = private_data;
+	const char *name;
+	char *ids, *id;
+	unsigned num_servers;
+	struct server_id *servers;
+	int i, ret;
+
+	if (key.dsize == 0) {
+		return 0;
+	}
+	if (key.dptr[key.dsize-1] != '\0') {
+		return 0;
+	}
+	name = (const char *)key.dptr;
+
+	ids = (char *)talloc_memdup(state->mem_ctx, data.dptr, data.dsize);
+	if (ids == NULL) {
+		return 0;
+	}
+
+	num_servers = strv_count(ids);
+	servers = talloc_array(ids, struct server_id, num_servers);
+
+	i = 0;
+
+	for (id = ids; id != NULL; id = strv_next(ids, id)) {
+		servers[i++] = server_id_from_string(NONCLUSTER_VNN, id);
+	}
+
+	ret = state->fn(name, num_servers, servers, state->private_data);
+
+	TALLOC_FREE(ids);
+
+	return ret;
+}
+
+int server_id_db_traverse_read(struct server_id_db *db,
+			       int (*fn)(const char *name,
+					 unsigned num_servers,
+					 const struct server_id *servers,
+					 void *private_data),
+			       void *private_data)
+{
+	struct server_id_db_traverse_state state;
+	int ret;
+
+	state = (struct server_id_db_traverse_state) {
+		.fn = fn, .private_data = private_data,
+		.mem_ctx = talloc_new(db)
+	};
+
+	if (state.mem_ctx == NULL) {
+		return ENOMEM;
+	}
+
+	ret = tdb_traverse_read(db->tdb->tdb, server_id_db_traverse_fn,
+				&state);
+	TALLOC_FREE(state.mem_ctx);
+	return ret;
+}
diff --git a/lib/util/server_id_db.h b/lib/util/server_id_db.h
new file mode 100644
index 0000000..31b3305
--- /dev/null
+++ b/lib/util/server_id_db.h
@@ -0,0 +1,47 @@
+/*
+ * Namedb
+ *
+ * Copyright Volker Lendecke <vl at samba.org> 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 _SERVER_ID_DB_H_
+#define _SERVER_ID_DB_H_
+
+#include "talloc.h"
+#include "librpc/gen_ndr/server_id.h"
+
+struct server_id_db;
+
+struct server_id_db *server_id_db_init(TALLOC_CTX *mem_ctx,
+				       struct server_id pid,
+				       const char *base_path,
+				       int hash_size, int tdb_flags);
+void server_id_db_reinit(struct server_id_db *db, struct server_id pid);
+int server_id_db_add(struct server_id_db *db, const char *name);
+int server_id_db_remove(struct server_id_db *db, const char *name);
+int server_id_db_lookup(struct server_id_db *db, const char *name,
+			TALLOC_CTX *mem_ctx, unsigned *num_servers,
+			struct server_id **servers);
+bool server_id_db_lookup_one(struct server_id_db *db, const char *name,
+			     struct server_id *server);
+int server_id_db_traverse_read(struct server_id_db *db,
+			       int (*fn)(const char *name,
+					 unsigned num_servers,
+					 const struct server_id *servers,
+					 void *private_data),
+			       void *private_data);
+
+#endif
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index 83d0bf1..c0d07e7 100755
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -139,3 +139,8 @@ if not bld.env.SAMBA_UTIL_CORE_ONLY:
                         public_deps='talloc'
 	                )
 
+    bld.SAMBA_LIBRARY('server_id_db',
+                      source='server_id_db.c',
+                      deps='talloc tdb strv util_tdb tdb-wrap samba-util',
+                      local_include=False,
+                      private_library=True)
-- 
1.9.1


From 488565188ed197c3a1144b2c1768fb5aec4a6de2 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 5 Nov 2014 13:02:38 +0000
Subject: [PATCH 24/41] dbwrap: Add code to marshall a db_context's db

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 lib/dbwrap/dbwrap.h      |   9 +++
 lib/dbwrap/dbwrap_util.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 175 insertions(+)

diff --git a/lib/dbwrap/dbwrap.h b/lib/dbwrap/dbwrap.h
index e56e036..00c7672 100644
--- a/lib/dbwrap/dbwrap.h
+++ b/lib/dbwrap/dbwrap.h
@@ -155,6 +155,15 @@ NTSTATUS dbwrap_store_bystring_upper(struct db_context *db, const char *key,
 NTSTATUS dbwrap_fetch_bystring_upper(struct db_context *db, TALLOC_CTX *mem_ctx,
 				     const char *key, TDB_DATA *value);
 
+size_t dbwrap_marshall(struct db_context *db, uint8_t *buf, size_t bufsize);
+NTSTATUS dbwrap_parse_marshall_buf(const uint8_t *buf, size_t buflen,
+				   bool (*fn)(TDB_DATA key, TDB_DATA value,
+					      void *private_data),
+				   void *private_data);
+NTSTATUS dbwrap_unmarshall(struct db_context *db, const uint8_t *buf,
+			   size_t buflen);
+
+
 /**
  * This opens an ntdb or tdb file: you can hand it a .ntdb or .tdb extension
  * and it will decide (based on parameter settings, or else what exists) which
diff --git a/lib/dbwrap/dbwrap_util.c b/lib/dbwrap/dbwrap_util.c
index 4185fff..901ef56 100644
--- a/lib/dbwrap/dbwrap_util.c
+++ b/lib/dbwrap/dbwrap_util.c
@@ -595,3 +595,169 @@ NTSTATUS dbwrap_fetch_bystring_upper(struct db_context *db, TALLOC_CTX *mem_ctx,
 	talloc_free(key_upper);
 	return status;
 }
+
+struct dbwrap_marshall_state {
+	uint8_t *buf;
+	size_t bufsize;
+	size_t dbsize;
+};
+
+static int dbwrap_marshall_fn(struct db_record *rec, void *private_data)
+{
+	struct dbwrap_marshall_state *state = private_data;
+	TDB_DATA key, value;
+	size_t new_dbsize;
+
+	key = dbwrap_record_get_key(rec);
+	value = dbwrap_record_get_value(rec);
+
+	new_dbsize = state->dbsize;
+	new_dbsize += 8 + key.dsize;
+	new_dbsize += 8 + value.dsize;
+
+	if (new_dbsize <= state->bufsize) {
+		uint8_t *p = state->buf + state->dbsize;
+
+		SBVAL(p, 0, key.dsize);
+		p += 8;
+		memcpy(p, key.dptr, key.dsize);
+		p += key.dsize;
+
+		SBVAL(p, 0, value.dsize);
+		p += 8;
+		memcpy(p, value.dptr, value.dsize);
+	}
+	state->dbsize = new_dbsize;
+	return 0;
+}
+
+size_t dbwrap_marshall(struct db_context *db, uint8_t *buf, size_t bufsize)
+{
+	struct dbwrap_marshall_state state;
+
+	state.bufsize = bufsize;
+	state.buf = buf;
+	state.dbsize = 0;
+
+	dbwrap_traverse_read(db, dbwrap_marshall_fn, &state, NULL);
+
+	return state.dbsize;
+}
+
+static ssize_t dbwrap_unmarshall_get_data(const uint8_t *buf, size_t buflen,
+					  size_t ofs, TDB_DATA *pdata)
+{
+	uint64_t space, len;
+	const uint8_t *p;
+
+	if (ofs == buflen) {
+		return 0;
+	}
+	if (ofs > buflen) {
+		return -1;
+	}
+
+	space = buflen - ofs;
+	if (space < 8) {
+		return -1;
+	}
+
+	p = buf + ofs;
+	len = BVAL(p, 0);
+
+	p += 8;
+	space -= 8;
+
+	if (len > space) {
+		return -1;
+	}
+
+	*pdata = (TDB_DATA) { .dptr = discard_const_p(uint8_t, p),
+			      .dsize = len };
+	return len + 8;
+}
+
+NTSTATUS dbwrap_parse_marshall_buf(const uint8_t *buf, size_t buflen,
+				   bool (*fn)(TDB_DATA key, TDB_DATA value,
+					      void *private_data),
+				   void *private_data)
+{
+	size_t ofs = 0;
+
+	while (true) {
+		ssize_t len;
+		TDB_DATA key, value;
+		bool ok;
+
+		len = dbwrap_unmarshall_get_data(buf, buflen, ofs, &key);
+		if (len == 0) {
+			break;
+		}
+		if (len == -1) {
+			return NT_STATUS_INVALID_PARAMETER;
+		}
+		ofs += len;
+
+		len = dbwrap_unmarshall_get_data(buf, buflen, ofs, &value);
+		if (len == 0) {
+			break;
+		}
+		if (len == -1) {
+			return NT_STATUS_INVALID_PARAMETER;
+		}
+		ofs += len;
+
+		ok = fn(key, value, private_data);
+		if (!ok) {
+			break;
+		}
+	}
+
+	return NT_STATUS_OK;
+}
+
+struct dbwrap_unmarshall_state {
+	struct db_context *db;
+	NTSTATUS ret;
+};
+
+static bool dbwrap_unmarshall_fn(TDB_DATA key, TDB_DATA value,
+				 void *private_data)
+{
+	struct dbwrap_unmarshall_state *state = private_data;
+	struct db_record *rec;
+	NTSTATUS status;
+
+	rec = dbwrap_fetch_locked(state->db, state->db, key);
+	if (rec == NULL) {
+		DEBUG(10, ("%s: dbwrap_fetch_locked failed\n",
+			   __func__));
+		state->ret = NT_STATUS_NO_MEMORY;
+		return false;
+	}
+
+	status = dbwrap_record_store(rec, value, 0);
+	TALLOC_FREE(rec);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: dbwrap_record_store failed: %s\n",
+			   __func__, nt_errstr(status)));
+		state->ret = status;
+		return false;
+	}
+
+	return true;
+}
+
+NTSTATUS dbwrap_unmarshall(struct db_context *db, const uint8_t *buf,
+			   size_t buflen)
+{
+	struct dbwrap_unmarshall_state state = { .db = db };
+	NTSTATUS status;
+
+	status = dbwrap_parse_marshall_buf(buf, buflen,
+					   dbwrap_unmarshall_fn, &state);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+	return state.ret;
+}
-- 
1.9.1


From 3223c0f9f5944b40bd0d8e1bc973ab1e52fe2dee Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 17 Oct 2014 12:09:03 +0000
Subject: [PATCH 25/41] messaging3: Add messaging_names_db

This will enable messaging3 users to more easily register themselves
under a name

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/include/messages.h |  1 +
 source3/lib/messages.c     | 19 +++++++++++++++++++
 source3/wscript_build      |  1 +
 3 files changed, 21 insertions(+)

diff --git a/source3/include/messages.h b/source3/include/messages.h
index fac561b..80b6c19 100644
--- a/source3/include/messages.h
+++ b/source3/include/messages.h
@@ -89,6 +89,7 @@ struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx,
 struct server_id messaging_server_id(const struct messaging_context *msg_ctx);
 struct tevent_context *messaging_tevent_context(
 	struct messaging_context *msg_ctx);
+struct server_id_db *messaging_names_db(struct messaging_context *msg_ctx);
 
 /*
  * re-init after a fork
diff --git a/source3/lib/messages.c b/source3/lib/messages.c
index 5b4daa2..1940359 100644
--- a/source3/lib/messages.c
+++ b/source3/lib/messages.c
@@ -53,6 +53,7 @@
 #include "lib/background.h"
 #include "lib/messages_dgm.h"
 #include "lib/iov_buf.h"
+#include "lib/util/server_id_db.h"
 
 struct messaging_callback {
 	struct messaging_callback *prev, *next;
@@ -75,6 +76,8 @@ struct messaging_context {
 	unsigned num_waiters;
 
 	struct messaging_backend *remote;
+
+	struct server_id_db *names_db;
 };
 
 struct messaging_hdr {
@@ -314,6 +317,15 @@ struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx,
 		return NULL;
 	}
 
+	ctx->names_db = server_id_db_init(
+		ctx, ctx->id, lp_cache_directory(), 0,
+		TDB_INCOMPATIBLE_HASH|TDB_CLEAR_IF_FIRST);
+	if (ctx->names_db == NULL) {
+		DEBUG(10, ("%s: server_id_db_init failed\n", __func__));
+		TALLOC_FREE(ctx);
+		return NULL;
+	}
+
 	talloc_set_destructor(ctx, messaging_context_destructor);
 
 	if (lp_clustering()) {
@@ -377,6 +389,8 @@ NTSTATUS messaging_reinit(struct messaging_context *msg_ctx)
 		}
 	}
 
+	server_id_db_reinit(msg_ctx->names_db, msg_ctx->id);
+
 	return NT_STATUS_OK;
 }
 
@@ -1070,4 +1084,9 @@ struct tevent_context *messaging_tevent_context(
 	return msg_ctx->event_ctx;
 }
 
+struct server_id_db *messaging_names_db(struct messaging_context *msg_ctx)
+{
+	return msg_ctx->names_db;
+}
+
 /** @} **/
diff --git a/source3/wscript_build b/source3/wscript_build
index 069d49e..807c09e 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -370,6 +370,7 @@ bld.SAMBA3_SUBSYSTEM('samba3core',
                         dbwrap
                         samba3-util
                         errors3
+                        server_id_db
                         TDB_LIB''')
 
 bld.SAMBA3_LIBRARY('smbd_shim',
-- 
1.9.1


From 8b2144cfd0701d4c0834252e2e1e6d26e36b887c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 12 Nov 2014 16:42:59 +0100
Subject: [PATCH 26/41] messaging3: Add messaging_send_iov_from

In the notifyd code it will be very helpful to fake source server_ids

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/include/messages.h |  5 +++++
 source3/lib/messages.c     | 36 +++++++++++++++++++++++-------------
 2 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/source3/include/messages.h b/source3/include/messages.h
index 80b6c19..3db6756 100644
--- a/source3/include/messages.h
+++ b/source3/include/messages.h
@@ -123,6 +123,11 @@ NTSTATUS messaging_send(struct messaging_context *msg_ctx,
 NTSTATUS messaging_send_buf(struct messaging_context *msg_ctx,
 			    struct server_id server, uint32_t msg_type,
 			    const uint8_t *buf, size_t len);
+NTSTATUS messaging_send_iov_from(struct messaging_context *msg_ctx,
+				 struct server_id src, struct server_id dst,
+				 uint32_t msg_type,
+				 const struct iovec *iov, int iovlen,
+				 const int *fds, size_t num_fds);
 NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
 			    struct server_id server, uint32_t msg_type,
 			    const struct iovec *iov, int iovlen,
diff --git a/source3/lib/messages.c b/source3/lib/messages.c
index 1940359..8d7358d 100644
--- a/source3/lib/messages.c
+++ b/source3/lib/messages.c
@@ -489,16 +489,17 @@ NTSTATUS messaging_send_buf(struct messaging_context *msg_ctx,
 	return messaging_send(msg_ctx, server, msg_type, &blob);
 }
 
-NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
-			    struct server_id server, uint32_t msg_type,
-			    const struct iovec *iov, int iovlen,
-			    const int *fds, size_t num_fds)
+NTSTATUS messaging_send_iov_from(struct messaging_context *msg_ctx,
+				 struct server_id src, struct server_id dst,
+				 uint32_t msg_type,
+				 const struct iovec *iov, int iovlen,
+				 const int *fds, size_t num_fds)
 {
 	int ret;
 	struct messaging_hdr hdr;
 	struct iovec iov2[iovlen+1];
 
-	if (server_id_is_disconnected(&server)) {
+	if (server_id_is_disconnected(&dst)) {
 		return NT_STATUS_INVALID_PARAMETER_MIX;
 	}
 
@@ -506,12 +507,12 @@ NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
 		return NT_STATUS_INVALID_PARAMETER_MIX;
 	}
 
-	if (!procid_is_local(&server)) {
+	if (!procid_is_local(&dst)) {
 		if (num_fds > 0) {
 			return NT_STATUS_NOT_SUPPORTED;
 		}
 
-		ret = msg_ctx->remote->send_fn(msg_ctx->id, server,
+		ret = msg_ctx->remote->send_fn(src, dst,
 					       msg_type, iov, iovlen,
 					       NULL, 0,
 					       msg_ctx->remote);
@@ -521,7 +522,7 @@ NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
 		return NT_STATUS_OK;
 	}
 
-	if (server_id_same_process(&msg_ctx->id, &server)) {
+	if (server_id_same_process(&msg_ctx->id, &dst)) {
 		struct messaging_rec rec;
 		uint8_t *buf;
 
@@ -541,8 +542,8 @@ NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
 		rec = (struct messaging_rec) {
 			.msg_version = MESSAGE_VERSION,
 			.msg_type = msg_type & MSG_TYPE_MASK,
-			.dest = server,
-			.src = msg_ctx->id,
+			.dest = dst,
+			.src = src,
 			.buf = data_blob_const(buf, talloc_get_size(buf)),
 		};
 
@@ -554,14 +555,14 @@ NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
 	ZERO_STRUCT(hdr);
 	hdr = (struct messaging_hdr) {
 		.msg_type = msg_type,
-		.dst = server,
-		.src = msg_ctx->id
+		.dst = dst,
+		.src = src
 	};
 	iov2[0] = (struct iovec){ .iov_base = &hdr, .iov_len = sizeof(hdr) };
 	memcpy(&iov2[1], iov, iovlen * sizeof(*iov));
 
 	become_root();
-	ret = messaging_dgm_send(server.pid, iov2, iovlen+1, fds, num_fds);
+	ret = messaging_dgm_send(dst.pid, iov2, iovlen+1, fds, num_fds);
 	unbecome_root();
 
 	if (ret != 0) {
@@ -570,6 +571,15 @@ NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
 	return NT_STATUS_OK;
 }
 
+NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
+			    struct server_id dst, uint32_t msg_type,
+			    const struct iovec *iov, int iovlen,
+			    const int *fds, size_t num_fds)
+{
+	return messaging_send_iov_from(msg_ctx, msg_ctx->id, dst, msg_type,
+				       iov, iovlen, fds, num_fds);
+}
+
 static struct messaging_rec *messaging_rec_dup(TALLOC_CTX *mem_ctx,
 					       struct messaging_rec *rec)
 {
-- 
1.9.1


From 7e38bcedf3fd416733a49fe98b20a949cd422bbb Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 13 Nov 2014 11:25:40 +0000
Subject: [PATCH 27/41] messaging3: Add messaging_handler_send/recv

This repeatedly listens on msg_type. It's similar to messaging_register
with talloc based autocleanup. The handler is free to talloc_move a way
the record for later use.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/include/messages.h |  8 +++++
 source3/lib/messages.c     | 81 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)

diff --git a/source3/include/messages.h b/source3/include/messages.h
index 3db6756..6b5e3da 100644
--- a/source3/include/messages.h
+++ b/source3/include/messages.h
@@ -150,6 +150,14 @@ struct tevent_req *messaging_read_send(TALLOC_CTX *mem_ctx,
 int messaging_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 			struct messaging_rec **presult);
 
+struct tevent_req *messaging_handler_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct messaging_context *msg_ctx, uint32_t msg_type,
+	bool (*handler)(struct messaging_context *msg_ctx,
+			struct messaging_rec **rec, void *private_data),
+	void *private_data);
+int messaging_handler_recv(struct tevent_req *req);
+
 int messaging_cleanup(struct messaging_context *msg_ctx, pid_t pid);
 
 bool messaging_parent_dgm_cleanup_init(struct messaging_context *msg);
diff --git a/source3/lib/messages.c b/source3/lib/messages.c
index 8d7358d..b859d09 100644
--- a/source3/lib/messages.c
+++ b/source3/lib/messages.c
@@ -822,6 +822,87 @@ int messaging_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 	return 0;
 }
 
+struct messaging_handler_state {
+	struct tevent_context *ev;
+	struct messaging_context *msg_ctx;
+	uint32_t msg_type;
+	bool (*handler)(struct messaging_context *msg_ctx,
+			struct messaging_rec **rec, void *private_data);
+	void *private_data;
+};
+
+static void messaging_handler_got_msg(struct tevent_req *subreq);
+
+struct tevent_req *messaging_handler_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct messaging_context *msg_ctx, uint32_t msg_type,
+	bool (*handler)(struct messaging_context *msg_ctx,
+			struct messaging_rec **rec, void *private_data),
+	void *private_data)
+{
+	struct tevent_req *req, *subreq;
+	struct messaging_handler_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct messaging_handler_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->msg_ctx = msg_ctx;
+	state->msg_type = msg_type;
+	state->handler = handler;
+	state->private_data = private_data;
+
+	subreq = messaging_read_send(state, state->ev, state->msg_ctx,
+				     state->msg_type);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, messaging_handler_got_msg, req);
+	return req;
+}
+
+static void messaging_handler_got_msg(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct messaging_handler_state *state = tevent_req_data(
+		req, struct messaging_handler_state);
+	struct messaging_rec *rec;
+	int ret;
+	bool ok;
+
+	ret = messaging_read_recv(subreq, state, &rec);
+	TALLOC_FREE(subreq);
+	if (tevent_req_error(req, ret)) {
+		return;
+	}
+
+	subreq = messaging_read_send(state, state->ev, state->msg_ctx,
+				     state->msg_type);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, messaging_handler_got_msg, req);
+
+	ok = state->handler(state->msg_ctx, &rec, state->private_data);
+	TALLOC_FREE(rec);
+	if (ok) {
+		/*
+		 * Next round
+		 */
+		return;
+	}
+	TALLOC_FREE(subreq);
+	tevent_req_done(req);
+}
+
+int messaging_handler_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_unix(req);
+}
+
 static bool messaging_append_new_waiters(struct messaging_context *msg_ctx)
 {
 	if (msg_ctx->num_new_waiters == 0) {
-- 
1.9.1


From acceb24be0aada655b8623b51808660bb45a15d7 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 13 Nov 2014 10:38:40 +0000
Subject: [PATCH 28/41] messaging3: Avoid self-send complexity

With the notify code I've hit another case where self-sends caused a problem.
This time messages were lost because we tried to do multiple dispatch_rec calls
from within a single inotify callback. Only the first one was being taken care
of, the others did not find receivers.

This patch makes self-sends go through the kernel as well, the kernel queues
everything nicely for us. With dgram messaging this should be pretty fast. If
it turns out to be a problem, we can solve it later by doing proper queueing in
user space. We need to completely decouple any processing from callbacks.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/lib/messages.c                      | 113 +---------------------------
 source3/torture/test_messaging_fd_passing.c |   2 +-
 source3/torture/test_messaging_read.c       |   2 +-
 3 files changed, 5 insertions(+), 112 deletions(-)

diff --git a/source3/lib/messages.c b/source3/lib/messages.c
index b859d09..7eadb02 100644
--- a/source3/lib/messages.c
+++ b/source3/lib/messages.c
@@ -522,36 +522,6 @@ NTSTATUS messaging_send_iov_from(struct messaging_context *msg_ctx,
 		return NT_STATUS_OK;
 	}
 
-	if (server_id_same_process(&msg_ctx->id, &dst)) {
-		struct messaging_rec rec;
-		uint8_t *buf;
-
-		/*
-		 * Self-send, directly dispatch
-		 */
-
-		if (num_fds > 0) {
-			return NT_STATUS_NOT_SUPPORTED;
-		}
-
-		buf = iov_buf(talloc_tos(), iov, iovlen);
-		if (buf == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
-
-		rec = (struct messaging_rec) {
-			.msg_version = MESSAGE_VERSION,
-			.msg_type = msg_type & MSG_TYPE_MASK,
-			.dest = dst,
-			.src = src,
-			.buf = data_blob_const(buf, talloc_get_size(buf)),
-		};
-
-		messaging_dispatch_rec(msg_ctx, &rec);
-		TALLOC_FREE(buf);
-		return NT_STATUS_OK;
-	}
-
 	ZERO_STRUCT(hdr);
 	hdr = (struct messaging_hdr) {
 		.msg_type = msg_type,
@@ -931,68 +901,6 @@ static bool messaging_append_new_waiters(struct messaging_context *msg_ctx)
 	return true;
 }
 
-struct messaging_defer_callback_state {
-	struct messaging_context *msg_ctx;
-	struct messaging_rec *rec;
-	void (*fn)(struct messaging_context *msg, void *private_data,
-		   uint32_t msg_type, struct server_id server_id,
-		   DATA_BLOB *data);
-	void *private_data;
-};
-
-static void messaging_defer_callback_trigger(struct tevent_context *ev,
-					     struct tevent_immediate *im,
-					     void *private_data);
-
-static void messaging_defer_callback(
-	struct messaging_context *msg_ctx, struct messaging_rec *rec,
-	void (*fn)(struct messaging_context *msg, void *private_data,
-		   uint32_t msg_type, struct server_id server_id,
-		   DATA_BLOB *data),
-	void *private_data)
-{
-	struct messaging_defer_callback_state *state;
-	struct tevent_immediate *im;
-
-	state = talloc(msg_ctx, struct messaging_defer_callback_state);
-	if (state == NULL) {
-		DEBUG(1, ("talloc failed\n"));
-		return;
-	}
-	state->msg_ctx = msg_ctx;
-	state->fn = fn;
-	state->private_data = private_data;
-
-	state->rec = messaging_rec_dup(state, rec);
-	if (state->rec == NULL) {
-		DEBUG(1, ("talloc failed\n"));
-		TALLOC_FREE(state);
-		return;
-	}
-
-	im = tevent_create_immediate(state);
-	if (im == NULL) {
-		DEBUG(1, ("tevent_create_immediate failed\n"));
-		TALLOC_FREE(state);
-		return;
-	}
-	tevent_schedule_immediate(im, msg_ctx->event_ctx,
-				  messaging_defer_callback_trigger, state);
-}
-
-static void messaging_defer_callback_trigger(struct tevent_context *ev,
-					     struct tevent_immediate *im,
-					     void *private_data)
-{
-	struct messaging_defer_callback_state *state = talloc_get_type_abort(
-		private_data, struct messaging_defer_callback_state);
-	struct messaging_rec *rec = state->rec;
-
-	state->fn(state->msg_ctx, state->private_data, rec->msg_type, rec->src,
-		  &rec->buf);
-	TALLOC_FREE(state);
-}
-
 /*
   Dispatch one messaging_rec
 */
@@ -1019,24 +927,9 @@ void messaging_dispatch_rec(struct messaging_context *msg_ctx,
 		rec->num_fds = 0;
 		rec->fds = NULL;
 
-		if (server_id_same_process(&rec->src, &rec->dest)) {
-			/*
-			 * This is a self-send. We are called here from
-			 * messaging_send(), and we don't want to directly
-			 * recurse into the callback but go via a
-			 * tevent_loop_once
-			 */
-			messaging_defer_callback(msg_ctx, rec, cb->fn,
-						 cb->private_data);
-		} else {
-			/*
-			 * This comes from a different process. we are called
-			 * from the event loop, so we should call back
-			 * directly.
-			 */
-			cb->fn(msg_ctx, cb->private_data, rec->msg_type,
-			       rec->src, &rec->buf);
-		}
+		cb->fn(msg_ctx, cb->private_data, rec->msg_type,
+		       rec->src, &rec->buf);
+
 		/*
 		 * we continue looking for matching messages after finding
 		 * one. This matters for subsystems like the internal notify
diff --git a/source3/torture/test_messaging_fd_passing.c b/source3/torture/test_messaging_fd_passing.c
index abd142f..7bee41b 100644
--- a/source3/torture/test_messaging_fd_passing.c
+++ b/source3/torture/test_messaging_fd_passing.c
@@ -63,7 +63,7 @@ bool run_messaging_fdpass1(int dummy)
 
 	status = messaging_send_iov(msg_ctx, dst, MSG_PING, NULL, 0,
 				    pass_fds, 1);
-	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+	if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
 		fprintf(stderr,
 			"messaging_send_iov gave: %s\n", nt_errstr(status));
 		goto fail;
diff --git a/source3/torture/test_messaging_read.c b/source3/torture/test_messaging_read.c
index 9c4017c..802b4fe 100644
--- a/source3/torture/test_messaging_read.c
+++ b/source3/torture/test_messaging_read.c
@@ -122,7 +122,7 @@ bool run_messaging_read1(int dummy)
 		goto fail;
 	}
 
-	for (i=0; i<2; i++) {
+	for (i=0; i<3; i++) {
 		if (tevent_loop_once(ev) != 0) {
 			fprintf(stderr, "tevent_loop_once failed\n");
 			goto fail;
-- 
1.9.1


From 2ed5a10ba4a3049216c745b6ca3932e0b520d3d7 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 12 Nov 2014 10:08:36 +0000
Subject: [PATCH 29/41] lib: Add ctdbd_messaging_send to ctdb_dummy.c

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

diff --git a/source3/lib/ctdb_dummy.c b/source3/lib/ctdb_dummy.c
index 4b360a8..e19765b 100644
--- a/source3/lib/ctdb_dummy.c
+++ b/source3/lib/ctdb_dummy.c
@@ -30,6 +30,13 @@ NTSTATUS ctdbd_probe(void)
 	return NT_STATUS_NOT_IMPLEMENTED;
 }
 
+NTSTATUS ctdbd_messaging_send(struct ctdbd_connection *conn,
+			      uint32_t dst_vnn, uint64_t dst_srvid,
+			      struct messaging_rec *msg)
+{
+	return NT_STATUS_NOT_IMPLEMENTED;
+}
+
 NTSTATUS ctdbd_messaging_send_blob(struct ctdbd_connection *conn,
 				   uint32_t dst_vnn, uint64_t dst_srvid,
 				   const uint8_t *buf, size_t buflen)
-- 
1.9.1


From 91f85a04987e8650c65c9e4315a116feaa8ba73a Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 2 Jan 2014 17:54:53 +0100
Subject: [PATCH 30/41] notify: Move path construction to notify_trigger

notify_msg won't need to construct the path anymore, it will be able to
put the parts into iovecs

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/notify.c          | 13 ++-----------
 source3/smbd/notify_internal.c | 19 ++++++++++++++++---
 source3/smbd/proto.h           |  3 ++-
 3 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index ac1a55c..5ac8c0c 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -442,21 +442,12 @@ void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
 		  const char *path)
 {
 	struct notify_context *notify_ctx = conn->sconn->notify_ctx;
-	char *fullpath, *to_free;
-	char tmpbuf[PATH_MAX];
-	ssize_t len;
 
 	if (path[0] == '.' && path[1] == '/') {
 		path += 2;
 	}
-	len = full_path_tos(conn->connectpath, path, tmpbuf, sizeof(tmpbuf),
-			    &fullpath, &to_free);
-	if (len == -1) {
-		DEBUG(0, ("full_path_tos failed\n"));
-		return;
-	}
-	notify_trigger(notify_ctx, action, filter, fullpath);
-	TALLOC_FREE(to_free);
+
+	notify_trigger(notify_ctx, action, filter, conn->connectpath, path);
 }
 
 static void notify_fsp(files_struct *fsp, struct timespec when,
diff --git a/source3/smbd/notify_internal.c b/source3/smbd/notify_internal.c
index 9c0b190..e612f16 100644
--- a/source3/smbd/notify_internal.c
+++ b/source3/smbd/notify_internal.c
@@ -616,7 +616,8 @@ static bool notify_pull_remote_blob(TALLOC_CTX *mem_ctx,
 }
 
 void notify_trigger(struct notify_context *notify,
-		    uint32_t action, uint32_t filter, const char *path)
+		    uint32_t action, uint32_t filter,
+		    const char *dir, const char *name)
 {
 	struct ctdbd_connection *ctdbd_conn;
 	struct notify_trigger_index_state idx_state;
@@ -625,22 +626,33 @@ void notify_trigger(struct notify_context *notify,
 	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, "
-		   "path=%s\n", (unsigned)action, (unsigned)filter, path));
+		   "dir=%s, name=%s\n", (unsigned)action, (unsigned)filter,
+		   dir, name));
 
 	/* see if change notify is enabled at all */
 	if (notify == NULL) {
 		return;
 	}
 
-	if (path[0] != '/') {
+	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;
@@ -708,6 +720,7 @@ void notify_trigger(struct notify_context *notify,
 done:
 	TALLOC_FREE(remote_blob);
 	TALLOC_FREE(idx_state.vnns);
+	TALLOC_FREE(to_free);
 }
 
 static void notify_trigger_local(struct notify_context *notify,
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index e2e2895..c99997d 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -548,7 +548,8 @@ NTSTATUS notify_add(struct notify_context *notify,
 		    void *private_data);
 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 *path);
+		    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,
-- 
1.9.1


From 6fa25aebe6b2d5f36fc22786d3b3ec4d9a9818a0 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 31/41] smbd/notify_fam.c

---
 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 c99997d..a996333 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -536,6 +536,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 807c09e..1137366 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -520,6 +520,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
@@ -639,7 +642,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 fdf735afb6f4649dc09d72b9f8e9f2969d2a5d94 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 32/41] 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 5dbcda8..41e07f7 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 f246c28..08aee50 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 c8c01126a9004e7b9d6b730bc1280ca145bff94e 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 33/41] 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 41e07f7..4143d77 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 6bd888a..099119a 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2101,7 +2101,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 08aee50..efb0d44 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 efa71c4b9d43ade8a350f774ba341f2831a86abe Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 16:20:10 +0100
Subject: [PATCH 34/41] ctdbd_conn: Accept msgs to all registered srvids

---
 source3/lib/ctdbd_conn.c | 43 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 38 insertions(+), 5 deletions(-)

diff --git a/source3/lib/ctdbd_conn.c b/source3/lib/ctdbd_conn.c
index 5a9d155..c41ec53 100644
--- a/source3/lib/ctdbd_conn.c
+++ b/source3/lib/ctdbd_conn.c
@@ -55,6 +55,7 @@ struct ctdbd_connection {
 	uint32_t reqid;
 	uint32_t our_vnn;
 	uint64_t rand_srvid;
+	uint64_t *srvids;
 	int fd;
 	struct tevent_fd *fde;
 
@@ -110,10 +111,43 @@ static void ctdb_packet_dump(struct ctdb_req_header *hdr)
 NTSTATUS register_with_ctdbd(struct ctdbd_connection *conn, uint64_t srvid)
 {
 
+	NTSTATUS status;
 	int cstatus;
-	return ctdbd_control(conn, CTDB_CURRENT_NODE,
-			     CTDB_CONTROL_REGISTER_SRVID, srvid, 0,
-			     tdb_null, NULL, NULL, &cstatus);
+	size_t num_srvids;
+	uint64_t *tmp;
+
+	status = ctdbd_control(conn, CTDB_CURRENT_NODE,
+			       CTDB_CONTROL_REGISTER_SRVID, srvid, 0,
+			       tdb_null, NULL, NULL, &cstatus);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	num_srvids = talloc_array_length(conn->srvids);
+
+	tmp = talloc_realloc(conn, conn->srvids, uint64_t,
+			     num_srvids + 1);
+	if (tmp == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	conn->srvids = tmp;
+
+	conn->srvids[num_srvids] = srvid;
+	return NT_STATUS_OK;
+}
+
+static bool ctdb_is_our_srvid(struct ctdbd_connection *conn, uint64_t srvid)
+{
+	size_t i, num_srvids;
+
+	num_srvids = talloc_array_length(conn->srvids);
+
+	for (i=0; i<num_srvids; i++) {
+		if (srvid == conn->srvids[i]) {
+			return true;
+		}
+	}
+	return false;
 }
 
 /*
@@ -662,8 +696,7 @@ static NTSTATUS ctdb_handle_message(struct messaging_context *msg_ctx,
 		return NT_STATUS_OK;
 	}
 
-	/* only messages to our pid or the broadcast are valid here */
-	if (msg->srvid != getpid() && msg->srvid != MSG_SRVID_SAMBA) {
+	if (!ctdb_is_our_srvid(conn, msg->srvid)) {
 		DEBUG(0,("Got unexpected message with srvid=%llu\n", 
 			 (unsigned long long)msg->srvid));
 		return NT_STATUS_OK;
-- 
1.9.1


From c071a95c1729b7aa3885c2d905e460171738b4ce 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 35/41] Add the notify daemon

---
 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..e740d3d
--- /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[1];
+};
+
+/*
+ * 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[1];
+};
+
+/*
+ * 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[1];
+};
+
+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 1137366..9fa1566 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1516,6 +1516,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 30bda58917899ac41f03e3831e20f2ba4d6d9a32 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 36/41] 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 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 8c90fe8..66151af 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -47,6 +47,7 @@
 #include "../lib/util/pidfile.h"
 #include "lib/smbd_shim.h"
 #include "scavenger.h"
+#include "smbd/notifyd/notifyd.h"
 
 struct smbd_open_socket;
 struct smbd_child_pid;
@@ -380,6 +381,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
@@ -1453,6 +1544,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);
-- 
1.9.1


From 364887fa10f074a2260f377c5e09ac6fce659326 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 37/41] Don't start the notify cleanup anymore

---
 source3/smbd/server.c | 100 --------------------------------------------------
 1 file changed, 100 deletions(-)

diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 66151af..50513df 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -256,103 +256,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)
 {
@@ -1541,9 +1444,6 @@ extern void build_options(bool screen);
 	if (!locking_init())
 		exit_daemon("Samba cannot init locking", 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 1ddf94536bc5b20009df145bdce35ec646ea3f01 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 21 Nov 2014 17:04:20 +0100
Subject: [PATCH 38/41] start notifyd requires a dependency

---
 source3/wscript_build | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source3/wscript_build b/source3/wscript_build
index 9fa1566..f62fff4 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -642,6 +642,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 350be432b13cf016635c61d841500aa7ddb827a0 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 39/41] 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 a996333..ef68aa4 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -575,11 +575,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 f62fff4..e1be12b 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -612,7 +612,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
@@ -1190,7 +1190,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 e3f10b62e9362eb83527e1f3dcac1e8f78bdf983 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 40/41] kernel change notify is done by notifyd

---
 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 69df62f3b7755b0e96b1c21013bf8dad7d856990 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 41/41] remove the notify_fam module

---
 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 f7d0db8..e4cf8b8 100644
--- a/docs-xml/wscript_build
+++ b/docs-xml/wscript_build
@@ -66,7 +66,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 e5d04f7..08a4595 100644
--- a/source3/modules/wscript_build
+++ b/source3/modules/wscript_build
@@ -285,13 +285,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



More information about the samba-technical mailing list