maildir-save.c revision 5192589b0f8942a741c8ea58bb45377b2703d752
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen/* Copyright (c) 2002-2014 Dovecot authors, see the included COPYING file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "lib.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "ioloop.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "array.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "buffer.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "istream.h"
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen#include "istream-crlf.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "ostream.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "fdatasync-path.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "eacces-error.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "str.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "index-mail.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "maildir-storage.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "maildir-uidlist.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "maildir-keywords.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "maildir-filename.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "maildir-filename-flags.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "maildir-sync.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen#include <stdio.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <stdlib.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <unistd.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <fcntl.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <utime.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <sys/stat.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#define MAILDIR_FILENAME_FLAG_MOVED 0x10000000
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstruct maildir_filename {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *next;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *tmp_name, *dest_basename;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen const char *pop3_uidl, *guid;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uoff_t size, vsize;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen enum mail_flags flags;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int pop3_order;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int preserve_filename:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int keywords_count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* unsigned int keywords[]; */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen};
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstruct maildir_save_context {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_save_context ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen pool_t pool;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_mailbox *mbox;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_index_transaction *trans;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_keywords_sync_ctx *keywords_sync_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_index_sync_context *sync_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail *mail, *cur_dest_mail;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *tmpdir, *newdir, *curdir;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *files, **files_tail, *file_last;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int files_count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen buffer_t keywords_buffer;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ARRAY_TYPE(keyword_indexes) keywords_array;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen struct istream *input;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen int fd;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uint32_t first_seq, seq, last_nonrecent_uid;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int have_keywords:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int have_preserved_filenames:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int locked:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int failed:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int last_save_finished:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int locked_uidlist_refresh:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen};
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int maildir_file_move(struct maildir_save_context *ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf, const char *destname,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen bool newdir)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *tmp_path, *new_path;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(*destname != '\0');
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(*mf->tmp_name != '\0');
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* if we have flags, we'll move it to cur/ directly, because files in
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen new/ directory can't have flags. alternative would be to write it
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen in new/ and set the flags dirty in index file, but in that case
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen external MUAs would see wrong flags. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen tmp_path = t_strconcat(ctx->tmpdir, "/", mf->tmp_name, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen new_path = newdir ?
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_strconcat(ctx->newdir, "/", destname, NULL) :
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_strconcat(ctx->curdir, "/", destname, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* maildir spec says we should use link() + unlink() here. however
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen since our filename is guaranteed to be unique, rename() works just
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen as well, except faster. even if the filename wasn't unique, the
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen problem could still happen if the file was already moved from
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen new/ to cur/, so link() doesn't really provide any safety anyway.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen Besides the small temporary performance benefits, this rename() is
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen almost required with OSX's HFS+ filesystem, since it implements
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen hard links in a pretty ugly way, which makes the performance crawl
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen when a lot of hard links are used. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (rename(tmp_path, new_path) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->flags |= MAILDIR_FILENAME_FLAG_MOVED;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return 0;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (ENOSPACE(errno)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_error(storage, MAIL_ERROR_NOSPACE,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAIL_ERRSTR_NO_SPACE);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return -1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen tmp_path, new_path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return -1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainenstatic struct mail_save_context *
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainenmaildir_save_transaction_init(struct mailbox_transaction_context *t)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->box;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_save_context *ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *path;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen pool_t pool;
049da065aa64c1a5ed46eed6cde7382b011612a9Timo Sirainen
049da065aa64c1a5ed46eed6cde7382b011612a9Timo Sirainen pool = pool_alloconly_create("maildir_save_context", 4096);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx = p_new(pool, struct maildir_save_context, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->ctx.transaction = t;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen ctx->pool = pool;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->mbox = mbox;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen ctx->trans = t->itrans;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->files_tail = &ctx->files;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->fd = -1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen path = mailbox_get_path(&mbox->box);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen ctx->tmpdir = p_strconcat(pool, path, "/tmp", NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->newdir = p_strconcat(pool, path, "/new", NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->curdir = p_strconcat(pool, path, "/cur", NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen buffer_create_from_const_data(&ctx->keywords_buffer, "", 0);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen array_create_from_buffer(&ctx->keywords_array, &ctx->keywords_buffer,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen sizeof(unsigned int));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->last_save_finished = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return &ctx->ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstruct maildir_filename *
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_save_add(struct mail_save_context *_ctx, const char *tmp_fname,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail *src_mail)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_save_data *mdata = &_ctx->data;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct istream *input;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int keyword_count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(*tmp_fname != '\0');
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* allow caller to specify recent flag only when uid is specified
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (we're replicating, converting, etc.). */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mdata->uid == 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mdata->flags |= MAIL_RECENT;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen else if ((mdata->flags & MAIL_RECENT) == 0 &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->last_nonrecent_uid < mdata->uid)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->last_nonrecent_uid = mdata->uid;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* now, we want to be able to rollback the whole append session,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen so we'll just store the name of this temp file and move it later
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen into new/ or cur/. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* @UNSAFE */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen keyword_count = mdata->keywords == NULL ? 0 : mdata->keywords->count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf = p_malloc(ctx->pool, sizeof(*mf) +
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen sizeof(unsigned int) * keyword_count);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->tmp_name = mf->dest_basename = p_strdup(ctx->pool, tmp_fname);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->flags = mdata->flags;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->size = (uoff_t)-1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->vsize = (uoff_t)-1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->file_last = mf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(*ctx->files_tail == NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *ctx->files_tail = mf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->files_tail = &mf->next;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->files_count++;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mdata->keywords != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* @UNSAFE */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->keywords_count = keyword_count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen memcpy(mf + 1, mdata->keywords->idx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen sizeof(unsigned int) * keyword_count);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->have_keywords = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mdata->pop3_uidl != NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->pop3_uidl = p_strdup(ctx->pool, mdata->pop3_uidl);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->pop3_order = mdata->pop3_order;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* insert into index */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_index_append(ctx->trans, mdata->uid, &ctx->seq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_index_update_flags(ctx->trans, ctx->seq,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODIFY_REPLACE, mdata->flags & ~MAIL_RECENT);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mdata->keywords != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_index_update_keywords(ctx->trans, ctx->seq,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODIFY_REPLACE, mdata->keywords);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mdata->min_modseq != 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_index_update_modseq(ctx->trans, ctx->seq,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mdata->min_modseq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->first_seq == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->first_seq = ctx->seq;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(ctx->files->next == NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (_ctx->dest_mail == NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->mail == NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->mail = mail_alloc(_ctx->transaction, 0, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen _ctx->dest_mail = ctx->mail;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_set_seq_saving(_ctx->dest_mail, ctx->seq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->input == NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* copying with hardlinking. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(src_mail != NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_copy_cache_fields(_ctx, src_mail, ctx->seq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->cur_dest_mail = NULL;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen input = index_mail_cache_parse_init(_ctx->dest_mail,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->input);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_stream_unref(&ctx->input);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->input = input;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->cur_dest_mail = _ctx->dest_mail;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return mf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid maildir_save_set_dest_basename(struct mail_save_context *_ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *basename)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->preserve_filename = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->dest_basename = p_strdup(ctx->pool, basename);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->have_preserved_filenames = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid maildir_save_set_sizes(struct maildir_filename *mf,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uoff_t size, uoff_t vsize)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->size = size;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->vsize = vsize;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic bool
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_get_dest_filename(struct maildir_save_context *ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char **fname_r)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *basename = mf->dest_basename;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mf->size != (uoff_t)-1 && !mf->preserve_filename) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAILDIR_EXTRA_FILE_SIZE, mf->size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mf->vsize != (uoff_t)-1 && !mf->preserve_filename) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAILDIR_EXTRA_VIRTUAL_SIZE,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->vsize);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mf->keywords_count == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((mf->flags & MAIL_FLAGS_MASK) == MAIL_RECENT) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *fname_r = basename;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *fname_r = maildir_filename_flags_set(basename,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->flags & MAIL_FLAGS_MASK);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(ctx->keywords_sync_ctx != NULL || mf->keywords_count == 0);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen buffer_create_from_const_data(&ctx->keywords_buffer, mf + 1,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->keywords_count * sizeof(unsigned int));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *fname_r = maildir_filename_flags_kw_set(ctx->keywords_sync_ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen basename,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf->flags & MAIL_FLAGS_MASK,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen &ctx->keywords_array);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic const char *maildir_mf_get_path(struct maildir_save_context *ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *fname, *dir;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((mf->flags & MAILDIR_FILENAME_FLAG_MOVED) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* file is still in tmp/ */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return t_strdup_printf("%s/%s", ctx->tmpdir, mf->tmp_name);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* already moved to new/ or cur/ */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen dir = maildir_get_dest_filename(ctx, mf, &fname) ?
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->newdir : ctx->curdir;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return t_strdup_printf("%s/%s", dir, fname);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic struct maildir_filename *
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_save_get_mf(struct mailbox_transaction_context *t, uint32_t seq)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_save_context *save_ctx =
c8c4bbf6b1415e9d0845bc8f1cd6d19b76ab0392Timo Sirainen (struct maildir_save_context *)t->save_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(seq >= save_ctx->first_seq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen seq -= save_ctx->first_seq;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf = save_ctx->files;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen while (seq > 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf = mf->next;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(mf != NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen seq--;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return mf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenint maildir_save_file_get_size(struct mailbox_transaction_context *t,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uint32_t seq, bool vsize, uoff_t *size_r)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf = maildir_save_get_mf(t, seq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *size_r = vsize ? mf->vsize : mf->size;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return *size_r == (uoff_t)-1 ? -1 : 0;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenconst char *maildir_save_file_get_path(struct mailbox_transaction_context *t,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uint32_t seq)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_save_context *save_ctx =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (struct maildir_save_context *)t->save_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf = maildir_save_get_mf(t, seq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return maildir_mf_get_path(save_ctx, mf);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char **fname_r)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mailbox *box = &mbox->box;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const struct mailbox_permissions *perm = mailbox_get_permissions(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int prefix_len;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *tmp_fname;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen string_t *path;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mode_t old_mask;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen int fd;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen path = t_str_new(256);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str_append(path, dir);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str_append_c(path, '/');
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen prefix_len = str_len(path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen do {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen tmp_fname = maildir_filename_generate();
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str_truncate(path, prefix_len);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str_append(path, tmp_fname);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* the generated filename is unique. the only reason why it
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen might return an existing filename is if the time moved
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen backwards. so we'll use O_EXCL anyway, although it's mostly
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen useless. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen old_mask = umask(0777 & ~perm->file_create_mode);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen fd = open(str_c(path),
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen umask(old_mask);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } while (fd == -1 && errno == EEXIST);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *fname_r = tmp_fname;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (fd == -1) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ENOSPACE(errno)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_error(box->storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAIL_ERROR_NOSPACE, MAIL_ERRSTR_NO_SPACE);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(box->storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "open(%s) failed: %m", str_c(path));
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen } else if (perm->file_create_gid != (gid_t)-1) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (errno == EPERM) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(box->storage, "%s",
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen eperm_error_get_chgrp("fchown",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str_c(path),
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen perm->file_create_gid,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen perm->file_create_gid_origin));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(box->storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "fchown(%s) failed: %m", str_c(path));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return fd;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstruct mail_save_context *
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmaildir_save_alloc(struct mailbox_transaction_context *t)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (t->save_ctx == NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t->save_ctx = maildir_save_transaction_init(t);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return t->save_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenint maildir_save_begin(struct mail_save_context *_ctx, struct istream *input)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_filename *mf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* new mail, new failure state */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->failed = FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen T_BEGIN {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* create a new file in tmp/ directory */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *fname;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->fd = maildir_create_tmp(ctx->mbox, ctx->tmpdir, &fname);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->fd == -1)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen else {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->mbox->storage->storage.set->mail_save_crlf)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->input = i_stream_create_crlf(input);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen else
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->input = i_stream_create_lf(input);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mf = maildir_save_add(_ctx, fname, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (_ctx->data.guid != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen maildir_save_set_dest_basename(_ctx, mf,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen _ctx->data.guid);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen } T_END;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!ctx->failed) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen _ctx->data.output = o_stream_create_fd_file(ctx->fd, 0, FALSE);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen o_stream_cork(_ctx->data.output);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->last_save_finished = FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return ctx->failed ? -1 : 0;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenint maildir_save_continue(struct mail_save_context *_ctx)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->failed)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return -1;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen do {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "o_stream_send_istream(%s/%s) "
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "failed: %m",
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen ctx->tmpdir, ctx->file_last->tmp_name);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen ctx->failed = TRUE;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen return -1;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->cur_dest_mail != NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_mail_cache_parse_continue(ctx->cur_dest_mail);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* both tee input readers may consume data from our primary
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen input stream. we'll have to make sure we don't return with
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen one of the streams still having data in them. */
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen } while (i_stream_read(ctx->input) > 0);
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen return 0;
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen}
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainenstatic int maildir_save_finish_received_date(struct maildir_save_context *ctx,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *path)
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct utimbuf buf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct stat st;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->ctx.data.received_date != (time_t)-1) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* set the received_date by modifying mtime */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen buf.actime = ioloop_time;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen buf.modtime = ctx->ctx.data.received_date;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (utime(path, &buf) < 0) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen mail_storage_set_critical(storage,
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen "utime(%s) failed: %m", path);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen return -1;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen } else if (ctx->fd != -1) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (fstat(ctx->fd, &st) == 0)
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen ctx->ctx.data.received_date = st.st_mtime;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen else {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen mail_storage_set_critical(storage,
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen "fstat(%s) failed: %m", path);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen return -1;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen } else {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* hardlinked */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (stat(path, &st) == 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->ctx.data.received_date = st.st_mtime;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen else {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "stat(%s) failed: %m", path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return -1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return 0;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void maildir_save_remove_last_filename(struct maildir_save_context *ctx)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen struct maildir_filename **fm;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_index_expunge(ctx->trans, ctx->seq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* currently we can't just drop pending cache updates for this one
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen specific record, so we'll reset the whole cache transaction. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_cache_transaction_reset(ctx->ctx.transaction->cache_trans);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->seq--;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (fm = &ctx->files; (*fm)->next != NULL; fm = &(*fm)->next) ;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(*fm == ctx->file_last);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen *fm = NULL;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->files_tail = fm;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->file_last = NULL;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->files_count--;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainenstatic int maildir_save_finish_real(struct mail_save_context *_ctx)
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen{
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *path;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen off_t real_size;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uoff_t size;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen int output_errno;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->last_save_finished = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->failed && ctx->fd == -1) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* tmp file creation failed */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return -1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen path = t_strconcat(ctx->tmpdir, "/", ctx->file_last->tmp_name, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!ctx->failed && o_stream_nfinish(_ctx->data.output) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "write(%s) failed: %m", path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (_ctx->data.save_date != (time_t)-1) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we can't change ctime, but we can add the date to cache */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct index_mail *mail = (struct index_mail *)_ctx->dest_mail;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uint32_t t = _ctx->data.save_date;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_mail_cache_add(mail, MAIL_CACHE_SAVE_DATE, &t, sizeof(t));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (maildir_save_finish_received_date(ctx, path) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->cur_dest_mail != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_mail_cache_parse_deinit(ctx->cur_dest_mail,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->ctx.data.received_date,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen !ctx->failed);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_stream_unref(&ctx->input);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* remember the size in case we want to add it to filename */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->file_last->size = _ctx->data.output->offset;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->cur_dest_mail == NULL ||
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_get_virtual_size(ctx->cur_dest_mail,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen &ctx->file_last->vsize) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->file_last->vsize = (uoff_t)-1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen output_errno = _ctx->data.output->last_failed_errno;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen o_stream_destroy(&_ctx->data.output);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen !ctx->failed) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (fsync(ctx->fd) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "fsync(%s) failed: %m", path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen real_size = lseek(ctx->fd, 0, SEEK_END);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (real_size == (off_t)-1) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "lseek(%s) failed: %m", path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (real_size != (off_t)ctx->file_last->size &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (!maildir_filename_get_size(ctx->file_last->dest_basename,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAILDIR_EXTRA_FILE_SIZE, &size) ||
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen size != ctx->file_last->size)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* e.g. zlib plugin was used. the "physical size" must be in
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen the maildir filename, since stat() will return wrong size */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->file_last->preserve_filename = FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* preserve the GUID if needed */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->file_last->guid == NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->file_last->guid = ctx->file_last->dest_basename;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* reset the base name as well, just in case there's a
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ,W=vsize */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->file_last->dest_basename = ctx->file_last->tmp_name;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (close(ctx->fd) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "close(%s) failed: %m", path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->fd = -1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->failed) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* delete the tmp file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (unlink(path) < 0 && errno != ENOENT) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "unlink(%s) failed: %m", path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen errno = output_errno;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ENOSPACE(errno)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_error(storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAIL_ERROR_NOSPACE, MAIL_ERRSTR_NO_SPACE);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (errno != 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_set_critical(storage,
"write(%s) failed: %m", path);
}
maildir_save_remove_last_filename(ctx);
return -1;
}
ctx->file_last = NULL;
return 0;
}
int maildir_save_finish(struct mail_save_context *ctx)
{
int ret;
T_BEGIN {
ret = maildir_save_finish_real(ctx);
} T_END;
index_save_context_free(ctx);
return ret;
}
void maildir_save_cancel(struct mail_save_context *_ctx)
{
struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
ctx->failed = TRUE;
(void)maildir_save_finish(_ctx);
}
static void
maildir_save_unlink_files(struct maildir_save_context *ctx)
{
struct maildir_filename *mf;
for (mf = ctx->files; mf != NULL; mf = mf->next) T_BEGIN {
(void)unlink(maildir_mf_get_path(ctx, mf));
} T_END;
ctx->files = NULL;
}
static int maildir_transaction_fsync_dirs(struct maildir_save_context *ctx,
bool new_changed, bool cur_changed)
{
struct mail_storage *storage = &ctx->mbox->storage->storage;
if (storage->set->parsed_fsync_mode == FSYNC_MODE_NEVER)
return 0;
if (new_changed) {
if (fdatasync_path(ctx->newdir) < 0) {
mail_storage_set_critical(storage,
"fdatasync_path(%s) failed: %m", ctx->newdir);
return -1;
}
}
if (cur_changed) {
if (fdatasync_path(ctx->curdir) < 0) {
mail_storage_set_critical(storage,
"fdatasync_path(%s) failed: %m", ctx->curdir);
return -1;
}
}
return 0;
}
static int seq_range_cmp(const struct seq_range *r1, const struct seq_range *r2)
{
if (r1->seq1 < r2->seq2)
return -1;
else if (r1->seq1 > r2->seq2)
return 1;
else
return 0;
}
static uint32_t
maildir_save_set_recent_flags(struct maildir_save_context *ctx)
{
struct maildir_mailbox *mbox = ctx->mbox;
ARRAY_TYPE(seq_range) saved_sorted_uids;
const struct seq_range *uids;
unsigned int i, count;
uint32_t uid;
count = array_count(&ctx->ctx.transaction->changes->saved_uids);
if (count == 0)
return 0;
t_array_init(&saved_sorted_uids, count);
array_append_array(&saved_sorted_uids,
&ctx->ctx.transaction->changes->saved_uids);
array_sort(&saved_sorted_uids, seq_range_cmp);
uids = array_get(&saved_sorted_uids, &count);
for (i = 0; i < count; i++) {
for (uid = uids[i].seq1; uid <= uids[i].seq2; uid++)
index_mailbox_set_recent_uid(&mbox->box, uid);
}
return uids[count-1].seq2 + 1;
}
static int
maildir_save_sync_index(struct maildir_save_context *ctx)
{
struct mailbox_transaction_context *_t = ctx->ctx.transaction;
struct maildir_mailbox *mbox = ctx->mbox;
uint32_t first_uid, next_uid, first_recent_uid;
int ret;
/* we'll need to keep the lock past the sync deinit */
ret = maildir_uidlist_lock(mbox->uidlist);
i_assert(ret > 0);
if (maildir_sync_header_refresh(mbox) < 0)
return -1;
if ((ret = maildir_uidlist_refresh_fast_init(mbox->uidlist)) < 0)
return -1;
if (ret == 0) {
/* uidlist doesn't exist. make sure all existing message
are added to uidlist first. */
(void)maildir_storage_sync_force(mbox, 0);
}
if (maildir_sync_index_begin(mbox, NULL, &ctx->sync_ctx) < 0)
return -1;
ctx->keywords_sync_ctx =
maildir_sync_get_keywords_sync_ctx(ctx->sync_ctx);
/* now that uidlist is locked, make sure all the existing mails
have been added to index. we don't really look into the
maildir, just add all the new mails listed in
dovecot-uidlist to index. */
if (maildir_sync_index(ctx->sync_ctx, TRUE) < 0)
return -1;
/* if messages were added to index, assign them UIDs */
first_uid = maildir_uidlist_get_next_uid(mbox->uidlist);
i_assert(first_uid != 0);
mail_index_append_finish_uids(ctx->trans, first_uid,
&_t->changes->saved_uids);
i_assert(ctx->files_count == seq_range_count(&_t->changes->saved_uids));
/* these mails are all recent in our session */
T_BEGIN {
next_uid = maildir_save_set_recent_flags(ctx);
} T_END;
if ((mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0)
first_recent_uid = next_uid;
else if (ctx->last_nonrecent_uid != 0)
first_recent_uid = ctx->last_nonrecent_uid + 1;
else
first_recent_uid = 0;
if (first_recent_uid != 0) {
/* maildir_sync_index() dropped recent flags from
existing messages. we'll still need to drop recent
flags from these newly added messages. */
mail_index_update_header(ctx->trans,
offsetof(struct mail_index_header,
first_recent_uid),
&first_recent_uid,
sizeof(first_recent_uid), FALSE);
}
return 0;
}
static void
maildir_save_rollback_index_changes(struct maildir_save_context *ctx)
{
uint32_t seq;
if (ctx->seq == 0)
return;
for (seq = ctx->seq; seq >= ctx->first_seq; seq--)
mail_index_expunge(ctx->trans, seq);
mail_cache_transaction_reset(ctx->ctx.transaction->cache_trans);
}
static bool maildir_filename_has_conflict(struct maildir_filename *mf,
struct maildir_filename *prev_mf)
{
if (strcmp(mf->dest_basename, prev_mf->dest_basename) == 0) {
/* already used this */
return TRUE;
}
if (prev_mf->guid != NULL &&
strcmp(mf->dest_basename, prev_mf->guid) == 0) {
/* previous filename also had a conflict */
return TRUE;
}
return FALSE;
}
static void
maildir_filename_check_conflicts(struct maildir_save_context *ctx,
struct maildir_filename *mf,
struct maildir_filename *prev_mf)
{
uoff_t size;
if (!ctx->locked_uidlist_refresh && ctx->locked) {
(void)maildir_uidlist_refresh(ctx->mbox->uidlist);
ctx->locked_uidlist_refresh = TRUE;
}
if (!ctx->locked_uidlist_refresh ||
(prev_mf != NULL && maildir_filename_has_conflict(mf, prev_mf)) ||
maildir_uidlist_get_full_filename(ctx->mbox->uidlist,
mf->dest_basename) != NULL) {
/* file already exists. give it another name.
but preserve the size/vsize in the filename if possible */
if (mf->size == (uoff_t)-1 &&
maildir_filename_get_size(mf->dest_basename,
MAILDIR_EXTRA_FILE_SIZE, &size))
mf->size = size;
if (mf->vsize == (uoff_t)-1 &&
maildir_filename_get_size(mf->dest_basename,
MAILDIR_EXTRA_VIRTUAL_SIZE,
&size))
mf->vsize = size;
mf->guid = mf->dest_basename;
mf->dest_basename = p_strdup(ctx->pool,
maildir_filename_generate());
mf->preserve_filename = FALSE;
}
}
static int
maildir_filename_dest_basename_cmp(struct maildir_filename *const *f1,
struct maildir_filename *const *f2)
{
return strcmp((*f1)->dest_basename, (*f2)->dest_basename);
}
static int
maildir_save_move_files_to_newcur(struct maildir_save_context *ctx)
{
ARRAY(struct maildir_filename *) files;
struct maildir_filename *mf, *const *mfp, *prev_mf;
bool newdir, new_changed, cur_changed;
int ret;
/* put files into an array sorted by the destination filename.
this way we can easily check if there are duplicate destination
filenames within this transaction. */
t_array_init(&files, ctx->files_count);
for (mf = ctx->files; mf != NULL; mf = mf->next)
array_append(&files, &mf, 1);
array_sort(&files, maildir_filename_dest_basename_cmp);
new_changed = cur_changed = FALSE; prev_mf = FALSE;
array_foreach(&files, mfp) {
mf = *mfp;
T_BEGIN {
const char *dest;
if (mf->preserve_filename) {
maildir_filename_check_conflicts(ctx, mf,
prev_mf);
}
newdir = maildir_get_dest_filename(ctx, mf, &dest);
if (newdir)
new_changed = TRUE;
else
cur_changed = TRUE;
ret = maildir_file_move(ctx, mf, dest, newdir);
} T_END;
if (ret < 0)
return -1;
prev_mf = mf;
}
if (ctx->locked) {
i_assert(ctx->sync_ctx != NULL);
maildir_sync_set_new_msgs_count(ctx->sync_ctx,
array_count(&files));
}
return maildir_transaction_fsync_dirs(ctx, new_changed, cur_changed);
}
static void maildir_save_sync_uidlist(struct maildir_save_context *ctx)
{
struct mailbox_transaction_context *t = ctx->ctx.transaction;
struct maildir_filename *mf;
struct seq_range_iter iter;
enum maildir_uidlist_rec_flag flags;
struct maildir_uidlist_rec *rec;
unsigned int n = 0;
uint32_t uid;
bool newdir, bret;
int ret;
seq_range_array_iter_init(&iter, &t->changes->saved_uids);
for (mf = ctx->files; mf != NULL; mf = mf->next) T_BEGIN {
const char *dest;
bret = seq_range_array_iter_nth(&iter, n++, &uid);
i_assert(bret);
newdir = maildir_get_dest_filename(ctx, mf, &dest);
flags = MAILDIR_UIDLIST_REC_FLAG_RECENT;
if (newdir)
flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
ret = maildir_uidlist_sync_next_uid(ctx->uidlist_sync_ctx,
dest, uid, flags, &rec);
i_assert(ret > 0);
i_assert(rec != NULL);
if (mf->guid != NULL) {
maildir_uidlist_sync_set_ext(ctx->uidlist_sync_ctx, rec,
MAILDIR_UIDLIST_REC_EXT_GUID, mf->guid);
}
if (mf->pop3_uidl != NULL) {
maildir_uidlist_sync_set_ext(ctx->uidlist_sync_ctx, rec,
MAILDIR_UIDLIST_REC_EXT_POP3_UIDL,
mf->pop3_uidl);
}
if (mf->pop3_order > 0) {
maildir_uidlist_sync_set_ext(ctx->uidlist_sync_ctx, rec,
MAILDIR_UIDLIST_REC_EXT_POP3_ORDER,
t_strdup_printf("%u", mf->pop3_order));
}
} T_END;
i_assert(!seq_range_array_iter_nth(&iter, n, &uid));
}
int maildir_transaction_save_commit_pre(struct mail_save_context *_ctx)
{
struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
struct mailbox_transaction_context *_t = _ctx->transaction;
enum maildir_uidlist_sync_flags sync_flags;
int ret;
i_assert(_ctx->data.output == NULL);
i_assert(ctx->last_save_finished);
if (ctx->files_count == 0) {
/* the mail must be freed in the commit_pre() */
if (ctx->mail != NULL)
mail_free(&ctx->mail);
return 0;
}
sync_flags = MAILDIR_UIDLIST_SYNC_PARTIAL |
MAILDIR_UIDLIST_SYNC_NOREFRESH;
if ((_t->flags & MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS) != 0) {
/* we want to assign UIDs, we must lock uidlist */
} else if (ctx->have_keywords) {
/* keywords file updating relies on uidlist lock. */
} else if (ctx->have_preserved_filenames) {
/* we're trying to use some potentially existing filenames.
we must lock to avoid race conditions where two sessions
try to save the same filename. */
} else {
/* no requirement to lock uidlist. if we happen to get a lock,
assign uids. */
sync_flags |= MAILDIR_UIDLIST_SYNC_TRYLOCK;
}
ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, sync_flags,
&ctx->uidlist_sync_ctx);
if (ret > 0) {
ctx->locked = TRUE;
if (maildir_save_sync_index(ctx) < 0) {
maildir_transaction_save_rollback(_ctx);
return -1;
}
} else if (ret == 0 &&
(sync_flags & MAILDIR_UIDLIST_SYNC_TRYLOCK) != 0) {
ctx->locked = FALSE;
i_assert(ctx->uidlist_sync_ctx == NULL);
/* since we couldn't lock uidlist, we'll have to drop the
appends to index. */
maildir_save_rollback_index_changes(ctx);
} else {
maildir_transaction_save_rollback(_ctx);
return -1;
}
T_BEGIN {
ret = maildir_save_move_files_to_newcur(ctx);
} T_END;
if (ctx->locked) {
if (ret == 0) {
/* update dovecot-uidlist file. */
maildir_save_sync_uidlist(ctx);
}
if (maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx,
ret == 0) < 0)
ret = -1;
}
_t->changes->uid_validity =
maildir_uidlist_get_uid_validity(ctx->mbox->uidlist);
if (ctx->mail != NULL) {
/* Mail freeing may trigger cache updates and a call to
maildir_save_file_get_path(). Do this before finishing index
sync so we still have keywords_sync_ctx. */
mail_free(&ctx->mail);
}
if (ctx->locked) {
/* It doesn't matter if index syncing fails */
ctx->keywords_sync_ctx = NULL;
if (ret < 0)
maildir_sync_index_rollback(&ctx->sync_ctx);
else
(void)maildir_sync_index_commit(&ctx->sync_ctx);
}
if (ret < 0) {
ctx->keywords_sync_ctx = !ctx->have_keywords ? NULL :
maildir_keywords_sync_init(ctx->mbox->keywords,
ctx->mbox->box.index);
/* unlink the files we just moved in an attempt to rollback
the transaction. uidlist is still locked, so at least other
Dovecot instances haven't yet seen the files. we need
to have the keywords sync context to be able to generate
the destination filenames if keywords were used. */
maildir_save_unlink_files(ctx);
if (ctx->keywords_sync_ctx != NULL)
maildir_keywords_sync_deinit(&ctx->keywords_sync_ctx);
/* returning failure finishes the save_context */
maildir_transaction_save_rollback(_ctx);
return -1;
}
return 0;
}
void maildir_transaction_save_commit_post(struct mail_save_context *_ctx,
struct mail_index_transaction_commit_result *result ATTR_UNUSED)
{
struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
_ctx->transaction = NULL; /* transaction is already freed */
if (ctx->locked)
maildir_uidlist_unlock(ctx->mbox->uidlist);
pool_unref(&ctx->pool);
}
void maildir_transaction_save_rollback(struct mail_save_context *_ctx)
{
struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
i_assert(_ctx->data.output == NULL);
if (!ctx->last_save_finished)
maildir_save_cancel(&ctx->ctx);
/* delete files in tmp/ */
maildir_save_unlink_files(ctx);
if (ctx->uidlist_sync_ctx != NULL)
(void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, FALSE);
if (ctx->sync_ctx != NULL)
maildir_sync_index_rollback(&ctx->sync_ctx);
if (ctx->locked)
maildir_uidlist_unlock(ctx->mbox->uidlist);
if (ctx->mail != NULL)
mail_free(&ctx->mail);
pool_unref(&ctx->pool);
}