bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2007-2018 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;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek#define MDBOX_SAVECTX(s) container_of(DBOX_SAVECTX(s), struct mdbox_save_context, ctx)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstatic struct dbox_file *
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenmdbox_copy_file_get_file(struct mailbox_transaction_context *t,
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_save_context *ctx = MDBOX_SAVECTX(t->save_ctx);
03010dbaa74ec70f062994dfe3cd39bedc99a28bTimo Sirainen mail_index_lookup_ext(t->view, seq, ctx->mbox->ext_id, &data, NULL);
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,
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_save_context *ctx = MDBOX_SAVECTX(t->save_ctx);
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)
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_mailbox *mbox = MDBOX_MAILBOX(t->box);
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_save_context *ctx = MDBOX_SAVECTX(t->save_ctx);
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)
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_save_context *ctx = MDBOX_SAVECTX(_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);
f776b9a125c59a96de6807e9558942cf7b7ab079Timo Sirainen index_storage_save_abort_last(&ctx->ctx.ctx, 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)
804fa3f03bd9170272168a5ad214053bbe3160c7Josef 'Jeff' Sipek struct dbox_save_context *ctx = DBOX_SAVECTX(_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 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)
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_save_context *ctx = MDBOX_SAVECTX(_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 */
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainen if (mdbox_map_atomic_lock(ctx->atomic, "saving") < 0) {
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 |
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen /* assign map UIDs for newly saved messages after we've successfully
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen acquired all the locks. the transaction is now very unlikely to
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen fail. the UIDs are written to the transaction log immediately within
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen this function, but the map is left locked. */
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen if (mdbox_map_append_assign_map_uids(ctx->append_ctx, &first_map_uid,
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen /* update dbox header flags */
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen dbox_save_update_header_flags(&ctx->ctx, ctx->sync_ctx->sync_view,
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen ctx->mbox->hdr_ext_id, offsetof(struct mdbox_index_header, flags));
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,
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen ctx->ctx.highest_pop3_uidl_seq - mails[0].seq;
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen i_assert(mails[highest_pop3_uidl_idx].seq == ctx->ctx.highest_pop3_uidl_seq);
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen seq_range_array_iter_init(&iter, &_t->changes->saved_uids);
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen if (!seq_range_array_iter_nth(&iter, highest_pop3_uidl_idx, &uid))
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen index_pop3_uidl_set_max_uid(&ctx->mbox->box, ctx->ctx.trans, 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,
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainen mail_index_sync_set_reason(ctx->sync_ctx->index_sync_ctx, "copying");
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainen mail_index_sync_set_reason(ctx->sync_ctx->index_sync_ctx, "saving");
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)
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_save_context *ctx = MDBOX_SAVECTX(_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 */
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainen if (mdbox_map_transaction_commit(ctx->map_trans, "copy refcount updates") < 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)
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_save_context *ctx = MDBOX_SAVECTX(_ctx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)mdbox_sync_finish(&ctx->sync_ctx, FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mdbox_copy(struct mail_save_context *_ctx, struct mail *mail)
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mdbox_save_context *ctx = MDBOX_SAVECTX(_ctx);
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen if (mail->box->storage != _ctx->transaction->box->storage ||
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen _ctx->transaction->box->disable_reflink_copy_to)
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen if (mdbox_mail_lookup(src_mbox, mail->transaction->view, mail->seq,
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen mail_index_lookup_ext(mail->transaction->view, mail->seq,
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen if (guid_data == NULL || guid_128_is_empty(guid_data)) {
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen /* missing GUID, something's broken. don't copy using
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen refcounting. */
61279c3c77aa4a6f8d1de82b468ab01b14418318Timo Sirainen (guid_128_from_string(_ctx->data.guid, wanted_guid) < 0 ||
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen memcmp(guid_data, wanted_guid, sizeof(wanted_guid)) != 0)) {
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen /* GUID change requested. we can't do it with refcount
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,
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
1554bed8d2b4e4286c10f7d6bcf716b246bd5bafTimo Sirainen index_copy_cache_fields(_ctx, mail, ctx->ctx.seq);