mail-index-transaction-export.c revision 9e6d83a3ef6abb393eeebca423cfd0d8cb08d430
89a126810703c666309310d0f3189e9834d70b5bTimo Sirainen/* Copyright (c) 2003-2016 Dovecot authors, see the included COPYING file */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen struct mail_transaction_log_append_ctx *append_ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenlog_append_buffer(struct mail_index_export_context *ctx,
dd0ba1bab2c1b89c7e063fa45d156fa72b8260d5Timo Sirainen const buffer_t *buf, enum mail_transaction_type type)
6cd02a9525acb9897a65b05bfeee9b8d283e0f74Timo Sirainen mail_transaction_log_append_add(ctx->append_ctx, type,
6cd02a9525acb9897a65b05bfeee9b8d283e0f74Timo Sirainenstatic void log_append_flag_updates(struct mail_index_export_context *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ARRAY(struct mail_transaction_flag_update) log_updates;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_transaction_flag_update *log_update;
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen unsigned int i, count;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen for (i = 0; i < count; i++) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen log_update = array_append_space(&log_updates);
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen log_update->add_flags = updates[i].add_flags & 0xff;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen log_update->remove_flags = updates[i].remove_flags & 0xff;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((updates[i].add_flags & MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ) != 0)
e1f05b193ac1edd3267294e9501e8063aa0f791aTimo Sirainen log_append_buffer(ctx, log_updates.arr.buffer,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenlog_get_hdr_update_buffer(struct mail_index_transaction *t, bool prepend)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(&u, 0, sizeof(u));
ee26329cb5cc679b5645e4933d529f86accb976aTimo Sirainen data = prepend ? t->pre_hdr_change : t->post_hdr_change;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mask = prepend ? t->pre_hdr_mask : t->post_hdr_mask;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 256);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen for (offset = 0; offset <= sizeof(t->pre_hdr_change); offset++) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (offset < sizeof(t->pre_hdr_change) && mask[offset] != 0) {
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainenstatic unsigned int
6cd02a9525acb9897a65b05bfeee9b8d283e0f74Timo Sirainenext_hdr_update_get_size(const struct mail_index_transaction_ext_hdr_update *hu)
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen unsigned int i;
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainenstatic void log_append_ext_intro(struct mail_index_export_context *ctx,
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainen unsigned int *hdr_size_r)
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainen struct mail_index_transaction *t = ctx->trans;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen struct mail_transaction_ext_intro *intro, *resizes;
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen unsigned int count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen !mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* new extension */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen rext = array_idx(&t->view->index->extensions, ext_id);
d24daa83c25063ef12b524c9ffcc9ecca34dadb9Timo Sirainen resizes = array_get_modifiable(&t->ext_resizes, &count);
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 128);
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen if (ext_id < count && resizes[ext_id].name_size != 0) {
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen /* we're resizing the extension. use the resize struct. */
d24daa83c25063ef12b524c9ffcc9ecca34dadb9Timo Sirainen /* generate a new intro structure */
d24daa83c25063ef12b524c9ffcc9ecca34dadb9Timo Sirainen intro = buffer_append_space_unsafe(buf, sizeof(*intro));
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen ext = array_idx(&t->view->index->map->extensions, idx);
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen intro->flags = MAIL_TRANSACTION_EXT_INTRO_FLAG_NO_SHRINK;
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen /* handle increasing header size automatically */
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen const struct mail_index_transaction_ext_hdr_update *hu;
31050c3df6cbe403e8ced8ef11b5c4e12124d354Timo Sirainen hdr_update_size = ext_hdr_update_get_size(hu);
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen i_assert(intro->record_size != 0 || intro->hdr_size != 0);
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen /* we're going to reset this extension in this transaction */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen /* use the existing reset_id */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen array_idx(&t->view->index->map->extensions, idx);
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen /* new extension, reset_id defaults to 0 */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen buffer_append(buf, rext->name, intro->name_size);
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen if (ctx->append_ctx->new_highest_modseq == 0 &&
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen strcmp(rext->name, MAIL_INDEX_MODSEQ_EXT_NAME) == 0) {
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen /* modseq tracking started */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen log_append_buffer(ctx, buf, MAIL_TRANSACTION_EXT_INTRO);
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainenlog_append_ext_hdr_update(struct mail_index_export_context *ctx,
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen const struct mail_index_transaction_ext_hdr_update *hdr,
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen bool started = FALSE, use_32 = hdr->alloc_size >= 65536;
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen memset(&u, 0, sizeof(u));
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 256);
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen for (offset = 0; offset <= hdr->alloc_size; offset++) {
cfbf891f065b18602703ed6fa2af1a541d4d0b04Timo Sirainen if (offset < hdr->alloc_size && mask[offset] != 0) {
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen i_assert(u32.offset + u32.size <= ext_hdr_size);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen buffer_append(buf, data + u32.offset, u32.size);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen log_append_buffer(ctx, buf, use_32 ? MAIL_TRANSACTION_EXT_HDR_UPDATE32 :
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenmail_transaction_log_append_ext_intros(struct mail_index_export_context *ctx)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_transaction *t = ctx->trans;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen const struct mail_transaction_ext_intro *resize;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen const struct mail_index_transaction_ext_hdr_update *hdrs;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int hdrs_count, reset_id_count, reset_count, hdr_size;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen const struct mail_transaction_ext_reset *reset;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen resize = array_get(&t->ext_resizes, &resize_count);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen reset_ids = array_get(&t->ext_reset_ids, &reset_id_count);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen reset = array_get(&t->ext_resets, &reset_count);
faef57a36800fe952a5794328467f846d5480ae6Timo Sirainen hdrs = array_get(&t->ext_hdr_updates, &hdrs_count);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen buffer_create_from_data(&reset_buf, &ext_reset, sizeof(ext_reset));
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen buffer_set_used_size(&reset_buf, sizeof(ext_reset));
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen for (ext_id = 0; ext_id < ext_count; ext_id++) {
faef57a36800fe952a5794328467f846d5480ae6Timo Sirainen if ((ext_id < resize_count && resize[ext_id].name_size > 0) ||
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0)) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* we're going to reset this extension
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen immediately after the intro */
6cd02a9525acb9897a65b05bfeee9b8d283e0f74Timo Sirainen log_append_ext_intro(ctx, ext_id, reset_id, &hdr_size);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (ext_id < hdrs_count && hdrs[ext_id].alloc_size > 0) {
2623ed55e5bc547363abc7bc9de6b4ad2c53e531Timo Sirainenstatic void log_append_ext_recs(struct mail_index_export_context *ctx,
2623ed55e5bc547363abc7bc9de6b4ad2c53e531Timo Sirainen struct mail_index_transaction *t = ctx->trans;
2623ed55e5bc547363abc7bc9de6b4ad2c53e531Timo Sirainen unsigned int ext_id, count, reset_id_count, hdr_size;
2623ed55e5bc547363abc7bc9de6b4ad2c53e531Timo Sirainen reset_ids = array_get_modifiable(&t->ext_reset_ids,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen reset_id = ext_id < reset_id_count ? reset_ids[ext_id] : 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen log_append_ext_intro(ctx, ext_id, reset_id, &hdr_size);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen log_append_buffer(ctx, updates[ext_id].arr.buffer, type);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenlog_append_keyword_update(struct mail_index_export_context *ctx,
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen buffer_t *tmp_buf, enum modify_type modify_type,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen const char *keyword, const buffer_t *uid_buffer)
b30499429feee4f16f1e09c7047101dc7ff38304Timo Sirainen struct mail_transaction_keyword_update kt_hdr;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen buffer_append(tmp_buf, &kt_hdr, sizeof(kt_hdr));
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen buffer_append(tmp_buf, keyword, kt_hdr.name_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_append_zero(tmp_buf, 4 - (tmp_buf->used % 4));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_append(tmp_buf, uid_buffer->data, uid_buffer->used);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen log_append_buffer(ctx, tmp_buf, MAIL_TRANSACTION_KEYWORD_UPDATE);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainenlog_append_keyword_updates(struct mail_index_export_context *ctx)
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen const struct mail_index_transaction_keyword_update *updates;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen const char *const *keywords;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen tmp_buf = buffer_create_dynamic(pool_datastack_create(), 64);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen keywords = array_get_modifiable(&ctx->trans->view->index->keywords,
b30499429feee4f16f1e09c7047101dc7ff38304Timo Sirainen updates = array_get_modifiable(&ctx->trans->keyword_updates, &count);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen for (i = 0; i < count; i++) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen change_mask |= MAIL_INDEX_FSYNC_MASK_KEYWORDS;
6cd02a9525acb9897a65b05bfeee9b8d283e0f74Timo Sirainen if (array_is_created(&updates[i].remove_seq) &&
b30499429feee4f16f1e09c7047101dc7ff38304Timo Sirainen change_mask |= MAIL_INDEX_FSYNC_MASK_KEYWORDS;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenvoid mail_index_transaction_export(struct mail_index_transaction *t,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_transaction_log_append_ctx *append_ctx)
cfbb3ff45d6bd27e6442e332fa69c43c984ca651Timo Sirainen mail_transaction_log_append_add(ctx.append_ctx,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* send all extension introductions and resizes before appends
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen to avoid resize overhead as much as possible */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen log_append_buffer(&ctx, log_get_hdr_update_buffer(t, TRUE),
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* need to have 32bit alignment */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* append the timestamp and value lengths */
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_assert(t->attribute_updates->used % 4 == 0);
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainen log_append_buffer(&ctx, t->appends.arr.buffer,
b30499429feee4f16f1e09c7047101dc7ff38304Timo Sirainen log_append_ext_recs(&ctx, &t->ext_rec_updates,
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen log_append_ext_recs(&ctx, &t->ext_rec_atomics,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen change_mask |= log_append_keyword_updates(&ctx);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen /* keep modseq updates almost last */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen log_append_buffer(&ctx, t->modseq_updates.arr.buffer,
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen /* non-external expunges are only requests, ignore them when
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen checking fsync_mask */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen change_mask |= MAIL_INDEX_FSYNC_MASK_EXPUNGES;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen log_append_buffer(&ctx, t->expunges.arr.buffer,
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen log_append_buffer(&ctx, log_get_hdr_update_buffer(t, FALSE),
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mail_transaction_log_append_add(ctx.append_ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen append_ctx->index_sync_transaction = t->sync_transaction;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen append_ctx->tail_offset_changed = t->tail_offset_changed;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen (t->view->index->fsync_mask & change_mask) != 0 ||
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen (t->flags & MAIL_INDEX_TRANSACTION_FLAG_FSYNC) != 0;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainenstatic unsigned int
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainencount_modseq_incs_with(struct mail_index_transaction *t,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mail_index_transaction_seq_range_to_uid(t, tmp_seqs);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenstatic unsigned int
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenmail_index_transaction_keywords_count_modseq_incs(struct mail_index_transaction *t)
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen const struct mail_index_transaction_keyword_update *update;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int count = 0;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen array_foreach_modifiable(&t->keyword_updates, update) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen count += count_modseq_incs_with(t, &tmp_seqs, &update->add_seq);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen count += count_modseq_incs_with(t, &tmp_seqs, &update->remove_seq);
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainenuint64_t mail_index_transaction_get_highest_modseq(struct mail_index_transaction *t)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen struct mail_transaction_log_file *file = t->view->index->log->head;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uint64_t new_highest_modseq = file->sync_highest_modseq;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* highest-modseq tracking isn't enabled in this transaction
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen log file. This shouldn't happen with logs created since
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen v2.2.26+, because initial_modseq is always set. We don't
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen also bother checking if this transaction itself enables the
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen highest-modseq tracking, because it's always done as a
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen standalone transaction in mail_index_modseq_enable(),
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen which doesn't care about this function. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_warning("%s: Requested highest-modseq for transaction, "
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen "but modseq tracking isn't enabled for the file "
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* finish everything that can affect highest-modseq */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* NOTE: keep in sync with mail_transaction_update_modseq() */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (array_is_created(&t->appends) && array_count(&t->appends) > 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* sorting may change the order of keyword_updates, */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (array_is_created(&t->updates) && array_count(&t->updates) > 0)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mail_index_transaction_keywords_count_modseq_incs(t);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* NOTE: the order of modseq_updates and everything following it
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen must match mail_index_transaction_export(). */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen const struct mail_transaction_modseq_update *mu;
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen /* mail_index_update_highest_modseq() is handled here also,
e37fbcda56ab154557e84f990012502be53aa6c6Timo Sirainen as a special case of uid==0. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uint64_t modseq = ((uint64_t)mu->modseq_high32 << 32) |
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (array_is_created(&t->expunges) && array_count(&t->expunges) > 0 &&