X-Git-Url: http://rtime.felk.cvut.cz/gitweb/notmuch.git/blobdiff_plain/602ac49eaca08741b0586e98e9e26779c65163ca..dcfcb4ba7b9dfdb49ec62d8fb47a72fb60540655:/notmuch-insert.c diff --git a/notmuch-insert.c b/notmuch-insert.c index 770275bb..a1d564c7 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -67,20 +67,21 @@ safe_gethostname (char *hostname, size_t len) static notmuch_bool_t sync_dir (const char *dir) { - notmuch_bool_t ret; - int fd; + int fd, r; fd = open (dir, O_RDONLY); if (fd == -1) { - fprintf (stderr, "Error: open() dir failed: %s\n", strerror (errno)); + fprintf (stderr, "Error: open %s: %s\n", dir, strerror (errno)); return FALSE; } - ret = (fsync (fd) == 0); - if (! ret) { - fprintf (stderr, "Error: fsync() dir failed: %s\n", strerror (errno)); - } + + r = fsync (fd); + if (r) + fprintf (stderr, "Error: fsync %s: %s\n", dir, strerror (errno)); + close (fd); - return ret; + + return r == 0; } /* @@ -103,97 +104,104 @@ is_valid_folder_name (const char *folder) } } -/* Make the given directory, succeeding if it already exists. */ +/* + * 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 -make_directory (char *path, int mode) +mkdir_recursive (const void *ctx, const char *path, int mode) { - notmuch_bool_t ret; - char *slash; + struct stat st; + int r; + char *parent = NULL, *slash; - if (mkdir (path, mode) != 0) - return (errno == EEXIST); + /* 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; + } - /* Sync the parent directory for durability. */ - ret = TRUE; - slash = strrchr (path, '/'); - if (slash) { - *slash = '\0'; - ret = sync_dir (path); - *slash = '/'; + return TRUE; + } else if (errno != ENOENT) { + fprintf (stderr, "Error: stat '%s': %s\n", path, strerror (errno)); + return FALSE; } - return ret; -} - -/* Make the given directory including its parent directories as necessary. - * Return TRUE on success, FALSE on error. */ -static notmuch_bool_t -make_directory_and_parents (char *path, int mode) -{ - struct stat st; - char *start; - char *end; - notmuch_bool_t ret; - /* First check the common case: directory already exists. */ - if (stat (path, &st) == 0) - return S_ISDIR (st.st_mode) ? TRUE : FALSE; - - for (start = path; *start != '\0'; start = end + 1) { - /* start points to the first unprocessed character. - * Find the next slash from start onwards. */ - end = strchr (start, '/'); - - /* If there are no more slashes then all the parent directories - * have been made. Now attempt to make the whole path. */ - if (end == NULL) - return make_directory (path, mode); - - /* Make the path up to the next slash, unless the current - * directory component is actually empty. */ - if (end > start) { - *end = '\0'; - ret = make_directory (path, mode); - *end = '/'; - if (! ret) - 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; } - return TRUE; + 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. dir and its subdirectories - * 'cur', 'new', 'tmp'. */ +/* + * Create the given maildir folder, i.e. maildir and its + * subdirectories cur/new/tmp. Return TRUE on success, FALSE + * otherwise. Partial results are not cleaned up on errors. + */ static notmuch_bool_t -maildir_create_folder (void *ctx, const char *dir) +maildir_create_folder (const void *ctx, const char *maildir) { + const char *subdirs[] = { "cur", "new", "tmp" }; const int mode = 0700; char *subdir; - char *tail; + unsigned int i; - /* Create 'cur' directory, including parent directories. */ - subdir = talloc_asprintf (ctx, "%s/cur", dir); - if (! subdir) { - fprintf (stderr, "Out of memory.\n"); - return FALSE; + for (i = 0; i < ARRAY_SIZE (subdirs); i++) { + subdir = talloc_asprintf (ctx, "%s/%s", maildir, subdirs[i]); + if (! subdir) { + fprintf (stderr, "Error: %s\n", strerror (ENOMEM)); + return FALSE; + } + + if (! mkdir_recursive (ctx, subdir, mode)) + return FALSE; } - if (! make_directory_and_parents (subdir, mode)) - return FALSE; - tail = subdir + strlen (subdir) - 3; + return TRUE; +} + +/* + * Generate a temporary file basename, no path, do not create an + * actual file. Return the basename, or NULL on errors. + */ +static char * +tempfilename (const void *ctx) +{ + char *filename; + char hostname[256]; + struct timeval tv; + pid_t pid; - /* Create 'new' directory. */ - strcpy (tail, "new"); - if (! make_directory (subdir, mode)) - return FALSE; + /* We follow the Dovecot file name generation algorithm. */ + pid = getpid (); + safe_gethostname (hostname, sizeof (hostname)); + gettimeofday (&tv, NULL); - /* Create 'tmp' directory. */ - strcpy (tail, "tmp"); - if (! make_directory (subdir, mode)) - return FALSE; + filename = talloc_asprintf (ctx, "%ld.M%ldP%d.%s", + tv.tv_sec, tv.tv_usec, pid, hostname); + if (! filename) + fprintf (stderr, "Error: %s\n", strerror (ENOMEM)); - talloc_free (subdir); - return TRUE; + return filename; } /* Open a unique file in the 'tmp' sub-directory of dir. @@ -205,23 +213,13 @@ static int maildir_open_tmp_file (void *ctx, const char *dir, char **tmppath, char **newpath, char **newdir) { - pid_t pid; - char hostname[256]; - struct timeval tv; char *filename; int fd = -1; - /* We follow the Dovecot file name generation algorithm. */ - pid = getpid (); - safe_gethostname (hostname, sizeof (hostname)); do { - gettimeofday (&tv, NULL); - filename = talloc_asprintf (ctx, "%ld.M%ldP%d.%s", - tv.tv_sec, tv.tv_usec, pid, hostname); - if (! filename) { - fprintf (stderr, "Out of memory\n"); + filename = tempfilename (ctx); + if (! filename) return -1; - } *tmppath = talloc_asprintf (ctx, "%s/tmp/%s", dir, filename); if (! *tmppath) { @@ -251,11 +249,12 @@ maildir_open_tmp_file (void *ctx, const char *dir, return fd; } -/* Copy the contents of standard input (fdin) into fdout. - * Returns TRUE if a non-empty file was written successfully. - * Otherwise, return FALSE. */ +/* + * Copy fdin to fdout, return TRUE on success, and FALSE on errors and + * empty input. + */ static notmuch_bool_t -copy_stdin (int fdin, int fdout) +copy_fd (int fdout, int fdin) { notmuch_bool_t empty = TRUE; @@ -294,52 +293,6 @@ copy_stdin (int fdin, int fdout) return (!interrupted && !empty); } -/* Add the specified message file to the notmuch database, applying tags. - * The file is renamed to encode notmuch tags as maildir flags. */ -static void -add_file_to_database (notmuch_database_t *notmuch, const char *path, - tag_op_list_t *tag_ops, notmuch_bool_t synchronize_flags) -{ - notmuch_message_t *message; - notmuch_status_t status; - - status = notmuch_database_add_message (notmuch, path, &message); - switch (status) { - case NOTMUCH_STATUS_SUCCESS: - case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: - break; - default: - case NOTMUCH_STATUS_FILE_NOT_EMAIL: - case NOTMUCH_STATUS_READ_ONLY_DATABASE: - case NOTMUCH_STATUS_XAPIAN_EXCEPTION: - case NOTMUCH_STATUS_OUT_OF_MEMORY: - case NOTMUCH_STATUS_FILE_ERROR: - case NOTMUCH_STATUS_NULL_POINTER: - case NOTMUCH_STATUS_TAG_TOO_LONG: - case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: - case NOTMUCH_STATUS_UNBALANCED_ATOMIC: - case NOTMUCH_STATUS_LAST_STATUS: - fprintf (stderr, "Error: failed to add `%s' to notmuch database: %s\n", - path, notmuch_status_to_string (status)); - return; - } - - if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) { - /* Don't change tags of an existing message. */ - if (synchronize_flags) { - status = notmuch_message_tags_to_maildir_flags (message); - if (status != NOTMUCH_STATUS_SUCCESS) - fprintf (stderr, "Error: failed to sync tags to maildir flags\n"); - } - } else { - tag_op_flag_t flags = synchronize_flags ? TAG_FLAG_MAILDIR_SYNC : 0; - - tag_op_list_apply (message, tag_ops, flags); - } - - notmuch_message_destroy (message); -} - static notmuch_bool_t write_message (void *ctx, int fdin, const char *dir, char **newpath) { @@ -354,7 +307,7 @@ write_message (void *ctx, int fdin, const char *dir, char **newpath) cleanup_path = tmppath; - if (! copy_stdin (fdin, fdout)) + if (! copy_fd (fdout, fdin)) goto FAIL; if (fsync (fdout) != 0) { @@ -389,6 +342,52 @@ write_message (void *ctx, int fdin, const char *dir, char **newpath) return FALSE; } +/* Add the specified message file to the notmuch database, applying tags. + * The file is renamed to encode notmuch tags as maildir flags. */ +static void +add_file_to_database (notmuch_database_t *notmuch, const char *path, + tag_op_list_t *tag_ops, notmuch_bool_t synchronize_flags) +{ + notmuch_message_t *message; + notmuch_status_t status; + + status = notmuch_database_add_message (notmuch, path, &message); + switch (status) { + case NOTMUCH_STATUS_SUCCESS: + case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: + break; + default: + case NOTMUCH_STATUS_FILE_NOT_EMAIL: + case NOTMUCH_STATUS_READ_ONLY_DATABASE: + case NOTMUCH_STATUS_XAPIAN_EXCEPTION: + case NOTMUCH_STATUS_OUT_OF_MEMORY: + case NOTMUCH_STATUS_FILE_ERROR: + case NOTMUCH_STATUS_NULL_POINTER: + case NOTMUCH_STATUS_TAG_TOO_LONG: + case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: + case NOTMUCH_STATUS_UNBALANCED_ATOMIC: + case NOTMUCH_STATUS_LAST_STATUS: + fprintf (stderr, "Error: failed to add `%s' to notmuch database: %s\n", + path, notmuch_status_to_string (status)); + return; + } + + if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) { + /* Don't change tags of an existing message. */ + if (synchronize_flags) { + status = notmuch_message_tags_to_maildir_flags (message); + if (status != NOTMUCH_STATUS_SUCCESS) + fprintf (stderr, "Error: failed to sync tags to maildir flags\n"); + } + } else { + tag_op_flag_t flags = synchronize_flags ? TAG_FLAG_MAILDIR_SYNC : 0; + + tag_op_list_apply (message, tag_ops, flags); + } + + notmuch_message_destroy (message); +} + int notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) { @@ -461,11 +460,8 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; } - if (create_folder && ! maildir_create_folder (config, maildir)) { - fprintf (stderr, "Error: creating maildir %s: %s\n", - maildir, strerror (errno)); + if (create_folder && ! maildir_create_folder (config, maildir)) return EXIT_FAILURE; - } } /* Setup our handler for SIGINT. We do not set SA_RESTART so that copying