--- /dev/null
+Return-Path: <bremner@tesseract.cs.unb.ca>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id 80ABA431FD8\r
+ for <notmuch@notmuchmail.org>; Sun, 28 Sep 2014 11:29:24 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+ autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id xS4RMl-BkY4h for <notmuch@notmuchmail.org>;\r
+ Sun, 28 Sep 2014 11:29:19 -0700 (PDT)\r
+Received: from yantan.tethera.net (yantan.tethera.net [199.188.72.155])\r
+ (using TLSv1 with cipher DHE-RSA-AES128-SHA (128/128 bits))\r
+ (No client certificate requested)\r
+ by olra.theworths.org (Postfix) with ESMTPS id B2600431FDC\r
+ for <notmuch@notmuchmail.org>; Sun, 28 Sep 2014 11:29:07 -0700 (PDT)\r
+Received: from remotemail by yantan.tethera.net with local (Exim 4.80)\r
+ (envelope-from <bremner@tesseract.cs.unb.ca>)\r
+ id 1XYJDX-0005gR-8c; Sun, 28 Sep 2014 15:29:07 -0300\r
+Received: (nullmailer pid 31288 invoked by uid 1000); Sun, 28 Sep 2014\r
+ 18:28:49 -0000\r
+From: David Bremner <david@tethera.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: [WIP 2/3] cli/insert: move several file/directory manipulation\r
+ routines into a new util file.\r
+Date: Sun, 28 Sep 2014 20:28:18 +0200\r
+Message-Id: <1411928899-29625-3-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 2.1.0\r
+In-Reply-To: <1411928899-29625-1-git-send-email-david@tethera.net>\r
+References: <87iok8vog6.fsf@steelpick.2x.cz>\r
+ <1411928899-29625-1-git-send-email-david@tethera.net>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Sun, 28 Sep 2014 18:29:25 -0000\r
+\r
+This will promote code sharing of low level file operations.\r
+---\r
+ notmuch-insert.c | 79 ++----------------------------------------\r
+ util/Makefile.local | 1 +\r
+ util/file-util.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++\r
+ util/file-util.h | 20 +++++++++++\r
+ 4 files changed, 121 insertions(+), 77 deletions(-)\r
+ create mode 100644 util/file-util.c\r
+ create mode 100644 util/file-util.h\r
+\r
+diff --git a/notmuch-insert.c b/notmuch-insert.c\r
+index 5ef6e66..48bdd28 100644\r
+--- a/notmuch-insert.c\r
++++ b/notmuch-insert.c\r
+@@ -23,6 +23,7 @@\r
+ \r
+ #include "notmuch-client.h"\r
+ #include "tag-util.h"\r
++#include "file-util.h"\r
+ \r
+ #include <sys/types.h>\r
+ #include <sys/stat.h>\r
+@@ -63,26 +64,6 @@ safe_gethostname (char *hostname, size_t len)\r
+ }\r
+ }\r
+ \r
+-/* Call fsync() on a directory path. */\r
+-static notmuch_bool_t\r
+-sync_dir (const char *dir)\r
+-{\r
+- int fd, r;\r
+-\r
+- fd = open (dir, O_RDONLY);\r
+- if (fd == -1) {\r
+- fprintf (stderr, "Error: open %s: %s\n", dir, strerror (errno));\r
+- return FALSE;\r
+- }\r
+-\r
+- r = fsync (fd);\r
+- if (r)\r
+- fprintf (stderr, "Error: fsync %s: %s\n", dir, strerror (errno));\r
+-\r
+- close (fd);\r
+-\r
+- return r == 0;\r
+-}\r
+ \r
+ /*\r
+ * Check the specified folder name does not contain a directory\r
+@@ -92,65 +73,9 @@ sync_dir (const char *dir)\r
+ static notmuch_bool_t\r
+ is_valid_folder_name (const char *folder)\r
+ {\r
+- const char *p = folder;\r
+-\r
+- for (;;) {\r
+- if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/'))\r
+- return FALSE;\r
+- p = strchr (p, '/');\r
+- if (!p)\r
+- return TRUE;\r
+- p++;\r
+- }\r
++ return ! has_double_dot_component (folder);\r
+ }\r
+ \r
+-/*\r
+- * Make the given directory and its parents as necessary, using the\r
+- * given mode. Return TRUE on success, FALSE otherwise. Partial\r
+- * results are not cleaned up on errors.\r
+- */\r
+-static notmuch_bool_t\r
+-mkdir_recursive (const void *ctx, const char *path, int mode)\r
+-{\r
+- struct stat st;\r
+- int r;\r
+- char *parent = NULL, *slash;\r
+-\r
+- /* First check the common case: directory already exists. */\r
+- r = stat (path, &st);\r
+- if (r == 0) {\r
+- if (! S_ISDIR (st.st_mode)) {\r
+- fprintf (stderr, "Error: '%s' is not a directory: %s\n",\r
+- path, strerror (EEXIST));\r
+- return FALSE;\r
+- }\r
+-\r
+- return TRUE;\r
+- } else if (errno != ENOENT) {\r
+- fprintf (stderr, "Error: stat '%s': %s\n", path, strerror (errno));\r
+- return FALSE;\r
+- }\r
+-\r
+- /* mkdir parents, if any */\r
+- slash = strrchr (path, '/');\r
+- if (slash && slash != path) {\r
+- parent = talloc_strndup (ctx, path, slash - path);\r
+- if (! parent) {\r
+- fprintf (stderr, "Error: %s\n", strerror (ENOMEM));\r
+- return FALSE;\r
+- }\r
+-\r
+- if (! mkdir_recursive (ctx, parent, mode))\r
+- return FALSE;\r
+- }\r
+-\r
+- if (mkdir (path, mode)) {\r
+- fprintf (stderr, "Error: mkdir '%s': %s\n", path, strerror (errno));\r
+- return FALSE;\r
+- }\r
+-\r
+- return parent ? sync_dir (parent) : TRUE;\r
+-}\r
+ \r
+ /*\r
+ * Create the given maildir folder, i.e. maildir and its\r
+diff --git a/util/Makefile.local b/util/Makefile.local\r
+index 905f237..8749cfb 100644\r
+--- a/util/Makefile.local\r
++++ b/util/Makefile.local\r
+@@ -5,6 +5,7 @@ extra_cflags += -I$(srcdir)/$(dir)\r
+ \r
+ libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c \\r
+ $(dir)/string-util.c $(dir)/talloc-extra.c $(dir)/zlib-extra.c \\r
++ $(dir)/file-util.c \\r
+ $(dir)/util.c\r
+ \r
+ libutil_modules := $(libutil_c_srcs:.c=.o)\r
+diff --git a/util/file-util.c b/util/file-util.c\r
+new file mode 100644\r
+index 0000000..66c4485\r
+--- /dev/null\r
++++ b/util/file-util.c\r
+@@ -0,0 +1,98 @@\r
++#include <errno.h>\r
++#include <fcntl.h>\r
++#include <stdio.h>\r
++#include <string.h>\r
++#include <sys/stat.h>\r
++#include <sys/types.h>\r
++#include <talloc.h>\r
++#include <unistd.h>\r
++\r
++#include "file-util.h"\r
++\r
++/*\r
++ * Check whether specified path name contains a directory\r
++ * component "..". to prevent writes outside of the Maildir\r
++ */\r
++int\r
++has_double_dot_component (const char *path)\r
++{\r
++ const char *p = path;\r
++\r
++ for (;;) {\r
++ if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/'))\r
++ return TRUE;\r
++ p = strchr (p, '/');\r
++ if (!p)\r
++ return FALSE;\r
++ p++;\r
++ }\r
++}\r
++\r
++/*\r
++ * Make the given directory and its parents as necessary, using the\r
++ * given mode. return TRUE on success, 0 otherwise. Partial\r
++ * results are not cleaned up on errors.\r
++ */\r
++int\r
++mkdir_recursive (const void *ctx, const char *path, int mode)\r
++{\r
++ struct stat st;\r
++ int r;\r
++ char *parent = NULL, *slash;\r
++\r
++ /* First check the common case: directory already exists. */\r
++ r = stat (path, &st);\r
++ if (r == 0) {\r
++ if (! S_ISDIR (st.st_mode)) {\r
++ fprintf (stderr, "Error: '%s' is not a directory: %s\n",\r
++ path, strerror (EEXIST));\r
++ return FALSE;\r
++ }\r
++\r
++ return TRUE;\r
++ } else if (errno != ENOENT) {\r
++ fprintf (stderr, "Error: stat '%s': %s\n", path, strerror (errno));\r
++ return FALSE;\r
++ }\r
++\r
++ /* mkdir parents, if any */\r
++ slash = strrchr (path, '/');\r
++ if (slash && slash != path) {\r
++ parent = talloc_strndup (ctx, path, slash - path);\r
++ if (! parent) {\r
++ fprintf (stderr, "Error: %s\n", strerror (ENOMEM));\r
++ return FALSE;\r
++ }\r
++\r
++ if (! mkdir_recursive (ctx, parent, mode))\r
++ return FALSE;\r
++ }\r
++\r
++ if (mkdir (path, mode)) {\r
++ fprintf (stderr, "Error: mkdir '%s': %s\n", path, strerror (errno));\r
++ return FALSE;\r
++ }\r
++\r
++ return parent ? sync_dir (parent) : TRUE;\r
++}\r
++\r
++/* Call fsync() on a directory path. */\r
++int\r
++sync_dir (const char *dir)\r
++{\r
++ int fd, r;\r
++\r
++ fd = open (dir, O_RDONLY);\r
++ if (fd == -1) {\r
++ fprintf (stderr, "Error: open %s: %s\n", dir, strerror (errno));\r
++ return FALSE;\r
++ }\r
++\r
++ r = fsync (fd);\r
++ if (r)\r
++ fprintf (stderr, "Error: fsync %s: %s\n", dir, strerror (errno));\r
++\r
++ close (fd);\r
++\r
++ return r == 0;\r
++}\r
+diff --git a/util/file-util.h b/util/file-util.h\r
+new file mode 100644\r
+index 0000000..4f96957\r
+--- /dev/null\r
++++ b/util/file-util.h\r
+@@ -0,0 +1,20 @@\r
++#ifndef _FILE_UTIL_H\r
++#define _FILE_UTIL_H\r
++\r
++#ifndef FALSE \r
++#define FALSE 0\r
++#endif\r
++#ifndef TRUE\r
++#define TRUE 1\r
++#endif\r
++\r
++int\r
++has_double_dot_component (const char *path);\r
++\r
++int\r
++mkdir_recursive (const void *ctx, const char *path, int mode);\r
++\r
++int\r
++sync_dir (const char *path);\r
++\r
++#endif\r
+-- \r
+2.1.0\r
+\r