From 73258fec796b57c757e10b0baec306157a69edeb Mon Sep 17 00:00:00 2001
From: Ali Polatel <alip@exherbo.org>
Date: Wed, 26 May 2010 14:21:15 +0300
Subject: [PATCH] notmuch-deliver: Use splice() if it's available

NOTMUCH_DELIVER_NO_SPLICE environment variable may be set to fallback to
the read/write method.
---
 contrib/notmuch-deliver/configure.ac |   2 +-
 contrib/notmuch-deliver/src/main.c   | 128 +++++++++++++++++++++------
 2 files changed, 103 insertions(+), 27 deletions(-)

diff --git a/contrib/notmuch-deliver/configure.ac b/contrib/notmuch-deliver/configure.ac
index 9d16af16..b6142bce 100644
--- a/contrib/notmuch-deliver/configure.ac
+++ b/contrib/notmuch-deliver/configure.ac
@@ -67,7 +67,7 @@ AC_STRUCT_TM
 dnl }}}
 
 dnl {{{ Check for library functions
-AC_CHECK_FUNCS([setgroups initgroups symlink readlink strcasecmp utime utimes])
+AC_CHECK_FUNCS([setgroups initgroups symlink readlink strcasecmp utime utimes splice])
 dnl }}}
 
 dnl {{{ gethostname()
diff --git a/contrib/notmuch-deliver/src/main.c b/contrib/notmuch-deliver/src/main.c
index 49919ff1..7c314911 100644
--- a/contrib/notmuch-deliver/src/main.c
+++ b/contrib/notmuch-deliver/src/main.c
@@ -26,7 +26,12 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
+#ifdef HAVE_SPLICE
+#include <fcntl.h>
+#endif
 
 #ifdef HAVE_SYSEXITS_H
 #include <sysexits.h>
@@ -136,32 +141,61 @@ load_keyfile(const gchar *path, gchar **db_path, gchar ***tags)
 	return TRUE;
 }
 
+#ifdef HAVE_SPLICE
 static int
-save_maildir(int fdin, const char *dir, int auto_create, char **path)
+save_splice(int fdin, int fdout)
 {
-	int fd, ret, written;
-	char buf[4096], *p;
-	struct maildir_tmpcreate_info info;
+	int ret, written, pfd[2];
 
-	maildir_tmpcreate_init(&info);
-	info.openmode = 0666;
-	info.maildir = dir;
-	info.doordie = 1;
+	if (pipe(pfd) < 0) {
+		g_critical("Failed to create pipe: %s", g_strerror(errno));
+		return EX_IOERR;
+	}
 
-	while ((fd = maildir_tmpcreate_fd(&info)) < 0)
-	{
-		if (errno == ENOENT && auto_create && maildir_mkdir(dir) == 0)
-		{
-			auto_create = 0;
-			continue;
+	for (;;) {
+		ret = splice(fdin, NULL, pfd[1], NULL, 4096, 0);
+		if (!ret)
+			break;
+		if (ret < 0) {
+			g_critical("Splicing data from standard input failed: %s",
+				g_strerror(errno));
+			close(pfd[0]);
+			close(pfd[1]);
+			return EX_IOERR;
 		}
 
-		g_critical("Failed to create temporary file `%s': %s",
-			info.tmpname, g_strerror(errno));
-		return EX_TEMPFAIL;
+		do {
+			written = splice(pfd[0], NULL, fdout, NULL, ret, 0);
+			if (!written) {
+				g_critical("Splicing data to temporary file failed: %s",
+					g_strerror(errno));
+				close(pfd[0]);
+				close(pfd[1]);
+				return EX_IOERR;
+			}
+			if (written < 0) {
+				g_critical("Splicing data to temporary file failed: %s",
+					g_strerror(errno));
+				close(pfd[0]);
+				close(pfd[1]);
+				return EX_IOERR;
+			}
+			ret -= written;
+		} while (ret);
 	}
 
-	g_debug("Reading from standard input and writing to `%s'", info.tmpname);
+	close(pfd[0]);
+	close(pfd[1]);
+	return 0;
+}
+#endif /* HAVE_SPLICE */
+
+static int
+save_readwrite(int fdin, int fdout)
+{
+	int ret, written;
+	char buf[4096], p;
+
 	for (;;) {
 		ret = read(fdin, buf, 4096);
 		if (!ret)
@@ -169,27 +203,66 @@ save_maildir(int fdin, const char *dir, int auto_create, char **path)
 		if (ret < 0) {
 			if (errno == EINTR)
 				continue;
-			g_critical("Reading from standard input failed: %s", g_strerror(errno));
-			goto fail;
+			g_critical("Reading from standard input failed: %s",
+				g_strerror(errno));
+			return EX_IOERR;
 		}
 		p = buf;
 		do {
-			written = write(fd, p, ret);
+			written = write(fdout, p, ret);
 			if (!written)
-				goto fail;
+				return EX_IOERR;
 			if (written < 0) {
 				if (errno == EINTR)
 					continue;
-				g_critical("Writing to temporary file `%s' failed: %s",
-					info.tmpname, g_strerror(errno));
-				goto fail;
+				g_critical("Writing to temporary file failed: %s",
+					g_strerror(errno));
+				return EX_IOERR;
 			}
 			p += written;
 			ret -= written;
 		} while (ret);
 	}
 
-	close(fd);
+	return 0;
+}
+
+static int
+save_maildir(int fdin, const char *dir, int auto_create, char **path)
+{
+	int fdout, ret;
+	struct maildir_tmpcreate_info info;
+
+	maildir_tmpcreate_init(&info);
+	info.openmode = 0666;
+	info.maildir = dir;
+	info.doordie = 1;
+
+	while ((fdout = maildir_tmpcreate_fd(&info)) < 0)
+	{
+		if (errno == ENOENT && auto_create && maildir_mkdir(dir) == 0)
+		{
+			auto_create = 0;
+			continue;
+		}
+
+		g_critical("Failed to create temporary file `%s': %s",
+			info.tmpname, g_strerror(errno));
+		return EX_TEMPFAIL;
+	}
+
+	g_debug("Reading from standard input and writing to `%s'", info.tmpname);
+#ifdef HAVE_SPLICE
+	ret = g_getenv("NOTMUCH_DELIVER_NO_SPLICE")
+		? save_readwrite(fdin, fdout)
+		: save_splice(fdin, fdout);
+#else
+	ret = save_readwrite(fdin, fdout);
+#endif /* HAVE_SPLICE */
+	if (ret)
+		goto fail;
+
+	close(fdout);
 	g_debug("Moving `%s' to `%s'", info.tmpname, info.newname);
 	if (maildir_movetmpnew(info.tmpname, info.newname)) {
 		g_critical("Moving `%s' to `%s' failed: %s",
@@ -295,6 +368,9 @@ main(int argc, char **argv)
 		"  "PACKAGE" uses notmuch's configuration file to determine database path and\n"
 		"  initial tags to add to new messages. You may set NOTMUCH_CONFIG environment\n"
 		"  variable to specify an alternative configuration file.\n"
+		"\nEnvironment:\n"
+		"  NOTMUCH_CONFIG: Path to notmuch configuration file\n"
+		"  NOTMUCH_DELIVER_NO_SPLICE: Don't use splice() even if it's available\n"
 		"\nExit codes:\n"
 		"  0   => Successful run\n"
 		"  64  => Usage error\n"
-- 
2.45.2