This will promote code sharing of low level file operations. --- notmuch-insert.c | 79 ++---------------------------------------- util/Makefile.local | 1 + util/file-util.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ util/file-util.h | 20 +++++++++++ 4 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 util/file-util.c create mode 100644 util/file-util.h diff --git a/notmuch-insert.c b/notmuch-insert.c index 5ef6e66..48bdd28 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -23,6 +23,7 @@ #include "notmuch-client.h" #include "tag-util.h" +#include "file-util.h" #include <sys/types.h> #include <sys/stat.h> @@ -63,26 +64,6 @@ safe_gethostname (char *hostname, size_t len) } } -/* Call fsync() on a directory path. */ -static notmuch_bool_t -sync_dir (const char *dir) -{ - int fd, r; - - fd = open (dir, O_RDONLY); - if (fd == -1) { - fprintf (stderr, "Error: open %s: %s\n", dir, strerror (errno)); - return FALSE; - } - - r = fsync (fd); - if (r) - fprintf (stderr, "Error: fsync %s: %s\n", dir, strerror (errno)); - - close (fd); - - return r == 0; -} /* * Check the specified folder name does not contain a directory @@ -92,65 +73,9 @@ sync_dir (const char *dir) static notmuch_bool_t is_valid_folder_name (const char *folder) { - const char *p = folder; - - for (;;) { - if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/')) - return FALSE; - p = strchr (p, '/'); - if (!p) - return TRUE; - p++; - } + return ! has_double_dot_component (folder); } -/* - * Make the given directory and its parents as necessary, using the - * given mode. Return TRUE on success, FALSE otherwise. Partial - * results are not cleaned up on errors. - */ -static notmuch_bool_t -mkdir_recursive (const void *ctx, const char *path, int mode) -{ - struct stat st; - int r; - char *parent = NULL, *slash; - - /* First check the common case: directory already exists. */ - r = stat (path, &st); - if (r == 0) { - if (! S_ISDIR (st.st_mode)) { - fprintf (stderr, "Error: '%s' is not a directory: %s\n", - path, strerror (EEXIST)); - return FALSE; - } - - return TRUE; - } else if (errno != ENOENT) { - fprintf (stderr, "Error: stat '%s': %s\n", path, strerror (errno)); - return FALSE; - } - - /* mkdir parents, if any */ - slash = strrchr (path, '/'); - if (slash && slash != path) { - parent = talloc_strndup (ctx, path, slash - path); - if (! parent) { - fprintf (stderr, "Error: %s\n", strerror (ENOMEM)); - return FALSE; - } - - if (! mkdir_recursive (ctx, parent, mode)) - return FALSE; - } - - if (mkdir (path, mode)) { - fprintf (stderr, "Error: mkdir '%s': %s\n", path, strerror (errno)); - return FALSE; - } - - return parent ? sync_dir (parent) : TRUE; -} /* * Create the given maildir folder, i.e. maildir and its diff --git a/util/Makefile.local b/util/Makefile.local index 905f237..8749cfb 100644 --- a/util/Makefile.local +++ b/util/Makefile.local @@ -5,6 +5,7 @@ extra_cflags += -I$(srcdir)/$(dir) libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c \ $(dir)/string-util.c $(dir)/talloc-extra.c $(dir)/zlib-extra.c \ + $(dir)/file-util.c \ $(dir)/util.c libutil_modules := $(libutil_c_srcs:.c=.o) diff --git a/util/file-util.c b/util/file-util.c new file mode 100644 index 0000000..66c4485 --- /dev/null +++ b/util/file-util.c @@ -0,0 +1,98 @@ +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <talloc.h> +#include <unistd.h> + +#include "file-util.h" + +/* + * Check whether specified path name contains a directory + * component "..". to prevent writes outside of the Maildir + */ +int +has_double_dot_component (const char *path) +{ + const char *p = path; + + for (;;) { + if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/')) + return TRUE; + p = strchr (p, '/'); + if (!p) + return FALSE; + p++; + } +} + +/* + * Make the given directory and its parents as necessary, using the + * given mode. return TRUE on success, 0 otherwise. Partial + * results are not cleaned up on errors. + */ +int +mkdir_recursive (const void *ctx, const char *path, int mode) +{ + struct stat st; + int r; + char *parent = NULL, *slash; + + /* First check the common case: directory already exists. */ + r = stat (path, &st); + if (r == 0) { + if (! S_ISDIR (st.st_mode)) { + fprintf (stderr, "Error: '%s' is not a directory: %s\n", + path, strerror (EEXIST)); + return FALSE; + } + + return TRUE; + } else if (errno != ENOENT) { + fprintf (stderr, "Error: stat '%s': %s\n", path, strerror (errno)); + return FALSE; + } + + /* mkdir parents, if any */ + slash = strrchr (path, '/'); + if (slash && slash != path) { + parent = talloc_strndup (ctx, path, slash - path); + if (! parent) { + fprintf (stderr, "Error: %s\n", strerror (ENOMEM)); + return FALSE; + } + + if (! mkdir_recursive (ctx, parent, mode)) + return FALSE; + } + + if (mkdir (path, mode)) { + fprintf (stderr, "Error: mkdir '%s': %s\n", path, strerror (errno)); + return FALSE; + } + + return parent ? sync_dir (parent) : TRUE; +} + +/* Call fsync() on a directory path. */ +int +sync_dir (const char *dir) +{ + int fd, r; + + fd = open (dir, O_RDONLY); + if (fd == -1) { + fprintf (stderr, "Error: open %s: %s\n", dir, strerror (errno)); + return FALSE; + } + + r = fsync (fd); + if (r) + fprintf (stderr, "Error: fsync %s: %s\n", dir, strerror (errno)); + + close (fd); + + return r == 0; +} diff --git a/util/file-util.h b/util/file-util.h new file mode 100644 index 0000000..4f96957 --- /dev/null +++ b/util/file-util.h @@ -0,0 +1,20 @@ +#ifndef _FILE_UTIL_H +#define _FILE_UTIL_H + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +int +has_double_dot_component (const char *path); + +int +mkdir_recursive (const void *ctx, const char *path, int mode); + +int +sync_dir (const char *path); + +#endif -- 2.1.0