maildir-save.c revision a058590b1c1834650b1f7e552322b30566ab31b7
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "lib.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "ioloop.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "array.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "buffer.h"
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen#include "istream.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "istream-crlf.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "ostream.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "str.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "index-mail.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "maildir-storage.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "maildir-uidlist.h"
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include "maildir-keywords.h"
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include "maildir-filename.h"
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include "maildir-sync.h"
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include <stdio.h>
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include <stdlib.h>
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include <unistd.h>
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include <fcntl.h>
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include <utime.h>
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#include <sys/stat.h>
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainenstruct maildir_filename {
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct maildir_filename *next;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen const char *basename;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen uoff_t size, vsize;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen enum mail_flags flags;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen unsigned int keywords_count;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen /* unsigned int keywords[]; */
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen};
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainenstruct maildir_save_context {
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct mail_save_context ctx;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen pool_t pool;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct maildir_mailbox *mbox;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct mail_index_transaction *trans;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct maildir_keywords_sync_ctx *keywords_sync_ctx;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct maildir_index_sync_context *sync_ctx;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct mail *mail, *cur_dest_mail;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen const char *tmpdir, *newdir, *curdir;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen struct maildir_filename *files, **files_tail, *file_last;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen unsigned int files_count;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen buffer_t *keywords_buffer;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen ARRAY_TYPE(keyword_indexes) keywords_array;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen struct istream *input;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen struct ostream *output;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen int fd;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen time_t received_date;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen uint32_t first_seq, seq;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen unsigned int want_mails:1;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen unsigned int have_keywords:1;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen unsigned int locked:1;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen unsigned int failed:1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int moving:1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int finished:1;
9d3ccd79130199ffdb19a688027d49bf20a4aaaaTimo Sirainen};
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int maildir_file_move(struct maildir_save_context *ctx,
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen const char *tmpname, const char *destname,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bool newdir)
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *tmp_path, *new_path;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen int ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* if we have flags, we'll move it to cur/ directly, because files in
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen new/ directory can't have flags. alternative would be to write it
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen in new/ and set the flags dirty in index file, but in that case
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen external MUAs would see wrong flags. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen tmp_path = t_strconcat(ctx->tmpdir, "/", tmpname, NULL);
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen new_path = newdir ?
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen t_strconcat(ctx->newdir, "/", destname, NULL) :
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen t_strconcat(ctx->curdir, "/", destname, NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* maildir spec says we should use link() + unlink() here. however
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen since our filename is guaranteed to be unique, rename() works just
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen as well, except faster. even if the filename wasn't unique, the
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen problem could still happen if the file was already moved from
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen new/ to cur/, so link() doesn't really provide any safety anyway.
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen Besides the small temporary performance benefits, this rename() is
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen almost required with OSX's HFS+ filesystem, since it implements
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen hard links in a pretty ugly way, which makes the performance crawl
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen when a lot of hard links are used. */
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen if (rename(tmp_path, new_path) == 0)
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen ret = 0;
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ENOSPACE(errno)) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_error(storage,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen MAIL_ERROR_NOSPACE, MAIL_ERRSTR_NO_SPACE);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_critical(storage,
0cea9b1f4fa0495a48f5f097e40492517d67e1baTimo Sirainen "rename(%s, %s) failed: %m",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen tmp_path, new_path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
0cea9b1f4fa0495a48f5f097e40492517d67e1baTimo Sirainen return ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstruct maildir_save_context *
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenmaildir_save_transaction_init(struct maildir_transaction_context *t)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->ictx.ibox;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_save_context *ctx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen pool_t pool;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen pool = pool_alloconly_create("maildir_save_context", 4096);
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen ctx = p_new(pool, struct maildir_save_context, 1);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ctx->ctx.transaction = &t->ictx.mailbox_ctx;
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen ctx->pool = pool;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->mbox = mbox;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->trans = t->ictx.trans;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen ctx->files_tail = &ctx->files;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->tmpdir = p_strconcat(pool, mbox->path, "/tmp", NULL);
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen ctx->newdir = p_strconcat(pool, mbox->path, "/new", NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->curdir = p_strconcat(pool, mbox->path, "/cur", NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->keywords_buffer = buffer_create_const_data(pool, NULL, 0);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen array_create_from_buffer(&ctx->keywords_array, ctx->keywords_buffer,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sizeof(unsigned int));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->finished = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return ctx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenuint32_t maildir_save_add(struct maildir_transaction_context *t,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *base_fname, enum mail_flags flags,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail_keywords *keywords,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail *dest_mail)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_save_context *ctx = t->save_ctx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_filename *mf;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen struct istream *input;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen /* now, we want to be able to rollback the whole append session,
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen so we'll just store the name of this temp file and move it later
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen into new/ or cur/. */
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen /* @UNSAFE */
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen mf = p_malloc(ctx->pool, sizeof(*mf) +
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen sizeof(unsigned int) * (keywords == NULL ? 0 :
ea4ca37ec14913354f3a0deebc0df96097eb9468Timo Sirainen keywords->count));
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen mf->basename = p_strdup(ctx->pool, base_fname);
ea4ca37ec14913354f3a0deebc0df96097eb9468Timo Sirainen mf->flags = flags;
ea4ca37ec14913354f3a0deebc0df96097eb9468Timo Sirainen mf->size = (uoff_t)-1;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen mf->vsize = (uoff_t)-1;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen ctx->file_last = mf;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen i_assert(*ctx->files_tail == NULL);
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen *ctx->files_tail = mf;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->files_tail = &mf->next;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->files_count++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (keywords != NULL) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(sizeof(keywords->idx[0]) == sizeof(unsigned int));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* @UNSAFE */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mf->keywords_count = keywords->count;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen memcpy(mf + 1, keywords->idx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sizeof(unsigned int) * keywords->count);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->have_keywords = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* insert into index */
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen mail_index_append(ctx->trans, 0, &ctx->seq);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE, flags);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (keywords != NULL) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mail_index_update_keywords(ctx->trans, ctx->seq,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen MODIFY_REPLACE, keywords);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ctx->first_seq == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->first_seq = ctx->seq;
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen i_assert(ctx->files->next == NULL);
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen }
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen if (dest_mail == NULL) {
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen if (ctx->mail == NULL) {
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen struct mailbox_transaction_context *_t =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen &t->ictx.mailbox_ctx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->mail = mail_alloc(_t, 0, NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_mail = ctx->mail;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen mail_set_seq(dest_mail, ctx->seq);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ctx->input == NULL) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* FIXME: copying with hardlinking. we could copy the
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cached data directly */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->cur_dest_mail = NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen input = index_mail_cache_parse_init(dest_mail, ctx->input);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_stream_unref(&ctx->input);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->input = input;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->cur_dest_mail = dest_mail;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return ctx->seq;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic bool
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenmaildir_get_updated_filename(struct maildir_save_context *ctx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_filename *mf,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char **fname_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *basename = mf->basename;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ctx->mbox->storage->save_size_in_filename &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mf->size != (uoff_t)-1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen MAILDIR_EXTRA_FILE_SIZE, mf->size);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /*if (mf->vsize != (uoff_t)-1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen MAILDIR_EXTRA_VIRTUAL_SIZE,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mf->vsize);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }*/
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mf->keywords_count == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if ((mf->flags & MAIL_FLAGS_MASK) == MAIL_RECENT) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *fname_r = basename;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *fname_r = maildir_filename_set_flags(NULL, basename,
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen mf->flags & MAIL_FLAGS_MASK, NULL);
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen return FALSE;
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen }
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen buffer_update_const_data(ctx->keywords_buffer, mf + 1,
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen mf->keywords_count * sizeof(unsigned int));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *fname_r = maildir_filename_set_flags(ctx->keywords_sync_ctx, basename,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mf->flags & MAIL_FLAGS_MASK,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen &ctx->keywords_array);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainenstatic const char *maildir_mf_get_path(struct maildir_save_context *ctx,
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen struct maildir_filename *mf)
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen{
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen const char *fname;
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen if (!ctx->moving && (mf->flags & MAILDIR_SAVE_FLAG_HARDLINK) == 0) {
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen /* file is still in tmp/ */
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen return t_strdup_printf("%s/%s", ctx->tmpdir, mf->basename);
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* already moved to new/ or cur/ */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (maildir_get_updated_filename(ctx, mf, &fname))
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return t_strdup_printf("%s/%s", ctx->newdir, mf->basename);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen else
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return t_strdup_printf("%s/%s", ctx->curdir, fname);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenconst char *maildir_save_file_get_path(struct mailbox_transaction_context *_t,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen uint32_t seq)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_transaction_context *t =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (struct maildir_transaction_context *)_t;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_save_context *ctx = t->save_ctx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_filename *mf;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(seq >= ctx->first_seq);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen seq -= ctx->first_seq;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mf = ctx->files;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen while (seq > 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mf = mf->next;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(mf != NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen seq--;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen return maildir_mf_get_path(ctx, mf);
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen}
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainenstatic int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir,
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen const char **fname_r)
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen{
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct mailbox *box = &mbox->ibox.box;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen struct stat st;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen unsigned int prefix_len;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen const char *tmp_fname = NULL;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen string_t *path;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen int fd;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen path = t_str_new(256);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen str_append(path, dir);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen str_append_c(path, '/');
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen prefix_len = str_len(path);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen for (;;) {
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen tmp_fname = maildir_filename_generate();
7bb939ef70752f2731d27b18c944ea94e5b23eb5Timo Sirainen str_truncate(path, prefix_len);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen str_append(path, tmp_fname);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* stat() first to see if it exists. pretty much the only
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen possibility of that happening is if time had moved
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen backwards, but even then it's highly unlikely. */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (stat(str_c(path), &st) == 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* try another file name */
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen } else if (errno != ENOENT) {
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen mail_storage_set_critical(box->storage,
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen "stat(%s) failed: %m", str_c(path));
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen return -1;
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen } else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* doesn't exist */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mode_t old_mask = umask(0777 & ~box->file_create_mode);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fd = open(str_c(path),
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen umask(old_mask);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (fd != -1 || errno != EEXIST)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen break;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* race condition between stat() and open().
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen highly unlikely. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen *fname_r = tmp_fname;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (fd == -1) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (ENOSPACE(errno)) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mail_storage_set_error(box->storage,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen MAIL_ERROR_NOSPACE, MAIL_ERRSTR_NO_SPACE);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen } else {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mail_storage_set_critical(box->storage,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "open(%s) failed: %m", str_c(path));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen } else if (box->file_create_gid != (gid_t)-1) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mail_storage_set_critical(box->storage,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "fchown(%s) failed: %m", str_c(path));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return fd;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainenint maildir_save_init(struct mailbox_transaction_context *_t,
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen enum mail_flags flags, struct mail_keywords *keywords,
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen time_t received_date, int timezone_offset ATTR_UNUSED,
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen const char *from_envelope ATTR_UNUSED,
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen struct istream *input, struct mail *dest_mail,
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen struct mail_save_context **ctx_r)
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen{
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen struct maildir_transaction_context *t =
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen (struct maildir_transaction_context *)_t;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen struct maildir_save_context *ctx;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->ictx.ibox;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen i_assert((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (t->save_ctx == NULL)
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen t->save_ctx = maildir_save_transaction_init(t);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen ctx = t->save_ctx;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen flags &= ~MAIL_RECENT;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (mbox->ibox.keep_recent)
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen flags |= MAIL_RECENT;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen T_BEGIN {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* create a new file in tmp/ directory */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen const char *fname;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->fd = maildir_create_tmp(mbox, ctx->tmpdir, &fname);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (ctx->fd == -1)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen ctx->failed = TRUE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen else {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ctx->received_date = received_date;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ctx->input = (ctx->mbox->storage->storage.flags &
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ?
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_stream_create_crlf(input) :
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_stream_create_lf(input);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen maildir_save_add(t, fname, flags, keywords, dest_mail);
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen }
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen } T_END;
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen if (ctx->failed)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen ctx->output = o_stream_create_fd_file(ctx->fd, 0, FALSE);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen o_stream_cork(ctx->output);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen *ctx_r = &ctx->ctx;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return ctx->failed ? -1 : 0;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint maildir_save_continue(struct mail_save_context *_ctx)
2c20ffcb5bb1ccdfdcd0b0ff0c7296f65b990362Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (ctx->failed)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen do {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (o_stream_send_istream(ctx->output, ctx->input) < 0) {
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen mail_storage_set_critical(storage,
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen "o_stream_send_istream(%s/%s) "
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen "failed: %m",
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen ctx->tmpdir, ctx->file_last->basename);
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen }
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen ctx->failed = TRUE;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen return -1;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen }
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen if (ctx->cur_dest_mail != NULL)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen index_mail_cache_parse_continue(ctx->cur_dest_mail);
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen /* both tee input readers may consume data from our primary
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen input stream. we'll have to make sure we don't return with
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen one of the streams still having data in them. */
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen } while (i_stream_read(ctx->input) > 0);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int maildir_save_finish_real(struct mail_save_context *_ctx)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct utimbuf buf;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct stat st;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *path;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen int output_errno;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->finished = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ctx->failed && ctx->fd == -1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* tmp file creation failed */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen path = t_strconcat(ctx->tmpdir, "/", ctx->file_last->basename, NULL);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (o_stream_flush(ctx->output) < 0) {
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mail_storage_set_critical(storage,
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen "o_stream_flush(%s) failed: %m", path);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen ctx->failed = TRUE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (ctx->received_date != (time_t)-1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* set the received_date by modifying mtime */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen buf.actime = ioloop_time;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen buf.modtime = ctx->received_date;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (utime(path, &buf) < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->failed = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_critical(storage,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "utime(%s) failed: %m", path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen } else if (ctx->fd != -1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (fstat(ctx->fd, &st) == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->received_date = st.st_mtime;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->failed = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_critical(storage,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "fstat(%s) failed: %m", path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
d5ac54ef50db16b50689b5c8b7bb64d344190832Timo Sirainen } else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* hardlinked */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (stat(path, &st) == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->received_date = st.st_mtime;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->failed = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_critical(storage,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "stat(%s) failed: %m", path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ctx->cur_dest_mail != NULL) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen index_mail_cache_parse_deinit(ctx->cur_dest_mail,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ctx->received_date, !ctx->failed);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_stream_unref(&ctx->input);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* remember the size in case we want to add it to filename */
ctx->file_last->size = ctx->output->offset;
if (ctx->cur_dest_mail == NULL ||
mail_get_virtual_size(ctx->cur_dest_mail,
&ctx->file_last->vsize) < 0)
ctx->file_last->vsize = (uoff_t)-1;
output_errno = ctx->output->stream_errno;
o_stream_destroy(&ctx->output);
if (!ctx->mbox->ibox.fsync_disable && !ctx->failed) {
if (fsync(ctx->fd) < 0) {
mail_storage_set_critical(storage,
"fsync(%s) failed: %m", path);
ctx->failed = TRUE;
}
}
if (close(ctx->fd) < 0) {
mail_storage_set_critical(storage,
"close(%s) failed: %m", path);
ctx->failed = TRUE;
}
ctx->fd = -1;
if (ctx->failed) {
struct maildir_filename **fm;
/* delete the tmp file */
if (unlink(path) < 0 && errno != ENOENT) {
mail_storage_set_critical(storage,
"unlink(%s) failed: %m", path);
}
errno = output_errno;
if (ENOSPACE(errno)) {
mail_storage_set_error(storage,
MAIL_ERROR_NOSPACE, MAIL_ERRSTR_NO_SPACE);
} else if (errno != 0) {
mail_storage_set_critical(storage,
"write(%s) failed: %m", ctx->mbox->path);
}
/* remove from the linked list */
for (fm = &ctx->files; (*fm)->next != NULL; fm = &(*fm)->next) ;
i_assert(*fm == ctx->file_last);
*fm = NULL;
ctx->files_tail = fm;
ctx->file_last = NULL;
ctx->files_count--;
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;
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_transaction_unlink_copied_files(struct maildir_save_context *ctx,
struct maildir_filename *pos)
{
struct maildir_filename *mf;
/* try to unlink the mails already moved */
for (mf = ctx->files; mf != pos; mf = mf->next) {
if ((mf->flags & MAILDIR_SAVE_FLAG_DELETED) == 0) {
T_BEGIN {
(void)unlink(maildir_mf_get_path(ctx, mf));
} T_END;
}
}
ctx->files = pos;
}
static int fdatasync_path(const char *path)
{
int fd, ret = 0;
/* Directories need to be opened as read-only.
fsync() doesn't appear to care about it. */
fd = open(path, O_RDONLY);
if (fd == -1) {
i_error("open(%s) failed: %m", path);
return -1;
}
if (fdatasync(fd) < 0) {
i_error("fdatasync(%s) failed: %m", path);
ret = -1;
}
(void)close(fd);
return ret;
}
static int maildir_transaction_fsync_dirs(struct maildir_save_context *ctx,
bool new_changed, bool cur_changed)
{
if (ctx->mbox->ibox.fsync_disable)
return 0;
if (new_changed) {
if (fdatasync_path(ctx->newdir) < 0)
return -1;
}
if (cur_changed) {
if (fdatasync_path(ctx->curdir) < 0)
return -1;
}
return 0;
}
int maildir_transaction_save_commit_pre(struct maildir_save_context *ctx)
{
struct maildir_transaction_context *t =
(struct maildir_transaction_context *)ctx->ctx.transaction;
struct maildir_filename *mf;
uint32_t seq, uid, first_uid, next_uid;
enum maildir_uidlist_rec_flag flags;
bool newdir, new_changed, cur_changed, sync_commit = FALSE;
int ret;
i_assert(ctx->output == NULL);
i_assert(ctx->finished);
/* if we want to assign UIDs or keywords, we require uidlist lock */
if ((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS) == 0 &&
!ctx->have_keywords) {
/* assign the UIDs if we happen to get a lock */
ctx->locked = maildir_uidlist_try_lock(ctx->mbox->uidlist) > 0;
} else {
if (maildir_uidlist_lock(ctx->mbox->uidlist) <= 0) {
/* error or timeout - our transaction is broken */
maildir_transaction_save_rollback(ctx);
return -1;
}
ctx->locked = TRUE;
}
if (ctx->locked) {
ret = maildir_uidlist_sync_init(ctx->mbox->uidlist,
MAILDIR_UIDLIST_SYNC_PARTIAL,
&ctx->uidlist_sync_ctx);
i_assert(ret > 0); /* already locked, shouldn't fail */
if (maildir_sync_index_begin(ctx->mbox, NULL,
&ctx->sync_ctx) < 0) {
maildir_transaction_save_rollback(ctx);
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) {
maildir_transaction_save_rollback(ctx);
return -1;
}
sync_commit = TRUE;
/* if messages were added to index, assign them UIDs */
first_uid = maildir_uidlist_get_next_uid(ctx->mbox->uidlist);
i_assert(first_uid != 0);
mail_index_append_assign_uids(ctx->trans, first_uid, &next_uid);
i_assert(next_uid = first_uid + ctx->files_count);
/* these mails are all recent in our session */
for (uid = first_uid; uid < next_uid; uid++)
index_mailbox_set_recent_uid(&ctx->mbox->ibox, uid);
if (!ctx->mbox->ibox.keep_recent) {
/* 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),
&next_uid, sizeof(next_uid), FALSE);
}
/* this will work even if index isn't updated */
*t->ictx.first_saved_uid = first_uid;
*t->ictx.last_saved_uid = next_uid - 1;
} else {
/* since we couldn't lock uidlist, we'll have to drop the
appends to index. */
for (seq = ctx->seq; seq >= ctx->first_seq; seq--)
mail_index_expunge(ctx->trans, seq);
mail_cache_transaction_rollback(&t->ictx.cache_trans);
t->ictx.cache_trans =
mail_cache_get_transaction(t->ictx.cache_view,
t->ictx.trans);
}
/* move them into new/ and/or cur/ */
ret = 0;
ctx->moving = TRUE;
new_changed = cur_changed = FALSE;
for (mf = ctx->files; mf != NULL; mf = mf->next) {
T_BEGIN {
const char *dest;
newdir = maildir_get_updated_filename(ctx, mf, &dest);
/* if hardlink-flag is set, the file is already in
destination. if the hardlinked mail contained
keywords, it was linked into tmp/ and it doesn't
have the hardlink-flag set, so it's treated as any
other saved mail. */
if ((mf->flags & MAILDIR_SAVE_FLAG_HARDLINK) != 0)
ret = 0;
else {
if (newdir)
new_changed = TRUE;
else
cur_changed = TRUE;
ret = maildir_file_move(ctx, mf->basename,
dest, newdir);
}
} T_END;
if (ret < 0)
break;
}
if (ret == 0) {
ret = maildir_transaction_fsync_dirs(ctx, new_changed,
cur_changed);
}
if (ret == 0 && ctx->uidlist_sync_ctx != NULL) {
/* everything was moved successfully. update our internal
state. */
for (mf = ctx->files; mf != NULL; mf = mf->next) T_BEGIN {
const char *dest;
newdir = maildir_get_updated_filename(ctx, mf, &dest);
flags = MAILDIR_UIDLIST_REC_FLAG_RECENT;
if (newdir)
flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
dest, flags);
i_assert(ret > 0);
} T_END;
}
if (ctx->uidlist_sync_ctx != NULL) {
/* update dovecot-uidlist file. */
if (maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx) < 0)
ret = -1;
}
*t->ictx.saved_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 (sync_commit) {
/* It doesn't matter if index syncing fails */
ctx->keywords_sync_ctx = NULL;
(void)maildir_sync_index_finish(&ctx->sync_ctx,
ret < 0, !sync_commit);
}
if (ret < 0) {
ctx->keywords_sync_ctx = !ctx->have_keywords ? NULL :
maildir_keywords_sync_init(ctx->mbox->keywords,
ctx->mbox->ibox.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. */
maildir_transaction_unlink_copied_files(ctx, mf);
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 ret;
}
void maildir_transaction_save_commit_post(struct maildir_save_context *ctx)
{
ctx->ctx.transaction = NULL; /* transaction is already freed */
if (ctx->locked)
maildir_uidlist_unlock(ctx->mbox->uidlist);
pool_unref(&ctx->pool);
}
static void
maildir_transaction_save_rollback_real(struct maildir_save_context *ctx)
{
struct maildir_filename *mf;
string_t *str;
size_t dir_len;
bool hardlinks = FALSE;
i_assert(ctx->output == NULL);
if (!ctx->finished)
maildir_save_cancel(&ctx->ctx);
str = t_str_new(1024);
str_append(str, ctx->tmpdir);
str_append_c(str, '/');
dir_len = str_len(str);
/* clean up the temp files */
for (mf = ctx->files; mf != NULL; mf = mf->next) {
if ((mf->flags & MAILDIR_SAVE_FLAG_HARDLINK) == 0) {
mf->flags |= MAILDIR_SAVE_FLAG_DELETED;
str_truncate(str, dir_len);
str_append(str, mf->basename);
(void)unlink(str_c(str));
} else {
hardlinks = TRUE;
}
}
if (hardlinks)
maildir_transaction_unlink_copied_files(ctx, NULL);
if (ctx->uidlist_sync_ctx != NULL)
(void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
if (ctx->locked)
maildir_uidlist_unlock(ctx->mbox->uidlist);
if (ctx->sync_ctx != NULL)
(void)maildir_sync_index_finish(&ctx->sync_ctx, TRUE, FALSE);
if (ctx->mail != NULL)
mail_free(&ctx->mail);
pool_unref(&ctx->pool);
}
void maildir_transaction_save_rollback(struct maildir_save_context *ctx)
{
T_BEGIN {
maildir_transaction_save_rollback_real(ctx);
} T_END;
}