mail-index-transaction.c revision 3b426f49d36187895debdda67fff09f97941881c
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "lib.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "ioloop.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "array.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "bsearch-insert-pos.h"
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen#include "mail-index-private.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "mail-transaction-log-private.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "mail-index-transaction-private.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include <stdlib.h>
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenvoid (*hook_mail_index_transaction_created)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen (struct mail_index_transaction *t) = NULL;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstruct mail_index_view *
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenmail_index_transaction_get_view(struct mail_index_transaction *t)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return t->view;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenbool mail_index_transaction_is_expunged(struct mail_index_transaction *t,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen uint32_t seq)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen{
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen struct mail_transaction_expunge_guid key;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (!array_is_created(&t->expunges))
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return FALSE;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (t->expunges_nonsorted)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen mail_index_transaction_sort_expunges(t);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen key.uid = seq;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return array_bsearch(&t->expunges, &key,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen mail_transaction_expunge_guid_cmp) != NULL;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenvoid mail_index_transaction_ref(struct mail_index_transaction *t)
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen{
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainen t->refcount++;
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainenvoid mail_index_transaction_unref(struct mail_index_transaction **_t)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen struct mail_index_transaction *t = *_t;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen *_t = NULL;
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen if (--t->refcount > 0)
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen return;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen mail_index_transaction_reset_v(t);
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen array_free(&t->module_contexts);
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen mail_index_view_transaction_unref(t->view);
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen mail_index_view_close(&t->view);
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen i_free(t);
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen}
e8eb96edcfe8cff7839f1258ab6e871e41a4785eTimo Sirainen
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainenuint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t)
42aaebd4b237403aff6bbfafdcdf52cf5f8c1c06Timo Sirainen{
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen const struct mail_index_header *head_hdr, *hdr;
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen unsigned int offset;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen uint32_t next_uid;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen head_hdr = &t->view->index->map->hdr;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen hdr = &t->view->map->hdr;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen next_uid = t->reset || head_hdr->uid_validity != hdr->uid_validity ?
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen 1 : hdr->next_uid;
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen if (array_is_created(&t->appends) && t->highest_append_uid != 0) {
e50c7afe297ab10e07a8acc816c76ce9d45ef409Timo Sirainen /* get next_uid from appends if they have UIDs. it's possible
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen that some appends have too low UIDs, they'll be caught
e50c7afe297ab10e07a8acc816c76ce9d45ef409Timo Sirainen later. */
e50c7afe297ab10e07a8acc816c76ce9d45ef409Timo Sirainen if (next_uid <= t->highest_append_uid)
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen next_uid = t->highest_append_uid + 1;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen
9b1d6da0f2b42b8b6f612a570a83355c2a5088eeTimo Sirainen /* see if it's been updated in pre/post header changes */
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen offset = offsetof(struct mail_index_header, next_uid);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (t->post_hdr_mask[offset] != 0) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen hdr = (const void *)t->post_hdr_change;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (hdr->next_uid > next_uid)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen next_uid = hdr->next_uid;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (t->pre_hdr_mask[offset] != 0) {
34ce7c45264902e217bfb5fa7f7a0aace9302074Timo Sirainen hdr = (const void *)t->pre_hdr_change;
34ce7c45264902e217bfb5fa7f7a0aace9302074Timo Sirainen if (hdr->next_uid > next_uid)
34ce7c45264902e217bfb5fa7f7a0aace9302074Timo Sirainen next_uid = hdr->next_uid;
34ce7c45264902e217bfb5fa7f7a0aace9302074Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return next_uid;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic int
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenmail_transaction_log_file_refresh(struct mail_index_transaction *t,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen struct mail_transaction_log_append_ctx *ctx)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen struct mail_transaction_log_file *file;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (t->reset) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* Reset the whole index, preserving only indexid. Begin by
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen rotating the log. We don't care if we skip some non-synced
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen transactions. */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (mail_transaction_log_rotate(t->view->index->log, TRUE) < 0)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return -1;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (!MAIL_INDEX_TRANSACTION_HAS_CHANGES(t)) {
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* we only wanted to reset */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return 0;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen file = t->view->index->log->head;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (!t->view->index->log_locked) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* update sync_offset */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (mail_transaction_log_file_map(file, file->sync_offset,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen (uoff_t)-1) <= 0)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return -1;
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen i_assert(file->sync_offset >= file->buffer_offset);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen ctx->new_highest_modseq = file->sync_highest_modseq;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return 1;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic int
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenmail_index_transaction_commit_real(struct mail_index_transaction *t,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen uoff_t *commit_size_r)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen struct mail_transaction_log *log = t->view->index->log;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen bool external = (t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen struct mail_transaction_log_append_ctx *ctx;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen uint32_t log_seq1, log_seq2;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen uoff_t log_offset1, log_offset2;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen int ret;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (mail_transaction_log_append_begin(log->index, external, &ctx) < 0)
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen return -1;
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen ret = mail_transaction_log_file_refresh(t, ctx);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (ret > 0) {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen ret = mail_index_transaction_finish(t);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (ret == 0)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen mail_index_transaction_export(t, ctx);
}
mail_transaction_log_get_head(log, &log_seq1, &log_offset1);
if (mail_transaction_log_append_commit(&ctx) < 0 || ret < 0)
return -1;
mail_transaction_log_get_head(log, &log_seq2, &log_offset2);
i_assert(log_seq1 == log_seq2);
if (t->reset) {
/* get rid of the old index. it might just confuse readers,
especially if it's broken. */
if (unlink(log->index->filepath) < 0 && errno != ENOENT)
i_error("unlink(%s) failed: %m", log->index->filepath);
}
*commit_size_r = log_offset2 - log_offset1;
if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_HIDE) != 0 &&
log_offset1 != log_offset2) {
/* mark the area covered by this transaction hidden */
mail_index_view_add_hidden_transaction(t->view, log_seq1,
log_offset1, log_offset2 - log_offset1);
}
return 0;
}
static int mail_index_transaction_commit_v(struct mail_index_transaction *t,
struct mail_index_transaction_commit_result *result_r)
{
struct mail_index *index = t->view->index;
bool changed;
int ret;
i_assert(t->first_new_seq >
mail_index_view_get_messages_count(t->view));
changed = MAIL_INDEX_TRANSACTION_HAS_CHANGES(t) || t->reset;
ret = !changed ? 0 :
mail_index_transaction_commit_real(t, &result_r->commit_size);
mail_transaction_log_get_head(index->log, &result_r->log_file_seq,
&result_r->log_file_offset);
if (ret == 0 && !index->syncing && changed) {
/* if we're committing a normal transaction, we want to
have those changes in the index mapping immediately. this
is especially important when committing cache offset
updates.
however if we're syncing the index now, the mapping must
be done later as MAIL_INDEX_SYNC_HANDLER_FILE so that
expunge handlers get run for the newly expunged messages
(and sync handlers that require HANDLER_FILE as well). */
index->sync_commit_result = result_r;
(void)mail_index_refresh(index);
index->sync_commit_result = NULL;
}
mail_index_transaction_unref(&t);
return ret;
}
static void mail_index_transaction_rollback_v(struct mail_index_transaction *t)
{
mail_index_transaction_unref(&t);
}
int mail_index_transaction_commit(struct mail_index_transaction **t)
{
struct mail_index_transaction_commit_result result;
return mail_index_transaction_commit_full(t, &result);
}
int mail_index_transaction_commit_full(struct mail_index_transaction **_t,
struct mail_index_transaction_commit_result *result_r)
{
struct mail_index_transaction *t = *_t;
struct mail_index *index = t->view->index;
bool index_undeleted = t->index_undeleted;
if (mail_index_view_is_inconsistent(t->view)) {
mail_index_transaction_rollback(_t);
return -1;
}
if (!index_undeleted) {
if (t->view->index->index_deleted ||
(t->view->index->index_delete_requested &&
!t->view->index->syncing)) {
/* no further changes allowed */
mail_index_transaction_rollback(_t);
return -1;
}
}
*_t = NULL;
memset(result_r, 0, sizeof(*result_r));
if (t->v.commit(t, result_r) < 0)
return -1;
if (index_undeleted) {
index->index_deleted = FALSE;
index->index_delete_requested = FALSE;
}
return 0;
}
void mail_index_transaction_rollback(struct mail_index_transaction **_t)
{
struct mail_index_transaction *t = *_t;
*_t = NULL;
t->v.rollback(t);
}
static struct mail_index_transaction_vfuncs trans_vfuncs = {
mail_index_transaction_reset_v,
mail_index_transaction_commit_v,
mail_index_transaction_rollback_v
};
struct mail_index_transaction *
mail_index_transaction_begin(struct mail_index_view *view,
enum mail_index_transaction_flags flags)
{
struct mail_index_transaction *t;
/* don't allow syncing view while there's ongoing transactions */
mail_index_view_transaction_ref(view);
mail_index_view_ref(view);
t = i_new(struct mail_index_transaction, 1);
t->refcount = 1;
t->v = trans_vfuncs;
t->view = view;
t->flags = flags;
if (view->syncing) {
/* transaction view cannot work if new records are being added
in two places. make sure it doesn't happen. */
t->no_appends = TRUE;
t->first_new_seq = (uint32_t)-1;
} else {
t->first_new_seq =
mail_index_view_get_messages_count(t->view) + 1;
}
i_array_init(&t->module_contexts,
I_MIN(5, mail_index_module_register.id));
if (hook_mail_index_transaction_created != NULL)
hook_mail_index_transaction_created(t);
return t;
}