]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Moved transaction commiting code to mail-index-transaction-*.c
authorTimo Sirainen <tss@iki.fi>
Sat, 9 May 2009 18:53:06 +0000 (14:53 -0400)
committerTimo Sirainen <tss@iki.fi>
Sat, 9 May 2009 18:53:06 +0000 (14:53 -0400)
--HG--
branch : HEAD

src/lib-index/Makefile.am
src/lib-index/mail-index-transaction-export.c [new file with mode: 0644]
src/lib-index/mail-index-transaction-finish.c [new file with mode: 0644]
src/lib-index/mail-index-transaction-private.h
src/lib-index/mail-index-transaction-sort-appends.c [new file with mode: 0644]
src/lib-index/mail-index-transaction.c
src/lib-index/mail-transaction-log-append.c
src/lib-index/mail-transaction-log.h

index 84bf35d4ea5a74cc27df7c531761f7bc440a06cc..92b4ae8703ea3bcafe9975697de2364ed0f35fd1 100644 (file)
@@ -19,6 +19,9 @@ libindex_la_SOURCES = \
         mail-index-map.c \
         mail-index-modseq.c \
         mail-index-transaction.c \
+        mail-index-transaction-export.c \
+        mail-index-transaction-finish.c \
+        mail-index-transaction-sort-appends.c \
         mail-index-transaction-view.c \
         mail-index-strmap.c \
         mail-index-sync.c \
