summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/index-transaction.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-storage/index/index-transaction.c')
-rw-r--r--src/lib-storage/index/index-transaction.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/lib-storage/index/index-transaction.c b/src/lib-storage/index/index-transaction.c
new file mode 100644
index 0000000..e3138b6
--- /dev/null
+++ b/src/lib-storage/index/index-transaction.c
@@ -0,0 +1,230 @@
+/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "dict.h"
+#include "index-storage.h"
+#include "index-sync-private.h"
+#include "index-pop3-uidl.h"
+#include "index-mail.h"
+
+static void index_transaction_free(struct mailbox_transaction_context *t)
+{
+ if (t->view_pvt != NULL)
+ mail_index_view_close(&t->view_pvt);
+ mail_cache_view_close(&t->cache_view);
+ mail_index_view_close(&t->view);
+ if (array_is_created(&t->pvt_saves))
+ array_free(&t->pvt_saves);
+ array_free(&t->module_contexts);
+ i_free(t->reason);
+ i_free(t);
+}
+
+static int
+index_transaction_index_commit(struct mail_index_transaction *index_trans,
+ struct mail_index_transaction_commit_result *result_r)
+{
+ struct mailbox_transaction_context *t =
+ MAIL_STORAGE_CONTEXT_REQUIRE(index_trans);
+ struct index_mailbox_sync_pvt_context *pvt_sync_ctx = NULL;
+ const char *error;
+ int ret = 0;
+
+ index_pop3_uidl_update_exists_finish(t);
+
+ if (t->attr_pvt_trans != NULL) {
+ if (dict_transaction_commit(&t->attr_pvt_trans, &error) < 0) {
+ mailbox_set_critical(t->box,
+ "Dict private transaction commit failed: %s", error);
+ ret = -1;
+ }
+ }
+ if (t->attr_shared_trans != NULL) {
+ if (dict_transaction_commit(&t->attr_shared_trans, &error) < 0) {
+ mailbox_set_critical(t->box,
+ "Dict shared transaction commit failed: %s", error);
+ ret = -1;
+ }
+ }
+
+ if (t->save_ctx != NULL) {
+ mailbox_save_context_deinit(t->save_ctx);
+ if (ret < 0) {
+ t->box->v.transaction_save_rollback(t->save_ctx);
+ t->save_ctx = NULL;
+ } else if (t->box->v.transaction_save_commit_pre(t->save_ctx) < 0) {
+ t->save_ctx = NULL;
+ ret = -1;
+ }
+ }
+
+ if (array_is_created(&t->pvt_saves)) {
+ if (index_mailbox_sync_pvt_init(t->box, TRUE, 0, &pvt_sync_ctx) < 0)
+ ret = -1;
+ }
+
+ i_assert(t->mail_ref_count == 0);
+ if (ret < 0)
+ t->super.rollback(index_trans);
+ else {
+ if (t->super.commit(index_trans, result_r) < 0) {
+ mailbox_set_index_error(t->box);
+ ret = -1;
+ } else {
+ t->changes->changes_mask = result_r->changes_mask;
+ }
+ }
+
+ if (t->save_ctx == NULL) {
+ } else if (ret >= 0) {
+ i_assert(t->save_ctx->dest_mail == NULL);
+ t->box->v.transaction_save_commit_post(t->save_ctx, result_r);
+ } else {
+ t->box->v.transaction_save_rollback(t->save_ctx);
+ }
+
+ if (pvt_sync_ctx != NULL) {
+ if (index_mailbox_sync_pvt_newmails(pvt_sync_ctx, t) < 0) {
+ /* failed to add private flags. a bit too late to
+ return failure though, so just ignore silently */
+ }
+ index_mailbox_sync_pvt_deinit(&pvt_sync_ctx);
+ }
+
+ if (ret < 0)
+ mail_index_set_error_nolog(t->box->index, mailbox_get_last_error(t->box, NULL));
+ index_transaction_free(t);
+ return ret;
+}
+
+static void
+index_transaction_index_rollback(struct mail_index_transaction *index_trans)
+{
+ struct mailbox_transaction_context *t =
+ MAIL_STORAGE_CONTEXT_REQUIRE(index_trans);
+
+ dict_transaction_rollback(&t->attr_pvt_trans);
+ dict_transaction_rollback(&t->attr_shared_trans);
+
+ if (t->save_ctx != NULL) {
+ mailbox_save_context_deinit(t->save_ctx);
+ t->box->v.transaction_save_rollback(t->save_ctx);
+ }
+
+ i_assert(t->mail_ref_count == 0);
+ t->super.rollback(index_trans);
+ index_transaction_free(t);
+}
+
+static enum mail_index_transaction_flags
+index_transaction_flags_get(enum mailbox_transaction_flags flags)
+{
+ enum mail_index_transaction_flags itrans_flags;
+
+ itrans_flags = MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES;
+ if ((flags & MAILBOX_TRANSACTION_FLAG_HIDE) != 0)
+ itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_HIDE;
+ if ((flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0)
+ itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL;
+ if ((flags & MAILBOX_TRANSACTION_FLAG_SYNC) != 0)
+ itrans_flags |= MAIL_INDEX_TRANSACTION_FLAG_SYNC;
+ return itrans_flags;
+}
+
+void index_transaction_init_pvt(struct mailbox_transaction_context *t)
+{
+ enum mail_index_transaction_flags itrans_flags;
+
+ if (t->box->view_pvt == NULL || t->itrans_pvt != NULL)
+ return;
+
+ itrans_flags = index_transaction_flags_get(t->flags);
+ t->itrans_pvt = mail_index_transaction_begin(t->box->view_pvt,
+ itrans_flags);
+ t->view_pvt = mail_index_transaction_open_updated_view(t->itrans_pvt);
+}
+
+void index_transaction_init(struct mailbox_transaction_context *t,
+ struct mailbox *box,
+ enum mailbox_transaction_flags flags,
+ const char *reason)
+{
+ enum mail_index_transaction_flags itrans_flags;
+
+ i_assert(box->opened);
+
+ itrans_flags = index_transaction_flags_get(flags);
+ if ((flags & MAILBOX_TRANSACTION_FLAG_REFRESH) != 0)
+ mail_index_refresh(box->index);
+
+ t->flags = flags;
+ t->box = box;
+ t->reason = i_strdup(reason);
+ t->itrans = mail_index_transaction_begin(box->view, itrans_flags);
+ t->view = mail_index_transaction_open_updated_view(t->itrans);
+
+ array_create(&t->module_contexts, default_pool,
+ sizeof(void *), 5);
+
+ t->cache_view = mail_cache_view_open(box->cache, t->view);
+ t->cache_trans = mail_cache_get_transaction(t->cache_view, t->itrans);
+
+ if ((flags & MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC) != 0)
+ mail_cache_view_update_cache_decisions(t->cache_view, FALSE);
+
+ /* set up after mail_cache_get_transaction(), so that we'll still
+ have the cache_trans available in _index_commit() */
+ t->super = t->itrans->v;
+ t->itrans->v.commit = index_transaction_index_commit;
+ t->itrans->v.rollback = index_transaction_index_rollback;
+ MODULE_CONTEXT_SET(t->itrans, mail_storage_mail_index_module, t);
+}
+
+struct mailbox_transaction_context *
+index_transaction_begin(struct mailbox *box,
+ enum mailbox_transaction_flags flags,
+ const char *reason)
+{
+ struct mailbox_transaction_context *t;
+
+ t = i_new(struct mailbox_transaction_context, 1);
+ index_transaction_init(t, box, flags, reason);
+ return t;
+}
+
+int index_transaction_commit(struct mailbox_transaction_context *t,
+ struct mail_transaction_commit_changes *changes_r)
+{
+ struct mailbox *box = t->box;
+ struct mail_index_transaction *itrans = t->itrans;
+ struct mail_index_transaction_commit_result result;
+ int ret = 0;
+
+ i_zero(changes_r);
+ changes_r->pool = pool_alloconly_create(MEMPOOL_GROWING
+ "transaction changes", 512);
+ p_array_init(&changes_r->saved_uids, changes_r->pool, 32);
+ t->changes = changes_r;
+
+ if (t->itrans_pvt != NULL)
+ ret = mail_index_transaction_commit(&t->itrans_pvt);
+ if (mail_index_transaction_commit_full(&itrans, &result) < 0)
+ ret = -1;
+ t = NULL;
+
+ if (ret < 0 && mail_index_is_deleted(box->index))
+ mailbox_set_deleted(box);
+
+ changes_r->ignored_modseq_changes = result.ignored_modseq_changes;
+ return ret;
+}
+
+void index_transaction_rollback(struct mailbox_transaction_context *t)
+{
+ struct mail_index_transaction *itrans = t->itrans;
+
+ if (t->itrans_pvt != NULL)
+ mail_index_transaction_rollback(&t->itrans_pvt);
+ mail_index_transaction_rollback(&itrans);
+}