mdbox-save.c revision 0001f76bf725c5cf403bade8556f142dd43144ee
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2007-2012 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct dbox_file_append_context *cur_file_append;
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen struct mdbox_map_transaction_context *map_trans;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstatic struct dbox_file *
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenmdbox_copy_file_get_file(struct mailbox_transaction_context *t,
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen mail_index_lookup_ext(t->view, seq, ctx->mbox->ext_id,
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (mdbox_map_lookup(ctx->mbox->storage->map, rec->map_uid,
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen return mdbox_file_init(ctx->mbox->storage, file_id);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmdbox_save_file_get_file(struct mailbox_transaction_context *t,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int count;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen /* copied mail */
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen return mdbox_copy_file_get_file(t, seq, offset_r);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen /* saved mail */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (dbox_file_append_flush(mail->file_append) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmdbox_save_alloc(struct mailbox_transaction_context *t)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)t->box;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* use the existing allocated structure */
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen ctx->atomic = mdbox_map_atomic_begin(mbox->storage->map);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen ctx->append_ctx = mdbox_map_append_begin(ctx->atomic);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mdbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* get the size of the mail to be saved, if possible */
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen if (i_stream_get_size(input, TRUE, &mail_size) <= 0) {
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen /* we couldn't find out the exact size. fallback to non-exact,
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen maybe it'll give something useful. the mail size is used
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen only to figure out if it's causing mdbox file to grow
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen too large. */
46908da82cd45dd90ca7e438c8453bc9669868ecTimo Sirainen if (i_stream_get_size(input, FALSE, &mail_size) <= 0)
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (mdbox_map_append_next(ctx->append_ctx, mail_size, 0,
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen i_assert(ctx->ctx.dbox_output->offset <= (uint32_t)-1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen save_mail->file_append = ctx->cur_file_append;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int mdbox_save_mail_write_metadata(struct mdbox_save_context *ctx,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct dbox_file *file = mail->file_append->file;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(file->msg_header_size == sizeof(dbox_msg_hdr));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail->append_offset - mail->file_append->file->msg_header_size;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen dbox_save_write_metadata(&ctx->ctx.ctx, ctx->ctx.dbox_output,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* save the 128bit GUID to index so if the map index gets corrupted
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen we can still find the message */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen dbox_msg_header_fill(&dbox_msg_hdr, message_size);
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen if (o_stream_pwrite(ctx->ctx.dbox_output, &dbox_msg_hdr,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sizeof(dbox_msg_hdr), mail->append_offset) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen dbox_file_set_syscall_error(file, "pwrite()");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int mdbox_save_finish_write(struct mail_save_context *_ctx)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen mail = array_idx_modifiable(&ctx->mails, array_count(&ctx->mails) - 1);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen if (mdbox_save_mail_write_metadata(ctx, mail) < 0)
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen /* if we try to read the saved mail before unlocking file,
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen make sure the input stream doesn't have stale data */
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen i_stream_sync(mail->file_append->file->input);
9047d770bfbb93ab6af5363dedb2d01363877243Timo Sirainen mail_index_expunge(ctx->ctx.trans, ctx->ctx.seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_delete(&ctx->mails, array_count(&ctx->mails) - 1, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mdbox_save_finish(struct mail_save_context *ctx)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid mdbox_save_cancel(struct mail_save_context *_ctx)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainenmdbox_save_set_map_uids(struct mdbox_save_context *ctx,
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen uint32_t first_map_uid, uint32_t last_map_uid)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen struct mail_index_view *view = ctx->ctx.ctx.transaction->view;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen const struct mdbox_mail_index_record *old_rec;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen unsigned int i, count;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen mdbox_update_header(mbox, ctx->ctx.trans, NULL);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen for (i = 0; i < count; i++) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen mail_index_lookup_ext(view, mails[i].seq, mbox->ext_id,
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (old_rec != NULL && old_rec->map_uid != 0) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* message was copied. keep the existing map uid */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen mail_index_update_ext(ctx->ctx.trans, mails[i].seq,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mdbox_transaction_save_commit_pre(struct mail_save_context *_ctx)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mailbox_transaction_context *_t = _ctx->transaction;
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen /* flush/fsync writes to m.* files before locking the map */
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen if (mdbox_map_append_flush(ctx->append_ctx) < 0) {
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen /* make sure the map gets locked */
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen /* assign map UIDs for newly saved messages. they're written to
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen transaction log immediately within this function, but the map
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen is left locked. */
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (mdbox_map_append_assign_map_uids(ctx->append_ctx, &first_map_uid,
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen /* lock the mailbox after map to avoid deadlocks. if we've noticed
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen any corruption, deal with it later, otherwise we won't have
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen up-to-date atomic->sync_view */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (mdbox_sync_begin(ctx->mbox, MDBOX_SYNC_FLAG_NO_PURGE |
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* assign UIDs for new messages */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen hdr = mail_index_get_header(ctx->sync_ctx->sync_view);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_append_finish_uids(ctx->ctx.trans, hdr->next_uid,
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen /* save map UIDs to mailbox index */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen mdbox_save_set_map_uids(ctx, first_map_uid, last_map_uid);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* increase map's refcount for copied mails */
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen ctx->map_trans = mdbox_map_transaction_begin(ctx->atomic, FALSE);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (mdbox_map_update_refcounts(ctx->map_trans,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen _t->changes->uid_validity = hdr->uid_validity;
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainenvoid mdbox_transaction_save_commit_post(struct mail_save_context *_ctx,
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen struct mail_index_transaction_commit_result *result)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen struct mail_storage *storage = _ctx->transaction->box->storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen _ctx->transaction = NULL; /* transaction is already freed */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen mail_index_sync_set_commit_result(ctx->sync_ctx->index_sync_ctx,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* finish writing the mailbox APPENDs */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mdbox_sync_finish(&ctx->sync_ctx, TRUE) == 0) {
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen /* commit refcount increases for copied mails */
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen if (mdbox_map_transaction_commit(ctx->map_trans) < 0)
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen /* flush file append writes */
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen if (mdbox_map_append_commit(ctx->append_ctx) < 0)
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen /* update the sync tail offset, everything else
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen was already written at this point. */
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen const char *box_path = mailbox_get_path(&ctx->mbox->box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid mdbox_transaction_save_rollback(struct mail_save_context *_ctx)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)mdbox_sync_finish(&ctx->sync_ctx, FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mdbox_copy(struct mail_save_context *_ctx, struct mail *mail)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen if (mail->box->storage != _ctx->transaction->box->storage ||
90e39174d397567c101dbf694761371af3682928Timo Sirainen _ctx->transaction->box->disable_reflink_copy_to ||
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen if (mdbox_mail_lookup(src_mbox, mail->transaction->view, mail->seq,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* remember the map_uid so we can later increase its refcount */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_append(&ctx->copy_map_uids, &rec.map_uid, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* add message to mailbox index */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen mail_index_lookup_ext(mail->transaction->view, mail->seq,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
1554bed8d2b4e4286c10f7d6bcf716b246bd5bafTimo Sirainen index_copy_cache_fields(_ctx, mail, ctx->ctx.seq);