diff --git a/src/lib-index/mail-index-transaction-export.c b/src/lib-index/mail-index-transaction-export.c
new file mode 100644 (file)
index 0000000..bea8cd4
--- /dev/null
@@ -0,0 +1,429 @@
+/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "mail-index-private.h"
+#include "mail-index-modseq.h"
+#include "mail-transaction-log-private.h"
+#include "mail-index-transaction-private.h"
+
+struct mail_index_export_context {
+       struct mail_index_transaction *trans;
+       struct mail_transaction_log_append_ctx *append_ctx;
+};
+
+static void log_append_buffer(struct mail_index_export_context *ctx,
+                             const buffer_t *buf, const buffer_t *hdr_buf,
+                             enum mail_transaction_type type)
+{
+       buffer_t *output = ctx->append_ctx->output;
+       struct mail_transaction_header hdr;
+
+       i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0);
+       i_assert((buf->used % 4) == 0);
+       i_assert(hdr_buf == NULL || (hdr_buf->used % 4) == 0);
+
+       if (buf->used == 0)
+               return;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.type = type;
+       if (type == MAIL_TRANSACTION_EXPUNGE)
+               hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT;
+       if ((ctx->trans->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0)
+               hdr.type |= MAIL_TRANSACTION_EXTERNAL;
+       hdr.size = sizeof(hdr) + buf->used +
+               (hdr_buf == NULL ? 0 : hdr_buf->used);
+       hdr.size = mail_index_uint32_to_offset(hdr.size);
+
+       buffer_append(output, &hdr, sizeof(hdr));
+       if (hdr_buf != NULL)
+               buffer_append(output, hdr_buf->data, hdr_buf->used);
+       buffer_append(output, buf->data, buf->used);
+
+       if (mail_transaction_header_has_modseq(buf->data,
+                               CONST_PTR_OFFSET(buf->data, sizeof(hdr)),
+                               ctx->append_ctx->new_highest_modseq))
+               ctx->append_ctx->new_highest_modseq++;
+}
+
+static const buffer_t *
+log_get_hdr_update_buffer(struct mail_index_transaction *t, bool prepend)
+{
+       buffer_t *buf;
+       const unsigned char *data, *mask;
+       struct mail_transaction_header_update u;
+       uint16_t offset;
+       int state = 0;
+
+       memset(&u, 0, sizeof(u));
+
+       data = prepend ? t->pre_hdr_change : t->post_hdr_change;
+       mask = prepend ? t->pre_hdr_mask : t->post_hdr_mask;
+
+       buf = buffer_create_dynamic(pool_datastack_create(), 256);
+       for (offset = 0; offset <= sizeof(t->pre_hdr_change); offset++) {
+               if (offset < sizeof(t->pre_hdr_change) && mask[offset]) {
+                       if (state == 0) {
+                               u.offset = offset;
+                               state++;
+                       }
+               } else {
+                       if (state > 0) {
+                               u.size = offset - u.offset;
+                               buffer_append(buf, &u, sizeof(u));
+                               buffer_append(buf, data + u.offset, u.size);
+                               state = 0;
+                       }
+               }
+       }
+       return buf;
+}
+
+static void log_append_ext_intro(struct mail_index_export_context *ctx,
+                                uint32_t ext_id, uint32_t reset_id)
+{
+       struct mail_index_transaction *t = ctx->trans;
+       const struct mail_index_registered_ext *rext;
+        struct mail_transaction_ext_intro *intro, *resizes;
+       buffer_t *buf;
+       uint32_t idx;
+       unsigned int count;
+
+       i_assert(ext_id != (uint32_t)-1);
+
+       if (t->reset ||
+           !mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
+               /* new extension */
+               idx = (uint32_t)-1;
+       }
+
+       rext = array_idx(&t->view->index->extensions, ext_id);
+       if (!array_is_created(&t->ext_resizes)) {
+               resizes = NULL;
+               count = 0;
+       } else {
+               resizes = array_get_modifiable(&t->ext_resizes, &count);
+       }
+
+       buf = buffer_create_dynamic(pool_datastack_create(), 128);
+       if (ext_id < count && resizes[ext_id].name_size != 0) {
+               /* we're resizing the extension. use the resize struct. */
+               intro = &resizes[ext_id];
+
+               i_assert(intro->ext_id == idx);
+               intro->name_size = idx != (uint32_t)-1 ? 0 :
+                       strlen(rext->name);
+               buffer_append(buf, intro, sizeof(*intro));
+       } else {
+               /* generate a new intro structure */
+               intro = buffer_append_space_unsafe(buf, sizeof(*intro));
+               intro->ext_id = idx;
+               intro->hdr_size = rext->hdr_size;
+               intro->record_size = rext->record_size;
+               intro->record_align = rext->record_align;
+               intro->flags = MAIL_TRANSACTION_EXT_INTRO_FLAG_NO_SHRINK;
+               intro->name_size = idx != (uint32_t)-1 ? 0 :
+                       strlen(rext->name);
+       }
+       if (reset_id != 0) {
+               /* we're going to reset this extension in this transaction */
+               intro->reset_id = reset_id;
+       } else if (idx != (uint32_t)-1) {
+               /* use the existing reset_id */
+               const struct mail_index_ext *map_ext =
+                       array_idx(&t->view->index->map->extensions, idx);
+               intro->reset_id = map_ext->reset_id;
+       } else {
+               /* new extension, reset_id defaults to 0 */
+       }
+       buffer_append(buf, rext->name, intro->name_size);
+       if ((buf->used % 4) != 0)
+               buffer_append_zero(buf, 4 - (buf->used % 4));
+
+       if (ctx->append_ctx->new_highest_modseq == 0 &&
+           strcmp(rext->name, MAIL_INDEX_MODSEQ_EXT_NAME) == 0) {
+               /* modseq tracking started */
+               ctx->append_ctx->new_highest_modseq = 1;
+       }
+
+       log_append_buffer(ctx, buf, NULL, MAIL_TRANSACTION_EXT_INTRO);
+}
+
+static void
+log_append_ext_hdr_update(struct mail_index_export_context *ctx,
+                       const struct mail_index_transaction_ext_hdr_update *hdr)
+{
+       buffer_t *buf;
+       const unsigned char *data, *mask;
+       struct mail_transaction_ext_hdr_update u;
+       uint16_t offset;
+       bool started = FALSE;
+
+       memset(&u, 0, sizeof(u));
+
+       data = hdr->data;
+       mask = hdr->mask;
+
+       buf = buffer_create_dynamic(pool_datastack_create(), 256);
+       for (offset = 0; offset <= hdr->alloc_size; offset++) {
+               if (offset < hdr->alloc_size && mask[offset] != 0) {
+                       if (!started) {
+                               u.offset = offset;
+                               started = TRUE;
+                       }
+               } else {
+                       if (started) {
+                               u.size = offset - u.offset;
+                               buffer_append(buf, &u, sizeof(u));
+                               buffer_append(buf, data + u.offset, u.size);
+                               started = FALSE;
+                       }
+               }
+       }
+       if (buf->used % 4 != 0)
+               buffer_append_zero(buf, 4 - buf->used % 4);
+       log_append_buffer(ctx, buf, NULL, MAIL_TRANSACTION_EXT_HDR_UPDATE);
+}
+
+static void
+mail_transaction_log_append_ext_intros(struct mail_index_export_context *ctx)
+{
+       struct mail_index_transaction *t = ctx->trans;
+        const struct mail_transaction_ext_intro *resize;
+       const struct mail_index_transaction_ext_hdr_update *hdrs;
+       struct mail_transaction_ext_reset ext_reset;
+       unsigned int resize_count, ext_count = 0;
+       unsigned int hdrs_count, reset_id_count, reset_count;
+       uint32_t ext_id, reset_id;
+       const struct mail_transaction_ext_reset *reset;
+       const uint32_t *reset_ids;
+       buffer_t *reset_buf;
+
+       if (!array_is_created(&t->ext_resizes)) {
+               resize = NULL;
+               resize_count = 0;
+       } else {
+               resize = array_get(&t->ext_resizes, &resize_count);
+               if (ext_count < resize_count)
+                       ext_count = resize_count;
+       }
+
+       if (!array_is_created(&t->ext_reset_ids)) {
+               reset_ids = NULL;
+               reset_id_count = 0;
+       } else {
+               reset_ids = array_get(&t->ext_reset_ids, &reset_id_count);
+       }
+
+       if (!array_is_created(&t->ext_resets)) {
+               reset = NULL;
+               reset_count = 0;
+       } else {
+               reset = array_get(&t->ext_resets, &reset_count);
+               if (ext_count < reset_count)
+                       ext_count = reset_count;
+       }
+
+       if (!array_is_created(&t->ext_hdr_updates)) {
+               hdrs = NULL;
+               hdrs_count = 0;
+       } else {
+               hdrs = array_get(&t->ext_hdr_updates, &hdrs_count);
+               if (ext_count < hdrs_count)
+                       ext_count = hdrs_count;
+       }
+
+       memset(&ext_reset, 0, sizeof(ext_reset));
+       reset_buf = buffer_create_data(pool_datastack_create(),
+                                      &ext_reset, sizeof(ext_reset));
+       buffer_set_used_size(reset_buf, sizeof(ext_reset));
+
+       for (ext_id = 0; ext_id < ext_count; ext_id++) {
+               if (ext_id < reset_count)
+                       ext_reset = reset[ext_id];
+               else
+                       ext_reset.new_reset_id = 0;
+               if ((ext_id < resize_count && resize[ext_id].name_size) ||
+                   ext_reset.new_reset_id != 0 ||
+                   (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0)) {
+                       if (ext_reset.new_reset_id != 0) {
+                               /* we're going to reset this extension
+                                  immediately after the intro */
+                               reset_id = 0;
+                       } else {
+                               reset_id = ext_id < reset_id_count ?
+                                       reset_ids[ext_id] : 0;
+                       }
+                       log_append_ext_intro(ctx, ext_id, reset_id);
+               }
+               if (ext_reset.new_reset_id != 0) {
+                       i_assert(ext_id < reset_id_count &&
+                                ext_reset.new_reset_id == reset_ids[ext_id]);
+                       log_append_buffer(ctx, reset_buf, NULL,
+                                         MAIL_TRANSACTION_EXT_RESET);
+               }
+               if (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0) {
+                       T_BEGIN {
+                               log_append_ext_hdr_update(ctx, &hdrs[ext_id]);
+                       } T_END;
+               }
+       }
+}
+
+static void log_append_ext_recs(struct mail_index_export_context *ctx,
+                               const ARRAY_TYPE(seq_array_array) *arr,
+                               enum mail_transaction_type type)
+{
+       struct mail_index_transaction *t = ctx->trans;
+       const ARRAY_TYPE(seq_array) *updates;
+       const uint32_t *reset_ids;
+       unsigned int ext_id, count, reset_id_count;
+       uint32_t reset_id;
+
+       if (!array_is_created(&t->ext_reset_ids)) {
+               reset_ids = NULL;
+               reset_id_count = 0;
+       } else {
+               reset_ids = array_get_modifiable(&t->ext_reset_ids,
+                                                &reset_id_count);
+       }
+
+       updates = array_get(arr, &count);
+       for (ext_id = 0; ext_id < count; ext_id++) {
+               if (!array_is_created(&updates[ext_id]))
+                       continue;
+
+               reset_id = ext_id < reset_id_count ? reset_ids[ext_id] : 0;
+               log_append_ext_intro(ctx, ext_id, reset_id);
+
+               log_append_buffer(ctx, updates[ext_id].arr.buffer, NULL, type);
+       }
+}
+
+static void
+log_append_keyword_update(struct mail_index_export_context *ctx,
+                         buffer_t *hdr_buf, enum modify_type modify_type,
+                         const char *keyword, const buffer_t *buffer)
+{
+       struct mail_transaction_keyword_update kt_hdr;
+
+       memset(&kt_hdr, 0, sizeof(kt_hdr));
+       kt_hdr.modify_type = modify_type;
+       kt_hdr.name_size = strlen(keyword);
+
+       buffer_set_used_size(hdr_buf, 0);
+       buffer_append(hdr_buf, &kt_hdr, sizeof(kt_hdr));
+       buffer_append(hdr_buf, keyword, kt_hdr.name_size);
+       if ((hdr_buf->used % 4) != 0)
+               buffer_append_zero(hdr_buf, 4 - (hdr_buf->used % 4));
+
+       log_append_buffer(ctx, buffer, hdr_buf,
+                         MAIL_TRANSACTION_KEYWORD_UPDATE);
+}
+
+static enum mail_index_sync_type
+log_append_keyword_updates(struct mail_index_export_context *ctx)
+{
+        const struct mail_index_transaction_keyword_update *updates;
+       const char *const *keywords;
+       buffer_t *hdr_buf;
+       enum mail_index_sync_type change_mask = 0;
+       unsigned int i, count, keywords_count;
+
+       hdr_buf = buffer_create_dynamic(pool_datastack_create(), 64);
+
+       keywords = array_get_modifiable(&ctx->trans->view->index->keywords,
+                                       &keywords_count);
+       updates = array_get_modifiable(&ctx->trans->keyword_updates, &count);
+       i_assert(count <= keywords_count);
+
+       for (i = 0; i < count; i++) {
+               if (array_is_created(&updates[i].add_seq)) {
+                       change_mask |= MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD;
+                       log_append_keyword_update(ctx, hdr_buf,
+                                       MODIFY_ADD, keywords[i],
+                                       updates[i].add_seq.arr.buffer);
+               }
+               if (array_is_created(&updates[i].remove_seq)) {
+                       change_mask |= MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE;
+                       log_append_keyword_update(ctx, hdr_buf,
+                                       MODIFY_REMOVE, keywords[i],
+                                       updates[i].remove_seq.arr.buffer);
+               }
+       }
+       return change_mask;
+}
+
+void mail_index_transaction_export(struct mail_index_transaction *t,
+                                  struct mail_transaction_log_append_ctx *append_ctx)
+{
+       enum mail_index_sync_type change_mask = 0;
+       struct mail_index_export_context ctx;
+
+       memset(&ctx, 0, sizeof(ctx));
+       ctx.trans = t;
+       ctx.append_ctx = append_ctx;
+
+       /* send all extension introductions and resizes before appends
+          to avoid resize overhead as much as possible */
+        mail_transaction_log_append_ext_intros(&ctx);
+
+       if (t->pre_hdr_changed) {
+               log_append_buffer(&ctx,
+                                 log_get_hdr_update_buffer(t, TRUE),
+                                 NULL, MAIL_TRANSACTION_HEADER_UPDATE);
+       }
+       if (array_is_created(&t->appends)) {
+               change_mask |= MAIL_INDEX_SYNC_TYPE_APPEND;
+               log_append_buffer(&ctx, t->appends.arr.buffer, NULL,
+                                 MAIL_TRANSACTION_APPEND);
+       }
+       if (array_is_created(&t->updates)) {
+               change_mask |= MAIL_INDEX_SYNC_TYPE_FLAGS;
+               log_append_buffer(&ctx, t->updates.arr.buffer, NULL,
+                                 MAIL_TRANSACTION_FLAG_UPDATE);
+       }
+
+       if (array_is_created(&t->ext_rec_updates)) {
+               log_append_ext_recs(&ctx, &t->ext_rec_updates,
+                                   MAIL_TRANSACTION_EXT_REC_UPDATE);
+       }
+       if (array_is_created(&t->ext_rec_atomics)) {
+               log_append_ext_recs(&ctx, &t->ext_rec_atomics,
+                                   MAIL_TRANSACTION_EXT_ATOMIC_INC);
+       }
+
+       /* keyword resets before updates */
+       if (array_is_created(&t->keyword_resets)) {
+               change_mask |= MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET;
+               log_append_buffer(&ctx, t->keyword_resets.arr.buffer,
+                                 NULL, MAIL_TRANSACTION_KEYWORD_RESET);
+       }
+       if (array_is_created(&t->keyword_updates))
+               change_mask |= log_append_keyword_updates(&ctx);
+
+       if (array_is_created(&t->expunges)) {
+               /* non-external expunges are only requests, ignore them when
+                  checking fsync_mask */
+               if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0)
+                       change_mask |= MAIL_INDEX_SYNC_TYPE_EXPUNGE;
+               log_append_buffer(&ctx, t->expunges.arr.buffer, NULL,
+                                 MAIL_TRANSACTION_EXPUNGE);
+       }
+
+       if (t->post_hdr_changed) {
+               log_append_buffer(&ctx, log_get_hdr_update_buffer(t, FALSE),
+                                 NULL, MAIL_TRANSACTION_HEADER_UPDATE);
+       }
+
+       /* Update the tail offsets only when committing the sync transaction.
+          Other transactions may not know the latest tail offset and might
+          end up shrinking it. (Alternatively the shrinking tail offsets could
+          just be ignored, which would probably work fine too.) */
+       append_ctx->append_sync_offset = t->sync_transaction;
+
+       append_ctx->want_fsync =
+               (t->view->index->fsync_mask & change_mask) != 0 ||
+               (t->flags & MAIL_INDEX_TRANSACTION_FLAG_FSYNC) != 0;
+}
diff --git a/src/lib-index/mail-index-transaction-finish.c b/src/lib-index/mail-index-transaction-finish.c
new file mode 100644 (file)
index 0000000..83bd9ab
--- /dev/null
@@ -0,0 +1,294 @@
+/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "mail-index-private.h"
+#include "mail-index-modseq.h"
+#include "mail-index-transaction-private.h"
+
+static void
+ext_reset_update_atomic(struct mail_index_transaction *t,
+                       uint32_t ext_id, uint32_t expected_reset_id)
+{
+       const struct mail_index_ext *map_ext;
+       struct mail_transaction_ext_reset *reset;
+       uint32_t idx, reset_id;
+
+       if (!mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
+               /* new extension */
+               reset_id = 1;
+       } else {
+               map_ext = array_idx(&t->view->index->map->extensions, idx);
+               reset_id = map_ext->reset_id + 1;
+       }
+       if (reset_id != expected_reset_id) {
+               /* ignore this extension update */
+               mail_index_ext_set_reset_id(t, ext_id, 0);
+               return;
+       }
+
+       if (reset_id == 0)
+               reset_id++;
+
+       array_idx_set(&t->ext_reset_ids, ext_id, &reset_id);
+
+       /* reseting existing data is optional */
+       if (array_is_created(&t->ext_resets)) {
+               reset = array_idx_modifiable(&t->ext_resets, ext_id);
+               if (reset->new_reset_id == (uint32_t)-1)
+                       reset->new_reset_id = reset_id;
+       }
+}
+
+static void
+transaction_update_atomic_reset_ids(struct mail_index_transaction *t)
+{
+       const uint32_t *expected_reset_ids;
+       unsigned int ext_id, count;
+
+       if (!array_is_created(&t->ext_reset_atomic))
+               return;
+
+       expected_reset_ids = array_get(&t->ext_reset_atomic, &count);
+       for (ext_id = 0; ext_id < count; ext_id++) {
+               if (expected_reset_ids[ext_id] != 0) {
+                       ext_reset_update_atomic(t, ext_id,
+                                               expected_reset_ids[ext_id]);
+               }
+       }
+}
+
+static bool
+mail_index_update_cancel_array(ARRAY_TYPE(seq_range) *array, uint32_t seq)
+{
+       if (array_is_created(array)) {
+               if (seq_range_array_remove(array, seq)) {
+                       if (array_count(array) == 0)
+                               array_free(array);
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+static bool
+mail_index_update_cancel(struct mail_index_transaction *t, uint32_t seq)
+{
+       struct mail_index_transaction_keyword_update *kw;
+       struct mail_transaction_flag_update *updates, tmp_update;
+       unsigned int i, count;
+       bool ret, have_kw_changes = FALSE;
+
+       ret = mail_index_update_cancel_array(&t->keyword_resets, seq);
+       if (array_is_created(&t->keyword_updates)) {
+               kw = array_get_modifiable(&t->keyword_updates, &count);
+               for (i = 0; i < count; i++) {
+                       if (mail_index_update_cancel_array(&kw[i].add_seq, seq))
+                               ret = TRUE;
+                       if (mail_index_update_cancel_array(&kw[i].remove_seq,
+                                                          seq))
+                               ret = TRUE;
+                       if (array_is_created(&kw[i].add_seq) ||
+                           array_is_created(&kw[i].remove_seq))
+                               have_kw_changes = TRUE;
+               }
+               if (!have_kw_changes)
+                       array_free(&t->keyword_updates);
+       }
+
+       if (!array_is_created(&t->updates))
+               return ret;
+
+       updates = array_get_modifiable(&t->updates, &count);
+       i = mail_index_transaction_get_flag_update_pos(t, 0, count, seq);
+       if (i < count && updates[i].uid1 <= seq && updates[i].uid2 >= seq) {
+               /* exists */
+               ret = TRUE;
+               if (updates[i].uid1 == seq && updates[i].uid2 == seq) {
+                       if (count > 1)
+                               array_delete(&t->updates, i, 1);
+                       else
+                               array_free(&t->updates);
+               } else if (updates[i].uid1 == seq)
+                       updates[i].uid1++;
+               else if (updates[i].uid2 == seq)
+                       updates[i].uid2--;
+               else {
+                       /* need to split it in two */
+                       tmp_update = updates[i];
+                       tmp_update.uid1 = seq+1;
+                       updates[i].uid2 = seq-1;
+                       array_insert(&t->updates, i + 1, &tmp_update, 1);
+               }
+       }
+       return ret;
+}
+
+static void
+mail_index_transaction_check_conflicts(struct mail_index_transaction *t)
+{
+       uint32_t seq;
+
+       i_assert(t->max_modseq != 0);
+       i_assert(t->conflict_seqs != NULL);
+
+       if (t->max_modseq == mail_index_modseq_get_highest(t->view)) {
+               /* no conflicts possible */
+               return;
+       }
+       if (t->min_flagupdate_seq == 0) {
+               /* no flag updates */
+               return;
+       }
+
+       for (seq = t->min_flagupdate_seq; seq <= t->max_flagupdate_seq; seq++) {
+               if (mail_index_modseq_lookup(t->view, seq) > t->max_modseq) {
+                       if (mail_index_update_cancel(t, seq))
+                               seq_range_array_add(t->conflict_seqs, 0, seq);
+               }
+       }
+       mail_index_transaction_set_log_updates(t);
+}
+
+static uint32_t
+mail_index_transaction_get_uid(struct mail_index_transaction *t, uint32_t seq)
+{
+       const struct mail_index_record *rec;
+
+       i_assert(seq > 0);
+
+       if (seq >= t->first_new_seq)
+               rec = mail_index_transaction_lookup(t, seq);
+       else {
+               i_assert(seq <= t->view->map->hdr.messages_count);
+               rec = MAIL_INDEX_MAP_IDX(t->view->map, seq - 1);
+       }
+       i_assert(rec->uid != 0);
+       return rec->uid;
+}
+
+static void
+mail_index_convert_to_uids(struct mail_index_transaction *t,
+                          ARRAY_TYPE(seq_array) *array)
+{
+       uint32_t *seq;
+       unsigned int i, count;
+
+       if (!array_is_created(array))
+               return;
+
+       count = array_count(array);
+       for (i = 0; i < count; i++) {
+               seq = array_idx_modifiable(array, i);
+               *seq = mail_index_transaction_get_uid(t, *seq);
+       }
+}
+
+static uint32_t
+get_nonexpunged_uid2(struct mail_index_transaction *t,
+                    uint32_t uid1, uint32_t seq1)
+{
+       seq1++;
+
+       while (mail_index_transaction_get_uid(t, seq1) == uid1 + 1) {
+               seq1++;
+               uid1++;
+       }
+       return uid1;
+}
+
+static void
+mail_index_convert_to_uid_ranges(struct mail_index_transaction *t,
+                                ARRAY_TYPE(seq_range) *array)
+{
+       struct seq_range *range, *new_range;
+       unsigned int i, count;
+       uint32_t uid1, uid2;
+
+       if (!array_is_created(array))
+               return;
+
+       count = array_count(array);
+       for (i = 0; i < count; i++) {
+               range = array_idx_modifiable(array, i);
+
+               uid1 = mail_index_transaction_get_uid(t, range->seq1);
+               uid2 = mail_index_transaction_get_uid(t, range->seq2);
+               if (uid2 - uid1 == range->seq2 - range->seq1) {
+                       /* simple conversion */
+                       range->seq1 = uid1;
+                       range->seq2 = uid2;
+               } else {
+                       /* remove expunged UIDs */
+                       new_range = array_insert_space(array, i);
+                       range = array_idx_modifiable(array, i + 1);
+                       count++;
+
+                       memcpy(new_range, range, array->arr.element_size);
+                       new_range->seq1 = uid1;
+                       new_range->seq2 = get_nonexpunged_uid2(t, uid1,
+                                                              range->seq1);
+
+                       /* continue the range without the inserted seqs */
+                       range->seq1 += new_range->seq2 - new_range->seq1 + 1;
+               }
+       }
+}
+
+static void keyword_updates_convert_to_uids(struct mail_index_transaction *t)
+{
+        struct mail_index_transaction_keyword_update *updates;
+       unsigned int i, count;
+
+       if (!array_is_created(&t->keyword_updates))
+               return;
+
+       updates = array_get_modifiable(&t->keyword_updates, &count);
+       for (i = 0; i < count; i++) {
+               mail_index_convert_to_uid_ranges(t, &updates[i].add_seq);
+               mail_index_convert_to_uid_ranges(t, &updates[i].remove_seq);
+       }
+}
+
+static void
+mail_index_transaction_convert_to_uids(struct mail_index_transaction *t)
+{
+       ARRAY_TYPE(seq_array) *updates;
+       unsigned int i, count;
+
+       if (array_is_created(&t->ext_rec_updates)) {
+               updates = array_get_modifiable(&t->ext_rec_updates, &count);
+               for (i = 0; i < count; i++)
+                       mail_index_convert_to_uids(t, (void *)&updates[i]);
+       }
+       if (array_is_created(&t->ext_rec_atomics)) {
+               updates = array_get_modifiable(&t->ext_rec_atomics, &count);
+               for (i = 0; i < count; i++)
+                       mail_index_convert_to_uids(t, (void *)&updates[i]);
+       }
+
+        keyword_updates_convert_to_uids(t);
+
+       mail_index_convert_to_uid_ranges(t, &t->expunges);
+       mail_index_convert_to_uid_ranges(t, (void *)&t->updates);
+       mail_index_convert_to_uid_ranges(t, &t->keyword_resets);
+}
+
+int mail_index_transaction_finish(struct mail_index_transaction *t)
+{
+       mail_index_transaction_sort_appends(t);
+
+       if (array_is_created(&t->ext_reset_atomic) || t->max_modseq != 0) {
+               if (mail_index_map(t->view->index,
+                                  MAIL_INDEX_SYNC_HANDLER_HEAD) <= 0)
+                       return -1;
+       }
+       if (array_is_created(&t->ext_reset_atomic))
+               transaction_update_atomic_reset_ids(t);
+       if (t->max_modseq != 0)
+               mail_index_transaction_check_conflicts(t);
+       /* finally convert all sequences to UIDs before we write them,
+          but after we've checked and removed conflicts */
+       mail_index_transaction_convert_to_uids(t);
+       return 0;
+}
index 0bc92d101b3f3338f80c3633178879150d85fe46..56ddafee70a3db71e75adcb69f8064cb860f6694 100644 (file)
@@ -89,6 +89,10 @@ struct mail_index_transaction {
        unsigned int log_ext_updates:1;
 };
 
+#define MAIL_INDEX_TRANSACTION_HAS_CHANGES(t) \
+       ((t)->log_updates || (t)->log_ext_updates || \
+        (array_is_created(&(t)->updates) && array_count(&(t)->updates) > 0))
+
 extern void (*hook_mail_index_transaction_created)
                (struct mail_index_transaction *t);
 
@@ -100,8 +104,7 @@ void mail_index_transaction_unref(struct mail_index_transaction **t);
 
 void mail_index_transaction_sort_appends(struct mail_index_transaction *t);
 uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t);
-void mail_index_transaction_convert_to_uids(struct mail_index_transaction *t);
-void mail_index_transaction_check_conflicts(struct mail_index_transaction *t);
+void mail_index_transaction_set_log_updates(struct mail_index_transaction *t);
 
 unsigned int
 mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t,
@@ -111,5 +114,12 @@ mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t,
 
 bool mail_index_seq_array_lookup(const ARRAY_TYPE(seq_array) *array,
                                 uint32_t seq, unsigned int *idx_r);
+bool mail_index_seq_array_add(ARRAY_TYPE(seq_array) *array, uint32_t seq,
+                             const void *record, size_t record_size,
+                             void *old_record);
+
+int mail_index_transaction_finish(struct mail_index_transaction *t);
+void mail_index_transaction_export(struct mail_index_transaction *t,
+                                  struct mail_transaction_log_append_ctx *append_ctx);
 
 #endif
diff --git a/src/lib-index/mail-index-transaction-sort-appends.c b/src/lib-index/mail-index-transaction-sort-appends.c
new file mode 100644 (file)
index 0000000..a62a635
--- /dev/null
@@ -0,0 +1,182 @@
+/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "seq-range-array.h"
+#include "mail-index-private.h"
+#include "mail-index-transaction-private.h"
+
+#include <stdlib.h>
+
+struct uid_map {
+       uint32_t idx;
+       uint32_t uid;
+};
+
+static int uid_map_cmp(const void *p1, const void *p2)
+{
+       const struct uid_map *m1 = p1, *m2 = p2;
+
+       return m1->uid < m2->uid ? -1 :
+               (m1->uid > m2->uid ? 1 : 0);
+}
+
+static void
+mail_index_transaction_sort_appends_ext(ARRAY_TYPE(seq_array_array) *updates,
+                                       uint32_t first_new_seq,
+                                       const uint32_t *old_to_newseq_map)
+{
+       ARRAY_TYPE(seq_array) *ext_rec_arrays;
+       ARRAY_TYPE(seq_array) *old_array;
+       ARRAY_TYPE(seq_array) new_array;
+       unsigned int ext_count;
+       const uint32_t *ext_rec;
+       uint32_t seq;
+       unsigned int i, j, count;
+
+       if (!array_is_created(updates))
+               return;
+
+       ext_rec_arrays = array_get_modifiable(updates, &count);
+       for (j = 0; j < count; j++) {
+               old_array = &ext_rec_arrays[j];
+               if (!array_is_created(old_array))
+                       continue;
+
+               ext_count = array_count(old_array);
+               array_create(&new_array, default_pool,
+                            old_array->arr.element_size, ext_count);
+               for (i = 0; i < ext_count; i++) {
+                       ext_rec = array_idx(old_array, i);
+
+                       seq = *ext_rec < first_new_seq ? *ext_rec :
+                               old_to_newseq_map[*ext_rec - first_new_seq];
+                       mail_index_seq_array_add(&new_array, seq, ext_rec+1,
+                                                old_array->arr.element_size -
+                                                sizeof(*ext_rec), NULL);
+               }
+               array_free(old_array);
+               ext_rec_arrays[j] = new_array;
+       }
+}
+
+static void
+sort_appends_seq_range(ARRAY_TYPE(seq_range) *array, uint32_t first_new_seq,
+                      const uint32_t *old_to_newseq_map)
+{
+       struct seq_range *range, temp_range;
+       ARRAY_TYPE(seq_range) old_seqs;
+       uint32_t idx, idx1, idx2;
+       unsigned int i, count;
+
+       range = array_get_modifiable(array, &count);
+       for (i = 0; i < count; i++) {
+               if (range[i].seq2 >= first_new_seq)
+                       break;
+       }
+       if (i == count) {
+               /* nothing to do */
+               return;
+       }
+
+       i_array_init(&old_seqs, count - i);
+       if (range[i].seq1 < first_new_seq) {
+               temp_range.seq1 = first_new_seq;
+               temp_range.seq2 = range[i].seq2;
+               array_append(&old_seqs, &temp_range, 1);
+               range[i].seq2 = first_new_seq - 1;
+               i++;
+       }
+       array_append(&old_seqs, &range[i], count - i);
+       array_delete(array, i, count - i);
+
+       range = array_get_modifiable(&old_seqs, &count);
+       for (i = 0; i < count; i++) {
+               idx1 = range[i].seq1 - first_new_seq;
+               idx2 = range[i].seq2 - first_new_seq;
+               for (idx = idx1; idx <= idx2; idx++)
+                       seq_range_array_add(array, 0, old_to_newseq_map[idx]);
+       }
+       array_free(&old_seqs);
+}
+
+static void
+mail_index_transaction_sort_appends_keywords(struct mail_index_transaction *t,
+                                            const uint32_t *old_to_newseq_map)
+{
+       struct mail_index_transaction_keyword_update *updates;
+       unsigned int i, count;
+
+       if (array_is_created(&t->keyword_updates)) {
+               updates = array_get_modifiable(&t->keyword_updates, &count);
+               for (i = 0; i < count; i++) {
+                       if (array_is_created(&updates[i].add_seq)) {
+                               sort_appends_seq_range(&updates[i].add_seq,
+                                                      t->first_new_seq,
+                                                      old_to_newseq_map);
+                       }
+                       if (array_is_created(&updates[i].remove_seq)) {
+                               sort_appends_seq_range(&updates[i].remove_seq,
+                                                      t->first_new_seq,
+                                                      old_to_newseq_map);
+                       }
+               }
+       }
+
+       if (array_is_created(&t->keyword_resets)) {
+               sort_appends_seq_range(&t->keyword_resets, t->first_new_seq,
+                                      old_to_newseq_map);
+       }
+}
+
+void mail_index_transaction_sort_appends(struct mail_index_transaction *t)
+{
+       struct mail_index_record *recs, *sorted_recs;
+       struct uid_map *new_uid_map;
+       uint32_t *old_to_newseq_map;
+       unsigned int i, count;
+
+       if (!t->appends_nonsorted || !array_is_created(&t->appends))
+               return;
+
+       /* first make a copy of the UIDs and map them to sequences */
+       recs = array_get_modifiable(&t->appends, &count);
+       i_assert(count > 0);
+
+       new_uid_map = i_new(struct uid_map, count);
+       for (i = 0; i < count; i++) {
+               new_uid_map[i].idx = i;
+               new_uid_map[i].uid = recs[i].uid;
+       }
+
+       /* now sort the UID map */
+       qsort(new_uid_map, count, sizeof(*new_uid_map), uid_map_cmp);
+
+       /* sort mail records */
+       sorted_recs = i_new(struct mail_index_record, count);
+       sorted_recs[0] = recs[new_uid_map[0].idx];
+       for (i = 1; i < count; i++) {
+               sorted_recs[i] = recs[new_uid_map[i].idx];
+               if (sorted_recs[i].uid == sorted_recs[i-1].uid)
+                       i_panic("Duplicate UIDs added in transaction");
+       }
+       buffer_write(t->appends.arr.buffer, 0, sorted_recs,
+                    sizeof(*sorted_recs) * count);
+       i_free(sorted_recs);
+
+       old_to_newseq_map = i_new(uint32_t, count);
+       for (i = 0; i < count; i++)
+               old_to_newseq_map[new_uid_map[i].idx] = i + t->first_new_seq;
+       i_free(new_uid_map);
+
+       mail_index_transaction_sort_appends_ext(&t->ext_rec_updates,
+                                               t->first_new_seq,
+                                               old_to_newseq_map);
+       mail_index_transaction_sort_appends_ext(&t->ext_rec_atomics,
+                                               t->first_new_seq,
+                                               old_to_newseq_map);
+       mail_index_transaction_sort_appends_keywords(t, old_to_newseq_map);
+       i_free(old_to_newseq_map);
+
+       t->appends_nonsorted = FALSE;
+}
index 1e938f3a74f838069d65b4762273243f1cb5afe6..c082910339186dd8380c5237489d6943b05681c2 100644 (file)
@@ -8,17 +8,11 @@
 #include "ioloop.h"
 #include "array.h"
 #include "bsearch-insert-pos.h"
-#include "seq-range-array.h"
-#include "mail-index-view-private.h"
-#include "mail-index-modseq.h"
-#include "mail-transaction-log.h"
-#include "mail-cache-private.h"
+#include "mail-index-private.h"
+#include "mail-transaction-log-private.h"
+#include "mail-cache.h"
 #include "mail-index-transaction-private.h"
 
-#include <stddef.h>
-#include <stdlib.h>
-#include <time.h>
-
 void (*hook_mail_index_transaction_created)
                (struct mail_index_transaction *t) = NULL;
 
@@ -106,10 +100,10 @@ void mail_index_transaction_reset(struct mail_index_transaction *t)
        t->log_ext_updates = FALSE;
 }
 
-static bool mail_index_transaction_has_changes(struct mail_index_transaction *t)
+void mail_index_transaction_set_log_updates(struct mail_index_transaction *t)
 {
        /* flag updates aren't included in log_updates */
-       return array_is_created(&t->appends) ||
+       t->log_updates = array_is_created(&t->appends) ||
                array_is_created(&t->expunges) ||
                array_is_created(&t->keyword_resets) ||
                array_is_created(&t->keyword_updates) ||
@@ -172,9 +166,9 @@ bool mail_index_seq_array_lookup(const ARRAY_TYPE(seq_array) *array,
                                  mail_index_seq_record_cmp, idx_r);
 }
 
-static bool mail_index_seq_array_add(ARRAY_TYPE(seq_array) *array, uint32_t seq,
-                                    const void *record, size_t record_size,
-                                    void *old_record)
+bool mail_index_seq_array_add(ARRAY_TYPE(seq_array) *array, uint32_t seq,
+                             const void *record, size_t record_size,
+                             void *old_record)
 {
        void *p;
        unsigned int idx, aligned_record_size;
@@ -208,140 +202,98 @@ static bool mail_index_seq_array_add(ARRAY_TYPE(seq_array) *array, uint32_t seq,
        }
 }
 
-static uint32_t
-mail_index_transaction_get_uid(struct mail_index_transaction *t, uint32_t seq)
+uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t)
 {
-       const struct mail_index_record *rec;
-
-       i_assert(seq > 0);
+       const struct mail_index_header *head_hdr, *hdr;
+       unsigned int offset;
+       uint32_t next_uid;
 
-       if (seq >= t->first_new_seq)
-               rec = mail_index_transaction_lookup(t, seq);
-       else {
-               i_assert(seq <= t->view->map->hdr.messages_count);
-               rec = MAIL_INDEX_MAP_IDX(t->view->map, seq - 1);
+       head_hdr = &t->view->index->map->hdr;
+       hdr = &t->view->map->hdr;
+       next_uid = t->reset || head_hdr->uid_validity != hdr->uid_validity ?
+               1 : hdr->next_uid;
+       if (array_is_created(&t->appends) && t->highest_append_uid != 0) {
+               /* get next_uid from appends if they have UIDs */
+               i_assert(next_uid <= t->highest_append_uid);
+               next_uid = t->highest_append_uid + 1;
        }
-       i_assert(rec->uid != 0);
-       return rec->uid;
-}
 
-static void
-mail_index_convert_to_uids(struct mail_index_transaction *t,
-                          ARRAY_TYPE(seq_array) *array)
-{
-       uint32_t *seq;
-       unsigned int i, count;
-
-       if (!array_is_created(array))
-               return;
-
-       count = array_count(array);
-       for (i = 0; i < count; i++) {
-               seq = array_idx_modifiable(array, i);
-               *seq = mail_index_transaction_get_uid(t, *seq);
+       /* see if it's been updated in pre/post header changes */
+       offset = offsetof(struct mail_index_header, next_uid);
+       if (t->post_hdr_mask[offset] != 0) {
+               hdr = (const void *)t->post_hdr_change;
+               if (hdr->next_uid > next_uid)
+                       next_uid = hdr->next_uid;
        }
-}
-
-static uint32_t
-get_nonexpunged_uid2(struct mail_index_transaction *t,
-                    uint32_t uid1, uint32_t seq1)
-{
-       seq1++;
-
-       while (mail_index_transaction_get_uid(t, seq1) == uid1 + 1) {
-               seq1++;
-               uid1++;
+       if (t->pre_hdr_mask[offset] != 0) {
+               hdr = (const void *)t->pre_hdr_change;
+               if (hdr->next_uid > next_uid)
+                       next_uid = hdr->next_uid;
        }
-       return uid1;
+       return next_uid;
 }
 
-static void
-mail_index_convert_to_uid_ranges(struct mail_index_transaction *t,
-                                ARRAY_TYPE(seq_range) *array)
+static int
+mail_transaction_log_file_refresh(struct mail_index_transaction *t,
+                                 struct mail_transaction_log_append_ctx *ctx)
 {
-       struct seq_range *range, *new_range;
-       unsigned int i, count;
-       uint32_t uid1, uid2;
-
-       if (!array_is_created(array))
-               return;
-
-       count = array_count(array);
-       for (i = 0; i < count; i++) {
-               range = array_idx_modifiable(array, i);
-
-               uid1 = mail_index_transaction_get_uid(t, range->seq1);
-               uid2 = mail_index_transaction_get_uid(t, range->seq2);
-               if (uid2 - uid1 == range->seq2 - range->seq1) {
-                       /* simple conversion */
-                       range->seq1 = uid1;
-                       range->seq2 = uid2;
-               } else {
-                       /* remove expunged UIDs */
-                       new_range = array_insert_space(array, i);
-                       range = array_idx_modifiable(array, i + 1);
-                       count++;
-
-                       memcpy(new_range, range, array->arr.element_size);
-                       new_range->seq1 = uid1;
-                       new_range->seq2 = get_nonexpunged_uid2(t, uid1,
-                                                              range->seq1);
-
-                       /* continue the range without the inserted seqs */
-                       range->seq1 += new_range->seq2 - new_range->seq1 + 1;
+       struct mail_transaction_log_file *file;
+
+       if (t->reset) {
+               /* Reset the whole index, preserving only indexid. Begin by
+                  rotating the log. We don't care if we skip some non-synced
+                  transactions. */
+               if (mail_transaction_log_rotate(t->view->index->log, TRUE) < 0)
+                       return -1;
+
+               if (!MAIL_INDEX_TRANSACTION_HAS_CHANGES(t)) {
+                       /* we only wanted to reset */
+                       return 0;
                }
        }
-}
+       file = t->view->index->log->head;
 
-static void keyword_updates_convert_to_uids(struct mail_index_transaction *t)
-{
-        struct mail_index_transaction_keyword_update *updates;
-       unsigned int i, count;
-
-       if (!array_is_created(&t->keyword_updates))
-               return;
-
-       updates = array_get_modifiable(&t->keyword_updates, &count);
-       for (i = 0; i < count; i++) {
-               mail_index_convert_to_uid_ranges(t, &updates[i].add_seq);
-               mail_index_convert_to_uid_ranges(t, &updates[i].remove_seq);
+       if (!t->view->index->log_locked) {
+               /* update sync_offset */
+               if (mail_transaction_log_file_map(file, file->sync_offset,
+                                                 (uoff_t)-1) <= 0)
+                       return -1;
        }
+       i_assert(file->sync_offset >= file->buffer_offset);
+       ctx->new_highest_modseq = file->sync_highest_modseq;
+       return 1;
 }
 
-void mail_index_transaction_convert_to_uids(struct mail_index_transaction *t)
+static int mail_index_transaction_commit_real(struct mail_index_transaction *t)
 {
-       ARRAY_TYPE(seq_array) *updates;
-       unsigned int i, count;
+       struct mail_transaction_log *log = t->view->index->log;
+       struct mail_transaction_log_append_ctx *ctx;
+       uint32_t log_seq1, log_seq2;
+       uoff_t log_offset1, log_offset2;
+       int ret;
 
-       if (array_is_created(&t->ext_rec_updates)) {
-               updates = array_get_modifiable(&t->ext_rec_updates, &count);
-               for (i = 0; i < count; i++)
-                       mail_index_convert_to_uids(t, (void *)&updates[i]);
-       }
-       if (array_is_created(&t->ext_rec_atomics)) {
-               updates = array_get_modifiable(&t->ext_rec_atomics, &count);
-               for (i = 0; i < count; i++)
-                       mail_index_convert_to_uids(t, (void *)&updates[i]);
+       if (mail_transaction_log_append_begin(t, &ctx) < 0)
+               return -1;
+       ret = mail_transaction_log_file_refresh(t, ctx);
+       if (ret > 0) {
+               ret = mail_index_transaction_finish(t);
+               if (ret == 0)
+                       mail_index_transaction_export(t, ctx);
        }
 
-        keyword_updates_convert_to_uids(t);
-
-       mail_index_convert_to_uid_ranges(t, &t->expunges);
-       mail_index_convert_to_uid_ranges(t, (void *)&t->updates);
-       mail_index_convert_to_uid_ranges(t, &t->keyword_resets);
-}
-
-struct uid_map {
-       uint32_t idx;
-       uint32_t uid;
-};
-
-static int uid_map_cmp(const void *p1, const void *p2)
-{
-       const struct uid_map *m1 = p1, *m2 = p2;
+       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);
 
-       return m1->uid < m2->uid ? -1 :
-               (m1->uid > m2->uid ? 1 : 0);
+       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 void
@@ -390,199 +342,11 @@ mail_index_update_day_headers(struct mail_index_transaction *t)
                hdr.day_first_uid, sizeof(hdr.day_first_uid), FALSE);
 }
 
-static void
-mail_index_transaction_sort_appends_ext(struct mail_index_transaction *t,
-                                       ARRAY_TYPE(seq_array_array) *updates,
-                                       const uint32_t *old_to_newseq_map)
-{
-       ARRAY_TYPE(seq_array) *ext_rec_arrays;
-       ARRAY_TYPE(seq_array) *old_array;
-       ARRAY_TYPE(seq_array) new_array;
-       unsigned int ext_count;
-       const uint32_t *ext_rec;
-       uint32_t seq;
-       unsigned int i, j, count;
-
-       if (!array_is_created(updates))
-               return;
-
-       ext_rec_arrays = array_get_modifiable(updates, &count);
-       for (j = 0; j < count; j++) {
-               old_array = &ext_rec_arrays[j];
-               if (!array_is_created(old_array))
-                       continue;
-
-               ext_count = array_count(old_array);
-               array_create(&new_array, default_pool,
-                            old_array->arr.element_size, ext_count);
-               for (i = 0; i < ext_count; i++) {
-                       ext_rec = array_idx(old_array, i);
-
-                       seq = *ext_rec < t->first_new_seq ? *ext_rec :
-                               old_to_newseq_map[*ext_rec - t->first_new_seq];
-                       mail_index_seq_array_add(&new_array, seq, ext_rec+1,
-                                                old_array->arr.element_size -
-                                                sizeof(*ext_rec), NULL);
-               }
-               array_free(old_array);
-               ext_rec_arrays[j] = new_array;
-       }
-}
-
-static void
-sort_appends_seq_range(struct mail_index_transaction *t,
-                      ARRAY_TYPE(seq_range) *array,
-                      const uint32_t *old_to_newseq_map)
-{
-       struct seq_range *range, temp_range;
-       ARRAY_TYPE(seq_range) old_seqs;
-       uint32_t idx, idx1, idx2;
-       unsigned int i, count;
-
-       range = array_get_modifiable(array, &count);
-       for (i = 0; i < count; i++) {
-               if (range[i].seq2 >= t->first_new_seq)
-                       break;
-       }
-       if (i == count) {
-               /* nothing to do */
-               return;
-       }
-
-       i_array_init(&old_seqs, count - i);
-       if (range[i].seq1 < t->first_new_seq) {
-               temp_range.seq1 = t->first_new_seq;
-               temp_range.seq2 = range[i].seq2;
-               array_append(&old_seqs, &temp_range, 1);
-               range[i].seq2 = t->first_new_seq - 1;
-               i++;
-       }
-       array_append(&old_seqs, &range[i], count - i);
-       array_delete(array, i, count - i);
-
-       range = array_get_modifiable(&old_seqs, &count);
-       for (i = 0; i < count; i++) {
-               idx1 = range[i].seq1 - t->first_new_seq;
-               idx2 = range[i].seq2 - t->first_new_seq;
-               for (idx = idx1; idx <= idx2; idx++)
-                       seq_range_array_add(array, 0, old_to_newseq_map[idx]);
-       }
-       array_free(&old_seqs);
-}
-
-static void
-mail_index_transaction_sort_appends_keywords(struct mail_index_transaction *t,
-                                            const uint32_t *old_to_newseq_map)
-{
-       struct mail_index_transaction_keyword_update *updates;
-       unsigned int i, count;
-
-       if (array_is_created(&t->keyword_updates)) {
-               updates = array_get_modifiable(&t->keyword_updates, &count);
-               for (i = 0; i < count; i++) {
-                       if (array_is_created(&updates[i].add_seq)) {
-                               sort_appends_seq_range(t, &updates[i].add_seq,
-                                                      old_to_newseq_map);
-                       }
-                       if (array_is_created(&updates[i].remove_seq)) {
-                               sort_appends_seq_range(t,
-                                                      &updates[i].remove_seq,
-                                                      old_to_newseq_map);
-                       }
-               }
-       }
-
-       if (array_is_created(&t->keyword_resets)) {
-               sort_appends_seq_range(t, &t->keyword_resets,
-                                      old_to_newseq_map);
-       }
-}
-
-void mail_index_transaction_sort_appends(struct mail_index_transaction *t)
-{
-       struct mail_index_record *recs, *sorted_recs;
-       struct uid_map *new_uid_map;
-       uint32_t *old_to_newseq_map;
-       unsigned int i, count;
-
-       if (!t->appends_nonsorted)
-               return;
-
-       /* first make a copy of the UIDs and map them to sequences */
-       recs = array_get_modifiable(&t->appends, &count);
-       i_assert(count > 0);
-
-       new_uid_map = i_new(struct uid_map, count);
-       for (i = 0; i < count; i++) {
-               new_uid_map[i].idx = i;
-               new_uid_map[i].uid = recs[i].uid;
-       }
-
-       /* now sort the UID map */
-       qsort(new_uid_map, count, sizeof(*new_uid_map), uid_map_cmp);
-
-       /* sort mail records */
-       sorted_recs = i_new(struct mail_index_record, count);
-       sorted_recs[0] = recs[new_uid_map[0].idx];
-       for (i = 1; i < count; i++) {
-               sorted_recs[i] = recs[new_uid_map[i].idx];
-               if (sorted_recs[i].uid == sorted_recs[i-1].uid)
-                       i_panic("Duplicate UIDs added in transaction");
-       }
-       buffer_write(t->appends.arr.buffer, 0, sorted_recs,
-                    sizeof(*sorted_recs) * count);
-       i_free(sorted_recs);
-
-       old_to_newseq_map = i_new(uint32_t, count);
-       for (i = 0; i < count; i++)
-               old_to_newseq_map[new_uid_map[i].idx] = i + t->first_new_seq;
-       i_free(new_uid_map);
-
-       mail_index_transaction_sort_appends_ext(t, &t->ext_rec_updates,
-                                               old_to_newseq_map);
-       mail_index_transaction_sort_appends_ext(t, &t->ext_rec_atomics,
-                                               old_to_newseq_map);
-       mail_index_transaction_sort_appends_keywords(t, old_to_newseq_map);
-       i_free(old_to_newseq_map);
-
-       t->appends_nonsorted = FALSE;
-}
-
-uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t)
-{
-       const struct mail_index_header *head_hdr, *hdr;
-       unsigned int offset;
-       uint32_t next_uid;
-
-       head_hdr = &t->view->index->map->hdr;
-       hdr = &t->view->map->hdr;
-       next_uid = t->reset || head_hdr->uid_validity != hdr->uid_validity ?
-               1 : hdr->next_uid;
-       if (array_is_created(&t->appends) && t->highest_append_uid != 0) {
-               /* get next_uid from appends if they have UIDs */
-               i_assert(next_uid <= t->highest_append_uid);
-               next_uid = t->highest_append_uid + 1;
-       }
-
-       /* see if it's been updated in pre/post header changes */
-       offset = offsetof(struct mail_index_header, next_uid);
-       if (t->post_hdr_mask[offset] != 0) {
-               hdr = (const void *)t->post_hdr_change;
-               if (hdr->next_uid > next_uid)
-                       next_uid = hdr->next_uid;
-       }
-       if (t->pre_hdr_mask[offset] != 0) {
-               hdr = (const void *)t->pre_hdr_change;
-               if (hdr->next_uid > next_uid)
-                       next_uid = hdr->next_uid;
-       }
-       return next_uid;
-}
-
 static int mail_index_transaction_commit_v(struct mail_index_transaction *t,
                                           uint32_t *log_file_seq_r,
                                           uoff_t *log_file_offset_r)
 {
+       struct mail_index *index = t->view->index;
        int ret;
 
        i_assert(t->first_new_seq >
@@ -591,14 +355,19 @@ static int mail_index_transaction_commit_v(struct mail_index_transaction *t,
        if (t->cache_trans_ctx != NULL)
                mail_cache_transaction_commit(&t->cache_trans_ctx);
 
-       if (array_is_created(&t->appends)) {
-               mail_index_transaction_sort_appends(t);
+       if (array_is_created(&t->appends))
                mail_index_update_day_headers(t);
-       }
 
-       ret = mail_transaction_log_append(t, log_file_seq_r, log_file_offset_r);
+       if (!MAIL_INDEX_TRANSACTION_HAS_CHANGES(t) && !t->reset) {
+               /* nothing to append */
+               ret = 0;
+       } else {
+               ret = mail_index_transaction_commit_real(t);
+       }
+       mail_transaction_log_get_head(index->log, log_file_seq_r,
+                                     log_file_offset_r);
 
-       if (ret == 0 && !t->view->index->syncing) {
+       if (ret == 0 && !index->syncing) {
                /* 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
@@ -608,7 +377,7 @@ static int mail_index_transaction_commit_v(struct mail_index_transaction *t,
                   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). */
-               (void)mail_index_refresh(t->view->index);
+               (void)mail_index_refresh(index);
        }
 
        mail_index_transaction_unref(&t);
@@ -782,7 +551,7 @@ mail_index_expunge_last_append(struct mail_index_transaction *t, uint32_t seq)
                t->appends_nonsorted = FALSE;
                array_free(&t->appends);
        }
-       t->log_updates = mail_index_transaction_has_changes(t);
+       mail_index_transaction_set_log_updates(t);
 }
 
 void mail_index_expunge(struct mail_index_transaction *t, uint32_t seq)
@@ -1209,7 +978,7 @@ mail_index_transaction_has_ext_changes(struct mail_index_transaction *t)
        if (array_is_created(&t->ext_hdr_updates)) {
                const struct mail_index_transaction_ext_hdr_update *hdr;
 
-               hdr =\v array_get(&t->ext_hdr_updates, &count);
+               hdr = array_get(&t->ext_hdr_updates, &count);
                for (i = 0; i < count; i++) {
                        if (hdr[i].alloc_size > 0)
                                return TRUE;
@@ -1603,97 +1372,6 @@ void mail_index_transaction_set_max_modseq(struct mail_index_transaction *t,
        t->conflict_seqs = seqs;
 }
 
-static bool
-mail_index_update_cancel_array(ARRAY_TYPE(seq_range) *array, uint32_t seq)
-{
-       if (array_is_created(array)) {
-               if (seq_range_array_remove(array, seq)) {
-                       if (array_count(array) == 0)
-                               array_free(array);
-                       return TRUE;
-               }
-       }
-       return FALSE;
-}
-
-static bool
-mail_index_update_cancel(struct mail_index_transaction *t, uint32_t seq)
-{
-       struct mail_index_transaction_keyword_update *kw;
-       struct mail_transaction_flag_update *updates, tmp_update;
-       unsigned int i, count;
-       bool ret, have_kw_changes = FALSE;
-
-       ret = mail_index_update_cancel_array(&t->keyword_resets, seq);
-       if (array_is_created(&t->keyword_updates)) {
-               kw = array_get_modifiable(&t->keyword_updates, &count);
-               for (i = 0; i < count; i++) {
-                       if (mail_index_update_cancel_array(&kw[i].add_seq, seq))
-                               ret = TRUE;
-                       if (mail_index_update_cancel_array(&kw[i].remove_seq,
-                                                          seq))
-                               ret = TRUE;
-                       if (array_is_created(&kw[i].add_seq) ||
-                           array_is_created(&kw[i].remove_seq))
-                               have_kw_changes = TRUE;
-               }
-               if (!have_kw_changes)
-                       array_free(&t->keyword_updates);
-       }
-
-       if (!array_is_created(&t->updates))
-               return ret;
-
-       updates = array_get_modifiable(&t->updates, &count);
-       i = mail_index_transaction_get_flag_update_pos(t, 0, count, seq);
-       if (i < count && updates[i].uid1 <= seq && updates[i].uid2 >= seq) {
-               /* exists */
-               ret = TRUE;
-               if (updates[i].uid1 == seq && updates[i].uid2 == seq) {
-                       if (count > 1)
-                               array_delete(&t->updates, i, 1);
-                       else
-                               array_free(&t->updates);
-               } else if (updates[i].uid1 == seq)
-                       updates[i].uid1++;
-               else if (updates[i].uid2 == seq)
-                       updates[i].uid2--;
-               else {
-                       /* need to split it in two */
-                       tmp_update = updates[i];
-                       tmp_update.uid1 = seq+1;
-                       updates[i].uid2 = seq-1;
-                       array_insert(&t->updates, i + 1, &tmp_update, 1);
-               }
-       }
-       return ret;
-}
-
-void mail_index_transaction_check_conflicts(struct mail_index_transaction *t)
-{
-       uint32_t seq;
-
-       i_assert(t->max_modseq != 0);
-       i_assert(t->conflict_seqs != NULL);
-
-       if (t->max_modseq == mail_index_modseq_get_highest(t->view)) {
-               /* no conflicts possible */
-               return;
-       }
-       if (t->min_flagupdate_seq == 0) {
-               /* no flag updates */
-               return;
-       }
-
-       for (seq = t->min_flagupdate_seq; seq <= t->max_flagupdate_seq; seq++) {
-               if (mail_index_modseq_lookup(t->view, seq) > t->max_modseq) {
-                       if (mail_index_update_cancel(t, seq))
-                               seq_range_array_add(t->conflict_seqs, 0, seq);
-               }
-       }
-       t->log_updates = mail_index_transaction_has_changes(t);
-}
-
 static struct mail_index_transaction_vfuncs trans_vfuncs = {
        mail_index_transaction_commit_v,
        mail_index_transaction_rollback_v
index 240675c54719cbb4756fd858adeacc94c97efbdb..ea6c61962a1fff66cf73212ddc02decff3852464 100644 (file)
@@ -2,75 +2,14 @@
 
 #include "lib.h"
 #include "array.h"
-#include "buffer.h"
 #include "write-full.h"
 #include "mail-index-private.h"
-#include "mail-index-view-private.h"
-#include "mail-index-modseq.h"
-#include "mail-index-transaction-private.h"
 #include "mail-transaction-log-private.h"
 
-struct log_append_context {
-       struct mail_transaction_log_file *file;
-       struct mail_index_transaction *trans;
-       buffer_t *output;
-
-       uint64_t modseq;
-       uint32_t first_append_size;
-       bool sync_includes_this;
-};
-
-static void log_append_buffer(struct log_append_context *ctx,
-                             const buffer_t *buf, const buffer_t *hdr_buf,
-                             enum mail_transaction_type type)
-{
-       struct mail_transaction_header hdr;
-       uint32_t hdr_size;
-       size_t hdr_pos;
-
-       i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0);
-       i_assert((buf->used % 4) == 0);
-       i_assert(hdr_buf == NULL || (hdr_buf->used % 4) == 0);
-
-       if (buf->used == 0)
-               return;
-
-       memset(&hdr, 0, sizeof(hdr));
-       hdr.type = type;
-       if (type == MAIL_TRANSACTION_EXPUNGE)
-               hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT;
-       if ((ctx->trans->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0)
-               hdr.type |= MAIL_TRANSACTION_EXTERNAL;
-       hdr.size = sizeof(hdr) + buf->used +
-               (hdr_buf == NULL ? 0 : hdr_buf->used);
-
-       hdr_pos = ctx->output->used;
-       buffer_append(ctx->output, &hdr, sizeof(hdr));
-       if (hdr_buf != NULL)
-               buffer_append(ctx->output, hdr_buf->data, hdr_buf->used);
-       buffer_append(ctx->output, buf->data, buf->used);
-
-       if (mail_transaction_header_has_modseq(buf->data,
-                       CONST_PTR_OFFSET(buf->data, sizeof(hdr)), ctx->modseq))
-               ctx->modseq++;
-
-       /* update the size */
-       hdr_size = mail_index_uint32_to_offset(hdr.size);
-       if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(ctx->file) &&
-           ctx->first_append_size == 0) {
-               /* size will be written later once everything
-                  is in disk */
-               ctx->first_append_size = hdr_size;
-               hdr.size = 0;
-       } else {
-               hdr.size = hdr_size;
-       }
-       buffer_write(ctx->output, hdr_pos, &hdr, sizeof(hdr));
-}
-
-static int log_buffer_move_to_memory(struct log_append_context *ctx)
+static int
+log_buffer_move_to_memory(struct mail_transaction_log_append_ctx *ctx)
 {
-       struct mail_transaction_log_file *file = ctx->file;
+       struct mail_transaction_log_file *file = ctx->log->head;
 
        /* first we need to truncate this latest write so that log syncing
           doesn't break */
@@ -87,15 +26,15 @@ static int log_buffer_move_to_memory(struct log_append_context *ctx)
        i_assert(file->buffer_offset + file->buffer->used ==
                 file->sync_offset);
        buffer_append_buf(file->buffer, ctx->output, 0, (size_t)-1);
-       buffer_write(file->buffer, file->sync_offset - file->buffer_offset,
-                    &ctx->first_append_size, sizeof(uint32_t));
        file->sync_offset = file->buffer_offset + file->buffer->used;
        return 0;
 }
 
-static int log_buffer_write(struct log_append_context *ctx, bool want_fsync)
+static int log_buffer_write(struct mail_transaction_log_append_ctx *ctx)
 {
-       struct mail_transaction_log_file *file = ctx->file;
+       struct mail_transaction_log_file *file = ctx->log->head;
+       struct mail_transaction_header *hdr;
+       uint32_t first_size;
 
        if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
                if (file->buffer == NULL) {
@@ -107,10 +46,16 @@ static int log_buffer_write(struct log_append_context *ctx, bool want_fsync)
                return 0;
        }
 
-       i_assert(ctx->first_append_size != 0);
+       /* size will be written later once everything is in disk */
+       hdr = buffer_get_space_unsafe(ctx->output, 0, sizeof(*hdr));
+       first_size = hdr->size;
+       i_assert(first_size != 0);
+       hdr->size = 0;
+
        if (pwrite_full(file->fd, ctx->output->data, ctx->output->used,
                        file->sync_offset) < 0) {
                /* write failure, fallback to in-memory indexes. */
+               hdr->size = first_size;
                mail_index_file_set_syscall_error(file->log->index,
                                                  file->filepath,
                                                  "pwrite_full()");
@@ -123,15 +68,16 @@ static int log_buffer_write(struct log_append_context *ctx, bool want_fsync)
 
        /* now that the whole transaction has been written, rewrite the first
           record's size so the transaction becomes visible */
-       if (pwrite_full(file->fd, &ctx->first_append_size,
-                       sizeof(uint32_t), file->sync_offset) < 0) {
+       if (pwrite_full(file->fd, &first_size, sizeof(uint32_t),
+                       file->sync_offset +
+                       offsetof(struct mail_transaction_header, size)) < 0) {
                mail_index_file_set_syscall_error(file->log->index,
                                                  file->filepath,
                                                  "pwrite_full()");
                return log_buffer_move_to_memory(ctx);
        }
 
-       if ((want_fsync && !file->log->index->fsync_disable) ||
+       if ((ctx->want_fsync && !file->log->index->fsync_disable) ||
            file->log->index->nfs_flush) {
                if (fdatasync(file->fd) < 0) {
                        mail_index_file_set_syscall_error(file->log->index,
@@ -150,531 +96,44 @@ static int log_buffer_write(struct log_append_context *ctx, bool want_fsync)
        return 0;
 }
 
-static const buffer_t *
-log_get_hdr_update_buffer(struct mail_index_transaction *t, bool prepend)
-{
-       buffer_t *buf;
-       const unsigned char *data, *mask;
-       struct mail_transaction_header_update u;
-       uint16_t offset;
-       int state = 0;
-
-       memset(&u, 0, sizeof(u));
-
-       data = prepend ? t->pre_hdr_change : t->post_hdr_change;
-       mask = prepend ? t->pre_hdr_mask : t->post_hdr_mask;
-
-       buf = buffer_create_dynamic(pool_datastack_create(), 256);
-       for (offset = 0; offset <= sizeof(t->pre_hdr_change); offset++) {
-               if (offset < sizeof(t->pre_hdr_change) && mask[offset]) {
-                       if (state == 0) {
-                               u.offset = offset;
-                               state++;
-                       }
-               } else {
-                       if (state > 0) {
-                               u.size = offset - u.offset;
-                               buffer_append(buf, &u, sizeof(u));
-                               buffer_append(buf, data + u.offset, u.size);
-                               state = 0;
-                       }
-               }
-       }
-       return buf;
-}
-
-static void
-ext_reset_update_atomic(struct mail_index_transaction *t,
-                       uint32_t ext_id, uint32_t expected_reset_id)
-{
-       const struct mail_index_ext *map_ext;
-       struct mail_transaction_ext_reset *reset;
-       uint32_t idx, reset_id;
-
-       if (!mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
-               /* new extension */
-               reset_id = 1;
-       } else {
-               map_ext = array_idx(&t->view->index->map->extensions, idx);
-               reset_id = map_ext->reset_id + 1;
-       }
-       if (reset_id != expected_reset_id) {
-               /* ignore this extension update */
-               mail_index_ext_set_reset_id(t, ext_id, 0);
-               return;
-       }
-
-       if (reset_id == 0)
-               reset_id++;
-
-       array_idx_set(&t->ext_reset_ids, ext_id, &reset_id);
-
-       /* reseting existing data is optional */
-       if (array_is_created(&t->ext_resets)) {
-               reset = array_idx_modifiable(&t->ext_resets, ext_id);
-               if (reset->new_reset_id == (uint32_t)-1)
-                       reset->new_reset_id = reset_id;
-       }
-}
-
-static void
-transaction_update_atomic_reset_ids(struct mail_index_transaction *t)
-{
-       const uint32_t *expected_reset_ids;
-       unsigned int ext_id, count;
-
-       if (!array_is_created(&t->ext_reset_atomic))
-               return;
-
-       expected_reset_ids = array_get(&t->ext_reset_atomic, &count);
-       for (ext_id = 0; ext_id < count; ext_id++) {
-               if (expected_reset_ids[ext_id] != 0) {
-                       ext_reset_update_atomic(t, ext_id,
-                                               expected_reset_ids[ext_id]);
-               }
-       }
-}
-
-static void log_append_ext_intro(struct log_append_context *ctx,
-                                uint32_t ext_id, uint32_t reset_id)
-{
-       struct mail_index_transaction *t = ctx->trans;
-       const struct mail_index_registered_ext *rext;
-        struct mail_transaction_ext_intro *intro, *resizes;
-       buffer_t *buf;
-       uint32_t idx;
-       unsigned int count;
-
-       i_assert(ext_id != (uint32_t)-1);
-
-       if (t->reset ||
-           !mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
-               /* new extension */
-               idx = (uint32_t)-1;
-       }
-
-       rext = array_idx(&t->view->index->extensions, ext_id);
-       if (!array_is_created(&t->ext_resizes)) {
-               resizes = NULL;
-               count = 0;
-       } else {
-               resizes = array_get_modifiable(&t->ext_resizes, &count);
-       }
-
-       buf = buffer_create_dynamic(pool_datastack_create(), 128);
-       if (ext_id < count && resizes[ext_id].name_size != 0) {
-               /* we're resizing the extension. use the resize struct. */
-               intro = &resizes[ext_id];
-
-               i_assert(intro->ext_id == idx);
-               intro->name_size = idx != (uint32_t)-1 ? 0 :
-                       strlen(rext->name);
-               buffer_append(buf, intro, sizeof(*intro));
-       } else {
-               /* generate a new intro structure */
-               intro = buffer_append_space_unsafe(buf, sizeof(*intro));
-               intro->ext_id = idx;
-               intro->hdr_size = rext->hdr_size;
-               intro->record_size = rext->record_size;
-               intro->record_align = rext->record_align;
-               intro->flags = MAIL_TRANSACTION_EXT_INTRO_FLAG_NO_SHRINK;
-               intro->name_size = idx != (uint32_t)-1 ? 0 :
-                       strlen(rext->name);
-       }
-       if (reset_id != 0) {
-               /* we're going to reset this extension in this transaction */
-               intro->reset_id = reset_id;
-       } else if (idx != (uint32_t)-1) {
-               /* use the existing reset_id */
-               const struct mail_index_ext *map_ext =
-                       array_idx(&t->view->index->map->extensions, idx);
-               intro->reset_id = map_ext->reset_id;
-       } else {
-               /* new extension, reset_id defaults to 0 */
-       }
-       buffer_append(buf, rext->name, intro->name_size);
-       if ((buf->used % 4) != 0)
-               buffer_append_zero(buf, 4 - (buf->used % 4));
-
-       if (ctx->file->sync_highest_modseq == 0 &&
-           strcmp(rext->name, MAIL_INDEX_MODSEQ_EXT_NAME) == 0) {
-               /* modseq tracking started */
-               ctx->file->sync_highest_modseq = 1;
-       }
-
-       log_append_buffer(ctx, buf, NULL, MAIL_TRANSACTION_EXT_INTRO);
-}
-
-static void
-log_append_ext_hdr_update(struct log_append_context *ctx,
-                       const struct mail_index_transaction_ext_hdr_update *hdr)
-{
-       buffer_t *buf;
-       const unsigned char *data, *mask;
-       struct mail_transaction_ext_hdr_update u;
-       uint16_t offset;
-       bool started = FALSE;
-
-       memset(&u, 0, sizeof(u));
-
-       data = hdr->data;
-       mask = hdr->mask;
-
-       buf = buffer_create_dynamic(pool_datastack_create(), 256);
-       for (offset = 0; offset <= hdr->alloc_size; offset++) {
-               if (offset < hdr->alloc_size && mask[offset] != 0) {
-                       if (!started) {
-                               u.offset = offset;
-                               started = TRUE;
-                       }
-               } else {
-                       if (started) {
-                               u.size = offset - u.offset;
-                               buffer_append(buf, &u, sizeof(u));
-                               buffer_append(buf, data + u.offset, u.size);
-                               started = FALSE;
-                       }
-               }
-       }
-       if (buf->used % 4 != 0)
-               buffer_append_zero(buf, 4 - buf->used % 4);
-       log_append_buffer(ctx, buf, NULL, MAIL_TRANSACTION_EXT_HDR_UPDATE);
-}
-
-static void
-mail_transaction_log_append_ext_intros(struct log_append_context *ctx)
-{
-       struct mail_index_transaction *t = ctx->trans;
-        const struct mail_transaction_ext_intro *resize;
-       const struct mail_index_transaction_ext_hdr_update *hdrs;
-       struct mail_transaction_ext_reset ext_reset;
-       unsigned int resize_count, ext_count = 0;
-       unsigned int hdrs_count, reset_id_count, reset_count;
-       uint32_t ext_id, reset_id;
-       const struct mail_transaction_ext_reset *reset;
-       const uint32_t *reset_ids;
-       buffer_t *reset_buf;
-
-       if (!array_is_created(&t->ext_resizes)) {
-               resize = NULL;
-               resize_count = 0;
-       } else {
-               resize = array_get(&t->ext_resizes, &resize_count);
-               if (ext_count < resize_count)
-                       ext_count = resize_count;
-       }
-
-       if (!array_is_created(&t->ext_reset_ids)) {
-               reset_ids = NULL;
-               reset_id_count = 0;
-       } else {
-               reset_ids = array_get(&t->ext_reset_ids, &reset_id_count);
-       }
-
-       if (!array_is_created(&t->ext_resets)) {
-               reset = NULL;
-               reset_count = 0;
-       } else {
-               reset = array_get(&t->ext_resets, &reset_count);
-               if (ext_count < reset_count)
-                       ext_count = reset_count;
-       }
-
-       if (!array_is_created(&t->ext_hdr_updates)) {
-               hdrs = NULL;
-               hdrs_count = 0;
-       } else {
-               hdrs = array_get(&t->ext_hdr_updates, &hdrs_count);
-               if (ext_count < hdrs_count)
-                       ext_count = hdrs_count;
-       }
-
-       memset(&ext_reset, 0, sizeof(ext_reset));
-       reset_buf = buffer_create_data(pool_datastack_create(),
-                                      &ext_reset, sizeof(ext_reset));
-       buffer_set_used_size(reset_buf, sizeof(ext_reset));
-
-       for (ext_id = 0; ext_id < ext_count; ext_id++) {
-               if (ext_id < reset_count)
-                       ext_reset = reset[ext_id];
-               else
-                       ext_reset.new_reset_id = 0;
-               if ((ext_id < resize_count && resize[ext_id].name_size) ||
-                   ext_reset.new_reset_id != 0 ||
-                   (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0)) {
-                       if (ext_reset.new_reset_id != 0) {
-                               /* we're going to reset this extension
-                                  immediately after the intro */
-                               reset_id = 0;
-                       } else {
-                               reset_id = ext_id < reset_id_count ?
-                                       reset_ids[ext_id] : 0;
-                       }
-                       log_append_ext_intro(ctx, ext_id, reset_id);
-               }
-               if (ext_reset.new_reset_id != 0) {
-                       i_assert(ext_id < reset_id_count &&
-                                ext_reset.new_reset_id == reset_ids[ext_id]);
-                       log_append_buffer(ctx, reset_buf, NULL,
-                                         MAIL_TRANSACTION_EXT_RESET);
-               }
-               if (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0) {
-                       T_BEGIN {
-                               log_append_ext_hdr_update(ctx, &hdrs[ext_id]);
-                       } T_END;
-               }
-       }
-}
-
-static void log_append_ext_recs(struct log_append_context *ctx,
-                               const ARRAY_TYPE(seq_array_array) *arr,
-                               enum mail_transaction_type type)
-{
-       struct mail_index_transaction *t = ctx->trans;
-       const ARRAY_TYPE(seq_array) *updates;
-       const uint32_t *reset_ids;
-       unsigned int ext_id, count, reset_id_count;
-       uint32_t reset_id;
-
-       if (!array_is_created(&t->ext_reset_ids)) {
-               reset_ids = NULL;
-               reset_id_count = 0;
-       } else {
-               reset_ids = array_get_modifiable(&t->ext_reset_ids,
-                                                &reset_id_count);
-       }
-
-       updates = array_get(arr, &count);
-       for (ext_id = 0; ext_id < count; ext_id++) {
-               if (!array_is_created(&updates[ext_id]))
-                       continue;
-
-               reset_id = ext_id < reset_id_count ? reset_ids[ext_id] : 0;
-               log_append_ext_intro(ctx, ext_id, reset_id);
-
-               log_append_buffer(ctx, updates[ext_id].arr.buffer, NULL, type);
-       }
-}
-
 static void
-log_append_keyword_update(struct log_append_context *ctx,
-                         buffer_t *hdr_buf, enum modify_type modify_type,
-                         const char *keyword, const buffer_t *buffer)
-{
-       struct mail_transaction_keyword_update kt_hdr;
-
-       memset(&kt_hdr, 0, sizeof(kt_hdr));
-       kt_hdr.modify_type = modify_type;
-       kt_hdr.name_size = strlen(keyword);
-
-       buffer_set_used_size(hdr_buf, 0);
-       buffer_append(hdr_buf, &kt_hdr, sizeof(kt_hdr));
-       buffer_append(hdr_buf, keyword, kt_hdr.name_size);
-       if ((hdr_buf->used % 4) != 0)
-               buffer_append_zero(hdr_buf, 4 - (hdr_buf->used % 4));
-
-       log_append_buffer(ctx, buffer, hdr_buf,
-                         MAIL_TRANSACTION_KEYWORD_UPDATE);
-}
-
-static enum mail_index_sync_type
-log_append_keyword_updates(struct log_append_context *ctx)
-{
-        const struct mail_index_transaction_keyword_update *updates;
-       const char *const *keywords;
-       buffer_t *hdr_buf;
-       enum mail_index_sync_type change_mask = 0;
-       unsigned int i, count, keywords_count;
-
-       hdr_buf = buffer_create_dynamic(pool_datastack_create(), 64);
-
-       keywords = array_get_modifiable(&ctx->trans->view->index->keywords,
-                                       &keywords_count);
-       updates = array_get_modifiable(&ctx->trans->keyword_updates, &count);
-       i_assert(count <= keywords_count);
-
-       for (i = 0; i < count; i++) {
-               if (array_is_created(&updates[i].add_seq)) {
-                       change_mask |= MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD;
-                       log_append_keyword_update(ctx, hdr_buf,
-                                       MODIFY_ADD, keywords[i],
-                                       updates[i].add_seq.arr.buffer);
-               }
-               if (array_is_created(&updates[i].remove_seq)) {
-                       change_mask |= MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE;
-                       log_append_keyword_update(ctx, hdr_buf,
-                                       MODIFY_REMOVE, keywords[i],
-                                       updates[i].remove_seq.arr.buffer);
-               }
-       }
-       return change_mask;
-}
-
-static void log_append_sync_offset_if_needed(struct log_append_context *ctx)
+log_append_sync_offset_if_needed(struct mail_transaction_log_append_ctx *ctx)
 {
+       struct mail_transaction_log_file *file = ctx->log->head;
        struct mail_transaction_header_update *u;
-       buffer_t *buf;
+       struct mail_transaction_header *hdr;
        uint32_t offset;
 
-       /* Update the tail offsets only when committing the sync transaction.
-          Other transactions may not know the latest tail offset and might
-          end up shrinking it. (Alternatively the shrinking tail offsets could
-          just be ignored, which would probably work fine too.) */
-       if (!ctx->trans->sync_transaction)
-               return;
-
-       if (ctx->file->max_tail_offset == ctx->file->sync_offset) {
+       if (file->max_tail_offset == file->sync_offset) {
                /* FIXME: when we remove exclusive log locking, we
                   can't rely on this. then write non-changed offset + check
                   real offset + rewrite the new offset if other transactions
                   weren't written in the middle */
-               ctx->file->max_tail_offset += ctx->output->used +
-                       sizeof(struct mail_transaction_header) +
-                       sizeof(*u) + sizeof(offset);
+               file->max_tail_offset += ctx->output->used +
+                       sizeof(*hdr) + sizeof(*u) + sizeof(offset);
                ctx->sync_includes_this = TRUE;
        }
-       offset = ctx->file->max_tail_offset;
+       offset = file->max_tail_offset;
 
-       if (ctx->file->saved_tail_offset == offset)
+       if (file->saved_tail_offset == offset)
                return;
-       i_assert(offset > ctx->file->saved_tail_offset);
+       i_assert(offset > file->saved_tail_offset);
+
+       hdr = buffer_append_space_unsafe(ctx->output, sizeof(*hdr));
+       hdr->type = MAIL_TRANSACTION_HEADER_UPDATE | MAIL_TRANSACTION_EXTERNAL;
+       hdr->size = sizeof(*hdr) + sizeof(*u) + sizeof(uint32_t);
+       hdr->size = mail_index_uint32_to_offset(hdr->size);
 
-       buf = buffer_create_static_hard(pool_datastack_create(),
-                                       sizeof(*u) + sizeof(offset));
-       u = buffer_append_space_unsafe(buf, sizeof(*u));
+       u = buffer_append_space_unsafe(ctx->output, sizeof(*u));
        u->offset = offsetof(struct mail_index_header, log_file_tail_offset);
        u->size = sizeof(offset);
-       buffer_append(buf, &offset, sizeof(offset));
-
-       log_append_buffer(ctx, buf, NULL, MAIL_TRANSACTION_HEADER_UPDATE);
+       buffer_append(ctx->output, &offset, sizeof(offset));
 }
 
-#define TRANSACTION_HAS_CHANGES(t) \
-       ((t)->log_updates || (t)->log_ext_updates || \
-        (array_is_created(&(t)->updates) && array_count(&(t)->updates) > 0))
-
 static int
-mail_transaction_log_append_locked(struct mail_index_transaction *t,
-                                  uint32_t *log_file_seq_r,
-                                  uoff_t *log_file_offset_r)
+mail_transaction_log_append_locked(struct mail_transaction_log_append_ctx *ctx)
 {
-       enum mail_index_sync_type change_mask = 0;
-       struct mail_index_view *view = t->view;
-       struct mail_index *index;
-       struct mail_transaction_log *log;
-       struct mail_transaction_log_file *file;
-       struct log_append_context ctx;
-       uoff_t append_offset;
-       bool want_fsync;
-
-       index = mail_index_view_get_index(view);
-       log = index->log;
-
-       if (t->reset) {
-               /* Reset the whole index, preserving only indexid. Begin by
-                  rotating the log. We don't care if we skip some non-synced
-                  transactions. */
-               if (mail_transaction_log_rotate(log, TRUE) < 0)
-                       return -1;
-
-               if (!TRANSACTION_HAS_CHANGES(t)) {
-                       /* we only wanted to reset */
-                       return 0;
-               }
-       }
-
-       if (!index->log_locked) {
-               /* update sync_offset */
-               if (mail_transaction_log_file_map(log->head,
-                                                 log->head->sync_offset,
-                                                 (uoff_t)-1) <= 0)
-                       return -1;
-       }
-
-       if (array_is_created(&t->ext_reset_atomic) || t->max_modseq != 0) {
-               if (mail_index_map(t->view->index,
-                                  MAIL_INDEX_SYNC_HANDLER_HEAD) <= 0)
-                       return -1;
-       }
-       if (array_is_created(&t->ext_reset_atomic))
-               transaction_update_atomic_reset_ids(t);
-       if (t->max_modseq != 0)
-               mail_index_transaction_check_conflicts(t);
-       if (!TRANSACTION_HAS_CHANGES(t)) {
-               /* we aborted all changes, nothing else to do */
-               return 0;
-       }
-       /* finally convert all sequences to UIDs before we write them,
-          but after we've checked and removed conflicts */
-       mail_index_transaction_convert_to_uids(t);
-
-       file = log->head;
-
-       i_assert(file->sync_offset >= file->buffer_offset);
-
-       memset(&ctx, 0, sizeof(ctx));
-       ctx.file = file;
-       ctx.trans = t;
-       ctx.output = buffer_create_dynamic(default_pool, 1024);
-       ctx.modseq = file->sync_highest_modseq;
-
-       /* send all extension introductions and resizes before appends
-          to avoid resize overhead as much as possible */
-        mail_transaction_log_append_ext_intros(&ctx);
-
-       if (t->pre_hdr_changed) {
-               log_append_buffer(&ctx,
-                                 log_get_hdr_update_buffer(t, TRUE),
-                                 NULL, MAIL_TRANSACTION_HEADER_UPDATE);
-       }
-       if (array_is_created(&t->appends)) {
-               change_mask |= MAIL_INDEX_SYNC_TYPE_APPEND;
-               log_append_buffer(&ctx, t->appends.arr.buffer, NULL,
-                                 MAIL_TRANSACTION_APPEND);
-       }
-       if (array_is_created(&t->updates)) {
-               change_mask |= MAIL_INDEX_SYNC_TYPE_FLAGS;
-               log_append_buffer(&ctx, t->updates.arr.buffer, NULL,
-                                 MAIL_TRANSACTION_FLAG_UPDATE);
-       }
-
-       if (array_is_created(&t->ext_rec_updates)) {
-               log_append_ext_recs(&ctx, &t->ext_rec_updates,
-                                   MAIL_TRANSACTION_EXT_REC_UPDATE);
-       }
-       if (array_is_created(&t->ext_rec_atomics)) {
-               log_append_ext_recs(&ctx, &t->ext_rec_atomics,
-                                   MAIL_TRANSACTION_EXT_ATOMIC_INC);
-       }
-
-       /* keyword resets before updates */
-       if (array_is_created(&t->keyword_resets)) {
-               change_mask |= MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET;
-               log_append_buffer(&ctx, t->keyword_resets.arr.buffer,
-                                 NULL, MAIL_TRANSACTION_KEYWORD_RESET);
-       }
-       if (array_is_created(&t->keyword_updates))
-               change_mask |= log_append_keyword_updates(&ctx);
-
-       if (array_is_created(&t->expunges)) {
-               /* non-external expunges are only requests, ignore them when
-                  checking fsync_mask */
-               if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0)
-                       change_mask |= MAIL_INDEX_SYNC_TYPE_EXPUNGE;
-               log_append_buffer(&ctx, t->expunges.arr.buffer, NULL,
-                                 MAIL_TRANSACTION_EXPUNGE);
-       }
-
-       if (t->post_hdr_changed) {
-               log_append_buffer(&ctx, log_get_hdr_update_buffer(t, FALSE),
-                                 NULL, MAIL_TRANSACTION_HEADER_UPDATE);
-       }
-
-       /* NOTE: mailbox sync offset update must be the last change.
-          it may update the sync offset to include this transaction, so it
-          needs to know this transaction's size */
-       if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0)
-               log_append_sync_offset_if_needed(&ctx);
+       struct mail_transaction_log_file *file = ctx->log->head;
 
        if (file->sync_offset < file->last_size) {
                /* there is some garbage at the end of the transaction log
@@ -684,58 +143,55 @@ mail_transaction_log_append_locked(struct mail_index_transaction *t,
                                     file->sync_offset - file->buffer_offset);
                if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
                        if (ftruncate(file->fd, file->sync_offset) < 0) {
-                               mail_index_file_set_syscall_error(index,
+                               mail_index_file_set_syscall_error(ctx->log->index,
                                        file->filepath, "ftruncate()");
                        }
                }
        }
 
-       want_fsync = (view->index->fsync_mask & change_mask) != 0 ||
-               (t->flags & MAIL_INDEX_TRANSACTION_FLAG_FSYNC) != 0;
-       append_offset = file->sync_offset;
-       if (log_buffer_write(&ctx, want_fsync) < 0) {
-               buffer_free(&ctx.output);
-               return -1;
-       }
-       file->sync_highest_modseq = ctx.modseq;
-       buffer_free(&ctx.output);
-
-       if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_HIDE) != 0) {
-               /* mark the area covered by this transaction hidden */
-               mail_index_view_add_hidden_transaction(view, file->hdr.file_seq,
-                       append_offset, file->sync_offset - append_offset);
-       }
+       if (ctx->append_sync_offset)
+               log_append_sync_offset_if_needed(ctx);
 
-       *log_file_seq_r = file->hdr.file_seq;
-       *log_file_offset_r = file->sync_offset;
+       if (log_buffer_write(ctx) < 0)
+               return -1;
+       file->sync_highest_modseq = ctx->new_highest_modseq;
        return 0;
 }
 
-int mail_transaction_log_append(struct mail_index_transaction *t,
-                               uint32_t *log_file_seq_r,
-                               uoff_t *log_file_offset_r)
+int mail_transaction_log_append_begin(struct mail_index_transaction *t,
+                                     struct mail_transaction_log_append_ctx **ctx_r)
 {
+       struct mail_transaction_log_append_ctx *ctx;
        struct mail_index *index;
-       int ret;
-
-       *log_file_seq_r = 0;
-       *log_file_offset_r = 0;
-
-       if (!TRANSACTION_HAS_CHANGES(t) && !t->reset) {
-               /* nothing to append */
-               return 0;
-       }
 
        index = mail_index_view_get_index(t->view);
        if (!index->log_locked) {
                if (mail_transaction_log_lock_head(index->log) < 0)
                        return -1;
        }
+       ctx = i_new(struct mail_transaction_log_append_ctx, 1);
+       ctx->log = index->log;
+       ctx->output = buffer_create_dynamic(default_pool, 1024);
+
+       *ctx_r = ctx;
+       return 0;
+}
+
+int mail_transaction_log_append_commit(struct mail_transaction_log_append_ctx **_ctx)
+{
+       struct mail_transaction_log_append_ctx *ctx = *_ctx;
+       struct mail_index *index = ctx->log->index;
+       int ret = 0;
 
-       ret = mail_transaction_log_append_locked(t, log_file_seq_r,
-                                                log_file_offset_r);
+       *_ctx = NULL;
+
+       if (ctx->output->used > 0)
+               ret = mail_transaction_log_append_locked(ctx);
 
        if (!index->log_locked)
                mail_transaction_log_file_unlock(index->log->head);
+
+       buffer_free(&ctx->output);
+       i_free(ctx);
        return ret;
 }
index f36e12fe38c25318b3f376062644f87303b9dc3d..295cc9f2c393d8accf549f6db672e978acca21ae 100644 (file)
@@ -131,6 +131,16 @@ struct mail_transaction_ext_atomic_inc {
        int32_t diff;
 };
 
+struct mail_transaction_log_append_ctx {
+       struct mail_transaction_log *log;
+       buffer_t *output;
+
+       uint64_t new_highest_modseq;
+       unsigned int append_sync_offset:1;
+       unsigned int sync_includes_this:1;
+       unsigned int want_fsync:1;
+};
+
 #define LOG_IS_BEFORE(seq1, offset1, seq2, offset2) \
        (((offset1) < (offset2) && (seq1) == (seq2)) || (seq1) < (seq2))
 
@@ -213,13 +223,9 @@ mail_transaction_log_view_is_corrupted(struct mail_transaction_log_view *view);
 
 void mail_transaction_log_views_close(struct mail_transaction_log *log);
 
-/* Write data to transaction log. This is atomic operation. Sequences in
-   updates[] and expunges[] are relative to given view, they're modified
-   to real ones. If nothing is written, log_file_seq_r and log_file_offset_r
-   will be set to 0. */
-int mail_transaction_log_append(struct mail_index_transaction *t,
-                               uint32_t *log_file_seq_r,
-                               uoff_t *log_file_offset_r);
+int mail_transaction_log_append_begin(struct mail_index_transaction *t,
+                                     struct mail_transaction_log_append_ctx **ctx_r);
+int mail_transaction_log_append_commit(struct mail_transaction_log_append_ctx **ctx);
 
 /* Lock transaction log for index synchronization. Log cannot be read or
    written to while it's locked. Returns end offset. */