mail-index-sync-update.c revision 1df526903ed039e8ff966a223c43b8d04eddf3c7
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher/* Copyright (c) 2004-2014 Dovecot authors, see the included COPYING file */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan/* If we have less than this many bytes to sync from log file, don't bother
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek reading the main index */
531661c7bb54eb71853977a64cb30f80c20b963eJakub Hrozek#define MAIL_INDEX_SYNC_MIN_READ_INDEX_SIZE 2048
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozekmail_index_sync_update_log_offset(struct mail_index_sync_map_ctx *ctx,
7a14e8f66c0e932fe2954d792614a3b61d444bd1Jakub Hrozek mail_transaction_log_view_get_prev_pos(ctx->view->log_view,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* handling lost changes in view syncing */
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek if (prev_offset == ctx->ext_intro_end_offset &&
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek /* previous transaction was an extension introduction.
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek we probably came here from
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek mail_index_sync_ext_reset(). if there are any more
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan views which want to continue syncing it needs the
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan intro. so back up a bit more.
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher don't do this in case the last transaction in the
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan log is the extension intro, so we don't keep trying
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan to sync it over and over again. */
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek i_assert(ctx->view->index->log->head->hdr.file_seq == prev_seq);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan map->hdr.log_file_head_offset = prev_offset;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic void mail_index_sync_replace_map(struct mail_index_sync_map_ctx *ctx,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher mail_index_sync_update_log_offset(ctx, view->map, FALSE);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (ctx->type != MAIL_INDEX_SYNC_HANDLER_VIEW)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher mail_index_modseq_sync_map_replaced(ctx->modseq_ctx);
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozekmail_index_sync_move_to_private_memory(struct mail_index_sync_map_ctx *ctx)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (!MAIL_INDEX_MAP_IS_IN_MEMORY(ctx->view->map))
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_index_map_move_to_memory(ctx->view->map);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_index_modseq_sync_map_replaced(ctx->modseq_ctx);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanmail_index_sync_get_atomic_map(struct mail_index_sync_map_ctx *ctx)
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek (void)mail_index_sync_move_to_private_memory(ctx);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher mail_index_record_map_move_to_private(ctx->view->map);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher mail_index_modseq_sync_map_replaced(ctx->modseq_ctx);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanmail_index_header_update_counts(struct mail_index_header *hdr,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (((old_flags ^ new_flags) & MAIL_SEEN) != 0) {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* different seen-flag */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (hdr->seen_messages_count >= hdr->messages_count) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher if (++hdr->seen_messages_count == hdr->messages_count)
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan hdr->first_unseen_uid_lowwater = hdr->next_uid;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (((old_flags ^ new_flags) & MAIL_DELETED) != 0) {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* different deleted-flag */
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher if (hdr->deleted_messages_count > hdr->messages_count) {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher hdr->deleted_messages_count > hdr->messages_count) {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher hdr->first_deleted_uid_lowwater = hdr->next_uid;
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozekmail_index_sync_header_update_counts_all(struct mail_index_sync_map_ctx *ctx,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher unsigned int i, count;
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek maps = array_get(&ctx->view->map->rec_map->maps, &count);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan for (i = 0; i < count; i++) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (mail_index_header_update_counts(&maps[i]->hdr,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_index_sync_set_corrupted(ctx, "%s", error);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanmail_index_sync_header_update_counts(struct mail_index_sync_map_ctx *ctx,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mail_index_sync_set_corrupted(ctx, "uid %u >= next_uid %u",
531661c7bb54eb71853977a64cb30f80c20b963eJakub Hrozek if (mail_index_header_update_counts(&ctx->view->map->hdr,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_index_sync_set_corrupted(ctx, "%s", error);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanmail_index_header_update_lowwaters(struct mail_index_sync_map_ctx *ctx,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan unsigned int i, count;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan maps = array_get(&ctx->view->map->rec_map->maps, &count);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan for (i = 0; i < count; i++) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher uid < maps[i]->hdr.first_unseen_uid_lowwater)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan maps[i]->hdr.first_unseen_uid_lowwater = uid;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan uid < maps[i]->hdr.first_deleted_uid_lowwater)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan maps[i]->hdr.first_deleted_uid_lowwater = uid;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivansync_expunge_call_handlers(struct mail_index_sync_map_ctx *ctx,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan const struct mail_index_expunge_handler *eh;
7797e361155f7ce937085fd98e360469d7baf1b6Jakub Hrozek rec = MAIL_INDEX_REC_AT_SEQ(ctx->view->map, seq);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* FIXME: does expunge handler's return value matter?
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher we probably shouldn't disallow expunges if the
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher handler returns failure.. should it be just changed
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan to return void? */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivansync_expunge_handlers_init(struct mail_index_sync_map_ctx *ctx)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* call expunge handlers only when syncing index file */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (ctx->type != MAIL_INDEX_SYNC_HANDLER_FILE)
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher if (!array_is_created(&ctx->expunge_handlers))
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozeksync_expunge_range(struct mail_index_sync_map_ctx *ctx, const ARRAY_TYPE(seq_range) *seqs)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek unsigned int i, count;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* call the expunge handlers first */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek for (i = 0; i < count; i++) {
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek for (i = 0; i < count; i++) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mail_index_sync_header_update_counts(ctx, rec->uid, rec->flags, 0);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* @UNSAFE: move (prev_seq2+1) .. (seq1-1) to its
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek final location in the map if necessary */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek uint32_t move_count = (seq1-1) - (prev_seq2+1) + 1;
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek mail_index_modseq_expunge(ctx->modseq_ctx, seq1, seq2);
531661c7bb54eb71853977a64cb30f80c20b963eJakub Hrozek /* Final stragglers */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek uint32_t final_move_count = orig_rec_count - prev_seq2;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic void *sync_append_record(struct mail_index_map *map)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek append_pos = map->rec_map->records_count * map->hdr.record_size;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek ret = buffer_get_space_unsafe(map->rec_map->buffer, append_pos,
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek buffer_get_modifiable_data(map->rec_map->buffer, NULL);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic bool sync_update_ignored_change(struct mail_index_sync_map_ctx *ctx)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek struct mail_index_transaction_commit_result *result =
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek uoff_t prev_log_offset, trans_start_offset, trans_end_offset;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* we'll return TRUE if this modseq change was written within the
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek transaction that was just committed */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek mail_transaction_log_view_get_prev_pos(ctx->view->log_view,
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek trans_start_offset = trans_end_offset - result->commit_size;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivansync_modseq_update(struct mail_index_sync_map_ctx *ctx,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan const struct mail_transaction_modseq_update *u,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan const struct mail_transaction_modseq_update *end;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan for (; u < end; u++) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan else if (!mail_index_lookup_seq(view, u->uid, &seq))
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan min_modseq = ((uint64_t)u->modseq_high32 << 32) |
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_index_modseq_set(view, seq, min_modseq);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher "modseqs updated before they were enabled");
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (ret == 0 && sync_update_ignored_change(ctx))
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan view->index->sync_commit_result->ignored_modseq_changes++;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_index_modseq_update_highest(ctx->modseq_ctx, highest_modseq);
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagherstatic int sync_append(const struct mail_index_record *rec,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek "Append with UID %u, but next_uid = %u",
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* move to memory. the mapping is written when unlocking so we don't
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan waste time re-mmap()ing multiple times or waste space growing index
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan file too large */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan map = mail_index_sync_move_to_private_memory(ctx);
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher if (rec->uid <= map->rec_map->last_appended_uid) {
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher i_assert(map->hdr.messages_count < map->rec_map->records_count);
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher /* the flags may have changed since it was added to map.
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan use the updated flags already, so flag counters won't get
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek old_rec = MAIL_INDEX_MAP_IDX(map, map->hdr.messages_count);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* don't rely on buffer->used being at the correct position.
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek at least expunges can move it */
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher if ((new_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_index_header_update_lowwaters(ctx, rec->uid, new_flags);
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher mail_index_sync_header_update_counts(ctx, rec->uid, 0, new_flags);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic int sync_flag_update(const struct mail_transaction_flag_update *u,
7797e361155f7ce937085fd98e360469d7baf1b6Jakub Hrozek if (!mail_index_lookup_seq_range(view, u->uid1, u->uid2, &seq1, &seq2))
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek if (!MAIL_TRANSACTION_FLAG_UPDATE_IS_INTERNAL(u)) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_index_modseq_update_flags(ctx->modseq_ctx,
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek if ((u->add_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek view->map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* we're not modifying any counted/lowwatered flags */
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek rec->flags = (rec->flags & flag_mask) | u->add_flags;
0172959f117b545c8a6b1893f5f56818d82dd624Jakub Hrozek rec->flags = (rec->flags & flag_mask) | u->add_flags;
0172959f117b545c8a6b1893f5f56818d82dd624Jakub Hrozek mail_index_header_update_lowwaters(ctx, rec->uid,
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek mail_index_sync_header_update_counts_all(ctx, rec->uid,
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozekstatic int sync_header_update(const struct mail_transaction_header_update *u,
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek#define MAIL_INDEX_HEADER_UPDATE_FIELD_IN_RANGE(u, field) \
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek ((u)->offset <= offsetof(struct mail_index_header, field) && \
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek (u)->offset + (u)->size > offsetof(struct mail_index_header, field))
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek uint32_t orig_log_file_tail_offset = map->hdr.log_file_tail_offset;
0172959f117b545c8a6b1893f5f56818d82dd624Jakub Hrozek u->offset + u->size > map->hdr.base_header_size) {
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek "Header update outside range: %u + %u > %u",
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek u->offset, u->size, map->hdr.base_header_size);
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek buffer_write(map->hdr_copy_buf, u->offset, u + 1, u->size);
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek /* @UNSAFE */
0172959f117b545c8a6b1893f5f56818d82dd624Jakub Hrozek if ((uint32_t)(u->offset + u->size) <= sizeof(map->hdr)) {
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek /* next_uid update tried to shrink its value. this can happen
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek in some race conditions with e.g. with dsync, so just
0172959f117b545c8a6b1893f5f56818d82dd624Jakub Hrozek silently ignore it. */
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek /* the tail offset updates are intended for internal transaction
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher log handling. we'll update the offset in the header only when
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher the sync is finished. */
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher map->hdr.log_file_tail_offset = orig_log_file_tail_offset;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallaghermail_index_sync_record_real(struct mail_index_sync_map_ctx *ctx,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan const struct mail_transaction_expunge *rec = data, *end;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan /* this is simply a request for expunge */
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan seq_range_array_add_range(&seqs, seq1, seq2);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan const struct mail_transaction_expunge_guid *rec = data, *end;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* this is simply a request for expunge */
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher if (mail_index_lookup_seq(ctx->view, rec->uid, &seq))
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek const struct mail_transaction_flag_update *rec, *end;
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher const struct mail_transaction_header_update *rec;
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher unsigned int i;
531661c7bb54eb71853977a64cb30f80c20b963eJakub Hrozek if ((i % 4) != 0)
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek const struct mail_transaction_ext_intro *rec = data;
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek unsigned int i;
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek mail_transaction_log_view_get_prev_pos(ctx->view->log_view,
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek /* should be just extra padding */
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek /* name_size checked by _log_view_next() */
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek i_assert(i + sizeof(*rec) + rec->name_size <= hdr->size);
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek if ((i % 4) != 0)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* old versions have only new_reset_id */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek "ext reset: invalid record size");
531661c7bb54eb71853977a64cb30f80c20b963eJakub Hrozek memcpy(&rec, data, I_MIN(hdr->size, sizeof(rec)));
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek const struct mail_transaction_ext_hdr_update *rec;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek unsigned int i;
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek "ext hdr update: invalid record size");
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek ret = mail_index_sync_ext_hdr_update(ctx, rec->offset,
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek if ((i % 4) != 0)
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek const struct mail_transaction_ext_hdr_update32 *rec;
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek unsigned int i;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek "ext hdr update: invalid record size");
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek ret = mail_index_sync_ext_hdr_update(ctx, rec->offset,
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek if ((i % 4) != 0)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher const struct mail_transaction_ext_rec_update *rec;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan unsigned int i, record_size;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan "Extension record updated "
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher "without intro prefix");
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan ext = array_idx(&ctx->view->map->extensions,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* the record is padded to 32bits in the transaction log */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher record_size = (sizeof(*rec) + ext->record_size + 3) & ~3;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan for (i = 0; i < hdr->size; i += record_size) {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher "ext rec update: invalid record size");
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher ret = mail_index_sync_ext_rec_update(ctx, rec);
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagher const struct mail_transaction_ext_atomic_inc *rec, *end;
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagher if (ctx->cur_ext_map_idx == (uint32_t)-1) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek "Extension record updated "
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagher "without intro prefix");
486237ee009f1d84fc4c85665dce80ade76f7079Stephen Gallagher ret = mail_index_sync_ext_atomic_inc(ctx, rec);
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher const struct mail_transaction_keyword_update *rec = data;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher ret = mail_index_sync_keywords(ctx, hdr, rec);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek const struct mail_transaction_keyword_reset *rec = data;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher ret = mail_index_sync_keywords_reset(ctx, hdr, rec);
7797e361155f7ce937085fd98e360469d7baf1b6Jakub Hrozek const struct mail_transaction_modseq_update *rec = data;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek /* next sync finishes the deletion */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek ctx->view->index->index_delete_requested = TRUE;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* transaction log reading handles this */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek ctx->view->index->index_delete_requested = FALSE;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek modseq = mail_transaction_log_view_get_prev_modseq(ctx->view->log_view);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek mail_index_modseq_update_highest(ctx->modseq_ctx, modseq);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek "Unknown transaction record type 0x%x",
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekint mail_index_sync_record(struct mail_index_sync_map_ctx *ctx,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek const void *data)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek ret = mail_index_sync_record_real(ctx, hdr, data);
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozekvoid mail_index_sync_map_init(struct mail_index_sync_map_ctx *sync_map_ctx,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek memset(sync_map_ctx, 0, sizeof(*sync_map_ctx));
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek sync_map_ctx->modseq_ctx = mail_index_modseq_sync_begin(sync_map_ctx);
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozekvoid mail_index_sync_map_deinit(struct mail_index_sync_map_ctx *sync_map_ctx)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek buffer_free(&sync_map_ctx->unknown_extensions);
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek mail_index_sync_deinit_expunge_handlers(sync_map_ctx);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekstatic void mail_index_sync_update_hdr_dirty_flag(struct mail_index_map *map)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek unsigned int i;
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek if ((map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* do we have dirty flags anymore? */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek for (i = 0; i < map->rec_map->records_count; i++) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekvoid mail_index_map_check(struct mail_index_map *map)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek const struct mail_index_header *hdr = &map->hdr;
531661c7bb54eb71853977a64cb30f80c20b963eJakub Hrozek i_assert(hdr->messages_count <= map->rec_map->records_count);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek i_assert(rec->uid >= hdr->first_deleted_uid_lowwater);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek i_assert(rec->uid >= hdr->first_unseen_uid_lowwater);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekint mail_index_sync_map(struct mail_index_map **_map,
531661c7bb54eb71853977a64cb30f80c20b963eJakub Hrozek enum mail_index_sync_handler_type type, bool force)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek i_assert(index->map == map || type == MAIL_INDEX_SYNC_HANDLER_VIEW);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek start_offset = type == MAIL_INDEX_SYNC_HANDLER_FILE ?
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek map->hdr.log_file_tail_offset : map->hdr.log_file_head_offset;
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (!force && (index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0) {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* see if we'd prefer to reopen the index file instead of
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek syncing the current map from the transaction log.
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan don't check this if mmap is disabled, because reopening
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan index causes sync to get lost. */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* we don't know the index's size, so use the
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan smallest index size we're willing to read */
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan index_size = MAIL_INDEX_SYNC_MIN_READ_INDEX_SIZE;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* this isn't necessary correct currently, but it should be
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher close enough */
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher view = mail_index_view_open_with_map(index, map);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan ret = mail_transaction_log_view_set(view->log_view,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* the seq/offset is probably broken */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mail_index_set_error(index, "Index %s: Lost log for "
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan "seq=%u offset=%"PRIuUOFF_T, index->filepath,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* can't use it. sync by re-reading index. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_transaction_log_get_head(index->log, &prev_seq, &prev_offset);
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher prev_offset - map->hdr.log_file_tail_offset >
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher /* we're reading more from log than we would have preferred.
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan remember that we probably want to rewrite index soon. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* view referenced the map. avoid unnecessary map cloning by
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan unreferencing the map while view exists. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan had_dirty = (map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0;
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher map->hdr.flags &= ~MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher if (map->hdr_base != map->hdr_copy_buf->data) {
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher /* if syncing updates the header, it updates hdr_copy_buf
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher and updates hdr_base to hdr_copy_buf. so the buffer must
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan initially contain a valid header or we'll break it when
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher writing it. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan buffer_append(map->hdr_copy_buf, map->hdr_base,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_transaction_log_view_get_prev_pos(view->log_view,
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher mail_index_sync_map_init(&sync_map_ctx, view, type);
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher /* Reset the entire index. Leave only indexid and
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan log_file_seq. */
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher mail_transaction_log_view_get_prev_pos(view->log_view,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher mail_index_sync_replace_map(&sync_map_ctx, map);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* FIXME: when transaction sync lock is removed, we'll need to handle
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher the case when a transaction is committed while mailbox is being
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek synced ([synced transactions][new transaction][ext transaction]).
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan this means int_offset contains [synced] and ext_offset contains
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan while ((ret = mail_transaction_log_view_next(view->log_view, &thdr,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan mail_transaction_log_view_get_prev_pos(view->log_view,
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek /* this has been synced already. we're here only to call
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan expunge handlers and extension update handlers. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_assert(type == MAIL_INDEX_SYNC_HANDLER_FILE);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) != 0)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if ((thdr->type & MAIL_TRANSACTION_EXT_MASK) == 0)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* we'll just skip over broken entries */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan (void)mail_index_sync_record(&sync_map_ctx, thdr, tdata);
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher mail_index_modseq_sync_end(&sync_map_ctx.modseq_ctx);
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher mail_index_sync_update_log_offset(&sync_map_ctx, view->map, TRUE);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek i_assert(map->hdr.indexid == index->indexid || map->hdr.indexid == 0);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* transaction log tracks internally the current tail offset.
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan besides using header updates, it also updates the offset to skip
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher over following external transactions to avoid extra unneeded log
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_assert(map->hdr.log_file_seq == index->log->head->hdr.file_seq);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan if (map->hdr.log_file_tail_offset < index->log->head->max_tail_offset) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan buffer_write(map->hdr_copy_buf, 0, &map->hdr, sizeof(map->hdr));
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan memcpy(map->rec_map->mmap_base, map->hdr_copy_buf->data,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* restore refcount before closing the view. this is necessary also
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek if map got cloned, because view closing would otherwise destroy it */
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher mail_index_sync_map_deinit(&sync_map_ctx);
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher i_assert(index->map == map || type == MAIL_INDEX_SYNC_HANDLER_VIEW);
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek "Synchronization corrupted index header: %s",
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek /* make sure the index looks valid now */