mdbox-save.c revision b67974c4b89ab6950c2694cce8dfb1b6561cc084
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */
e1f05b193ac1edd3267294e9501e8063aa0f791aTimo Sirainen struct dbox_file_append_context *cur_file_append;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mdbox_map_transaction_context *map_trans;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define MDBOX_SAVECTX(s) container_of(DBOX_SAVECTX(s), struct mdbox_save_context, ctx)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct dbox_file *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmdbox_copy_file_get_file(struct mailbox_transaction_context *t,
ee26329cb5cc679b5645e4933d529f86accb976aTimo Sirainen struct mdbox_save_context *ctx = MDBOX_SAVECTX(t->save_ctx);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_index_lookup_ext(t->view, seq, ctx->mbox->ext_id, &data, NULL);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mdbox_map_lookup(ctx->mbox->storage->map, rec->map_uid,
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen return mdbox_file_init(ctx->mbox->storage, file_id);
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainenmdbox_save_file_get_file(struct mailbox_transaction_context *t,
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen struct mdbox_save_context *ctx = MDBOX_SAVECTX(t->save_ctx);
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen unsigned int count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* copied mail */
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen return mdbox_copy_file_get_file(t, seq, offset_r);
50c4a9739b55370b1d3950d7b3ec2f7cd2ed5f49Timo Sirainen /* saved mail */
e9f2d9104d395bcf54be3f8ba8d9f63aecf0bcbeTimo Sirainen if (dbox_file_append_flush(mail->file_append) < 0)
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainenmdbox_save_alloc(struct mailbox_transaction_context *t)
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen struct mdbox_mailbox *mbox = MDBOX_MAILBOX(t->box);
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen struct mdbox_save_context *ctx = MDBOX_SAVECTX(t->save_ctx);
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen /* use the existing allocated structure */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen ctx->atomic = mdbox_map_atomic_begin(mbox->storage->map);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen ctx->append_ctx = mdbox_map_append_begin(ctx->atomic);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mdbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mdbox_save_context *ctx = MDBOX_SAVECTX(_ctx);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* get the size of the mail to be saved, if possible */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (i_stream_get_size(input, TRUE, &mail_size) <= 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we couldn't find out the exact size. fallback to non-exact,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maybe it'll give something useful. the mail size is used
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen only to figure out if it's causing mdbox file to grow
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen too large. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (i_stream_get_size(input, FALSE, &mail_size) <= 0)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mdbox_map_append_next(ctx->append_ctx, mail_size, 0,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen i_assert(ctx->ctx.dbox_output->offset <= (uint32_t)-1);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen save_mail->file_append = ctx->cur_file_append;
faef57a36800fe952a5794328467f846d5480ae6Timo Sirainenstatic int mdbox_save_mail_write_metadata(struct mdbox_save_context *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct dbox_file *file = mail->file_append->file;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen i_assert(file->msg_header_size == sizeof(dbox_msg_hdr));
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen mail->append_offset - mail->file_append->file->msg_header_size;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen dbox_save_write_metadata(&ctx->ctx.ctx, ctx->ctx.dbox_output,
faef57a36800fe952a5794328467f846d5480ae6Timo Sirainen /* save the 128bit GUID to index so if the map index gets corrupted
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen we can still find the message */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dbox_msg_header_fill(&dbox_msg_hdr, message_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (o_stream_pwrite(ctx->ctx.dbox_output, &dbox_msg_hdr,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen sizeof(dbox_msg_hdr), mail->append_offset) < 0) {
b30499429feee4f16f1e09c7047101dc7ff38304Timo Sirainen dbox_file_set_syscall_error(file, "pwrite()");
b30499429feee4f16f1e09c7047101dc7ff38304Timo Sirainenstatic int mdbox_save_finish_write(struct mail_save_context *_ctx)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen mail = array_idx_modifiable(&ctx->mails, array_count(&ctx->mails) - 1);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mdbox_save_mail_write_metadata(ctx, mail) < 0)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* if we try to read the saved mail before unlocking file,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen make sure the input stream doesn't have stale data */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen i_stream_sync(mail->file_append->file->input);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen index_storage_save_abort_last(&ctx->ctx.ctx, ctx->ctx.seq);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen array_delete(&ctx->mails, array_count(&ctx->mails) - 1, 1);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainenint mdbox_save_finish(struct mail_save_context *ctx)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mdbox_save_cancel(struct mail_save_context *_ctx)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct dbox_save_context *ctx = DBOX_SAVECTX(_ctx);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenmdbox_save_set_map_uids(struct mdbox_save_context *ctx,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen uint32_t first_map_uid, uint32_t last_map_uid)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mail_index_view *view = ctx->ctx.ctx.transaction->view;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen const struct mdbox_mail_index_record *old_rec;
bb1e9bd834c04f272b6e3f4d4a7686c26d6e5c2aTimo Sirainen unsigned int i, count;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mdbox_update_header(mbox, ctx->ctx.trans, NULL);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen for (i = 0; i < count; i++) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mail_index_lookup_ext(view, mails[i].seq, mbox->ext_id,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (old_rec != NULL && old_rec->map_uid != 0) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* message was copied. keep the existing map uid */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mail_index_update_ext(ctx->ctx.trans, mails[i].seq,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenint mdbox_transaction_save_commit_pre(struct mail_save_context *_ctx)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mdbox_save_context *ctx = MDBOX_SAVECTX(_ctx);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mailbox_transaction_context *_t = _ctx->transaction;
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen /* flush/fsync writes to m.* files before locking the map */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mdbox_map_append_flush(ctx->append_ctx) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* make sure the map gets locked */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mdbox_map_atomic_lock(ctx->atomic, "saving") < 0) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* lock the mailbox after map to avoid deadlocks. if we've noticed
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen any corruption, deal with it later, otherwise we won't have
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen up-to-date atomic->sync_view */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mdbox_sync_begin(ctx->mbox, MDBOX_SYNC_FLAG_NO_PURGE |
f79a807865222dc8d5afd21667d2ace67f6c831bTimo Sirainen /* assign map UIDs for newly saved messages after we've successfully
0e7a1bd9b7b39e57a22dbd4ba12df9b9603e6391Timo Sirainen acquired all the locks. the transaction is now very unlikely to
f79a807865222dc8d5afd21667d2ace67f6c831bTimo Sirainen fail. the UIDs are written to the transaction log immediately within
f79a807865222dc8d5afd21667d2ace67f6c831bTimo Sirainen this function, but the map is left locked. */
b30499429feee4f16f1e09c7047101dc7ff38304Timo Sirainen if (mdbox_map_append_assign_map_uids(ctx->append_ctx, &first_map_uid,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* update dbox header flags */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dbox_save_update_header_flags(&ctx->ctx, ctx->sync_ctx->sync_view,
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen ctx->mbox->hdr_ext_id, offsetof(struct mdbox_index_header, flags));
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* assign UIDs for new messages */
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen hdr = mail_index_get_header(ctx->sync_ctx->sync_view);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen mail_index_append_finish_uids(ctx->ctx.trans, hdr->next_uid,
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen ctx->ctx.highest_pop3_uidl_seq - mails[0].seq;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen i_assert(mails[highest_pop3_uidl_idx].seq == ctx->ctx.highest_pop3_uidl_seq);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen seq_range_array_iter_init(&iter, &_t->changes->saved_uids);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen if (!seq_range_array_iter_nth(&iter, highest_pop3_uidl_idx, &uid))
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen index_pop3_uidl_set_max_uid(&ctx->mbox->box, ctx->ctx.trans, uid);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* save map UIDs to mailbox index */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mdbox_save_set_map_uids(ctx, first_map_uid, last_map_uid);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen /* increase map's refcount for copied mails */
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen ctx->map_trans = mdbox_map_transaction_begin(ctx->atomic, FALSE);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mdbox_map_update_refcounts(ctx->map_trans,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mail_index_sync_set_reason(ctx->sync_ctx->index_sync_ctx, "copying");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_sync_set_reason(ctx->sync_ctx->index_sync_ctx, "saving");
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen _t->changes->uid_validity = hdr->uid_validity;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenvoid mdbox_transaction_save_commit_post(struct mail_save_context *_ctx,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mail_index_transaction_commit_result *result)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mdbox_save_context *ctx = MDBOX_SAVECTX(_ctx);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mail_storage *storage = _ctx->transaction->box->storage;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen _ctx->transaction = NULL; /* transaction is already freed */
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen mail_index_sync_set_commit_result(ctx->sync_ctx->index_sync_ctx,
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen /* finish writing the mailbox APPENDs */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (mdbox_sync_finish(&ctx->sync_ctx, TRUE) == 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* commit refcount increases for copied mails */
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen if (mdbox_map_transaction_commit(ctx->map_trans, "copy refcount updates") < 0)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* flush file append writes */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (mdbox_map_append_commit(ctx->append_ctx) < 0)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* update the sync tail offset, everything else
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen was already written at this point. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen const char *box_path = mailbox_get_path(&ctx->mbox->box);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenvoid mdbox_transaction_save_rollback(struct mail_save_context *_ctx)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen struct mdbox_save_context *ctx = MDBOX_SAVECTX(_ctx);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen (void)mdbox_sync_finish(&ctx->sync_ctx, FALSE);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenint mdbox_copy(struct mail_save_context *_ctx, struct mail *mail)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mdbox_save_context *ctx = MDBOX_SAVECTX(_ctx);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen if (mail->box->storage != _ctx->transaction->box->storage ||
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen _ctx->transaction->box->disable_reflink_copy_to)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mdbox_mail_lookup(src_mbox, mail->transaction->view, mail->seq,
7496b0969019303b4917eaccd1f4f771584d8a48Timo Sirainen mail_index_lookup_ext(mail->transaction->view, mail->seq,
7496b0969019303b4917eaccd1f4f771584d8a48Timo Sirainen if (guid_data == NULL || guid_128_is_empty(guid_data)) {
7496b0969019303b4917eaccd1f4f771584d8a48Timo Sirainen /* missing GUID, something's broken. don't copy using
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen refcounting. */
e9f2d9104d395bcf54be3f8ba8d9f63aecf0bcbeTimo Sirainen (guid_128_from_string(_ctx->data.guid, wanted_guid) < 0 ||
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen memcmp(guid_data, wanted_guid, sizeof(wanted_guid)) != 0)) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* GUID change requested. we can't do it with refcount
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen /* remember the map_uid so we can later increase its refcount */
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen array_append(&ctx->copy_map_uids, &rec.map_uid, 1);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* add message to mailbox index */
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen index_copy_cache_fields(_ctx, mail, ctx->ctx.seq);