mail-index-view-sync.c revision 2a6af811ea3de3cf9e2f15e446674dd21b0705f3
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainenmail_transaction_log_sort_expunges(ARRAY_TYPE(seq_range) *expunges,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Note that all the sequences are actually still UIDs at this point */
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen /* @UNSAFE */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen dest = array_get_modifiable(expunges, &dest_count);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen array_append(expunges, src, src_size / sizeof(*src));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* src[] must be sorted. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (src+1 != src_end && src->seq2 >= src[1].seq1))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (; i < dest_count; i++) {
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen while (i < dest_count && src->seq2 >= dest[i].seq1-1) {
e9ac518aaf49a06d976bf7f24ab14a3e2d6d86abTimo Sirainen /* we can/must merge with next record */
230ef558135f16a66b86cbe3762524eaa9ae9d81Timo Sirainen if (first > 0 && new_exp.seq1 <= dest[first-1].seq2+1) {
230ef558135f16a66b86cbe3762524eaa9ae9d81Timo Sirainen /* continue previous record */
e9ac518aaf49a06d976bf7f24ab14a3e2d6d86abTimo Sirainen } else if (i == first) {
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen dest = array_get_modifiable(expunges, &dest_count);
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen /* use next record */
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen dest = array_get_modifiable(expunges, &dest_count);
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainenstatic int view_sync_set_log_view_range(struct mail_index_view *view,
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen const struct mail_index_header *hdr = view->index->hdr;
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainen /* the view begins from the first non-synced transaction */
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen ret = mail_transaction_log_view_set(view->log_view,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* FIXME: use the new index to get needed changes */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen "Transaction log got desynced for index %s",
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainenview_sync_get_expunges(struct mail_index_view *view,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen unsigned int count;
29f32cdcf44cda9688576bfdc7450a8a15e90e86Timo Sirainen if (view_sync_set_log_view_range(view, MAIL_TRANSACTION_EXPUNGE) < 0)
29f32cdcf44cda9688576bfdc7450a8a15e90e86Timo Sirainen /* get a list of expunge transactions. there may be some that we have
29f32cdcf44cda9688576bfdc7450a8a15e90e86Timo Sirainen already synced, but it doesn't matter because they'll get dropped
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen out when converting to sequences */
b955a1c1b6d466977d971c029a9305bee492f73cTimo Sirainen while ((ret = mail_transaction_log_view_next(view->log_view,
b955a1c1b6d466977d971c029a9305bee492f73cTimo Sirainen i_assert((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0);
b955a1c1b6d466977d971c029a9305bee492f73cTimo Sirainen if (mail_transaction_log_sort_expunges(expunges_r, data,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_transaction_log_view_set_corrupted(view->log_view,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen "Corrupted expunge record");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* convert UIDs to sequences */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen src = dest = array_get_modifiable(expunges_r, &count);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ret = mail_index_lookup_uid_range(view, src->seq1,
72cc352b25ad401b923436c6ed0f1f3adaffa737Timo Sirainen array_delete(expunges_r, count, array_count(expunges_r) - count);
9ebd0c59de5f8240c0dbc58773fe5679391199dbTimo Sirainenstatic void mail_index_view_hdr_update(struct mail_index_view *view,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Keep message count the same. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen map->hdr.messages_count = view->hdr.messages_count;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Keep the old message flag counts also, although they may be
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen somewhat stale already. We just don't want them to be more than
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen our old messages_count. */
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen map->hdr.recent_messages_count = view->hdr.recent_messages_count;
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen map->hdr.seen_messages_count = view->hdr.seen_messages_count;
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen map->hdr.deleted_messages_count = view->hdr.deleted_messages_count;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Keep log position so we know where to continue syncing */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen map->hdr.log_file_seq = view->hdr.log_file_seq;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen buffer_write(map->hdr_copy_buf, 0, &map->hdr, sizeof(map->hdr));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void mail_index_view_check(struct mail_index_view *view)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen unsigned int i, del = 0, recent = 0, seen = 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (i = 0; i < view->map->records_count; i++) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_assert(rec->uid >= view->hdr.first_deleted_uid_lowwater);
5d46f4d076fc13ae2779c365cf4bd9bda3a5bc69Timo Sirainen i_assert(rec->uid >= view->hdr.first_recent_uid_lowwater);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_assert(rec->uid >= view->hdr.first_unseen_uid_lowwater);
5d46f4d076fc13ae2779c365cf4bd9bda3a5bc69Timo Sirainen i_assert(del == view->hdr.deleted_messages_count);
3a0f9aa9504497e4e47f32df54fbf47fdc7423b6Timo Sirainen i_assert(recent == view->hdr.recent_messages_count);
3a0f9aa9504497e4e47f32df54fbf47fdc7423b6Timo Sirainen i_assert(seen == view->hdr.seen_messages_count);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD | MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_APPEND | \
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen MAIL_TRANSACTION_FLAG_UPDATE | MAIL_TRANSACTION_KEYWORD_UPDATE | \
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen ((hdr)->log_file_seq == (view)->log_file_seq && \
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen (hdr)->log_file_index_int_offset == (view)->log_file_offset && \
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen (hdr)->log_file_index_ext_offset == (view)->log_file_offset && \
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenint mail_index_view_sync_begin(struct mail_index_view *view,
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen enum mail_transaction_type log_get_mask, visible_mask;
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen /* We must sync flags as long as view is mmap()ed, as the flags may
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen have already changed under us. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_assert((sync_mask & MAIL_INDEX_VIEW_VISIBLE_FLAGS_MASK) ==
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Currently we're not handling correctly expunges + no-appends case */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_assert((sync_mask & MAIL_INDEX_SYNC_TYPE_EXPUNGE) == 0 ||
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen (sync_mask & MAIL_INDEX_SYNC_TYPE_APPEND) != 0);
601b455f4d5e780044b9e4fac5f687c1b07ae145Timo Sirainen if ((sync_mask & MAIL_INDEX_SYNC_TYPE_EXPUNGE) != 0) {
601b455f4d5e780044b9e4fac5f687c1b07ae145Timo Sirainen /* get list of all expunges first */
cdc8485491045d82bb98405d4b995f277d12838eTimo Sirainen if (view_sync_get_expunges(view, &expunges) < 0)
e5097d2c8efecdd274272b222cf2b30a8ae4ca2aTimo Sirainen /* only flags, appends and expunges can be left to be synced later */
e5097d2c8efecdd274272b222cf2b30a8ae4ca2aTimo Sirainen visible_mask = mail_transaction_type_mask_get(sync_mask);
e5097d2c8efecdd274272b222cf2b30a8ae4ca2aTimo Sirainen i_assert((visible_mask & ~MAIL_TRANSACTION_VISIBLE_SYNC_MASK) == 0);
e5097d2c8efecdd274272b222cf2b30a8ae4ca2aTimo Sirainen /* we want to also get non-visible changes. especially because we use
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen the returned skipped-flag in mail_transaction_log_view_next() to
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen tell us if any visible changes were skipped. */
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen log_get_mask = visible_mask | (MAIL_TRANSACTION_TYPE_MASK ^
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen if (view_sync_set_log_view_range(view, log_get_mask) < 0) {
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen ctx = i_new(struct mail_index_view_sync_ctx, 1);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_index_sync_map_init(&ctx->sync_map_ctx, view,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if ((sync_mask & MAIL_INDEX_SYNC_TYPE_EXPUNGE) != 0 &&
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen (sync_mask & MAIL_INDEX_SYNC_TYPE_APPEND) != 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* since we're syncing everything, the counters get fixed */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* keep the old mapping without expunges until we're
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen fully synced */
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen /* We need a private copy of the map if we don't want to
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen sync expunges.
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen If view's map is the head map, it means that it contains
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen already all the latest changes and there's no need for us
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen to apply any changes to it. This can only happen if there
3a0f9aa9504497e4e47f32df54fbf47fdc7423b6Timo Sirainen hadn't been any expunges. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen uint32_t old_records_count = view->map->records_count;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Using non-head mapping. We have to apply
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainen transactions to it to get latest changes into it. */
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen /* Unless map was synced at the exact same position as
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen view, the message flags can't be reliably used to
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen update flag counters. note that map->hdr may contain
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen old information if another process updated the
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen index file since. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen !VIEW_IS_SYNCED_TO_SAME(view, &view->map->hdr);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Copy only the mails that we see currently, since
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen we're going to append the new ones when we see
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen their transactions. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen view->map->records_count = view->hdr.messages_count;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Start the sync using our old view's header.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen The old view->hdr may differ from map->hdr if
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen another view sharing the map with us had synced
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_assert(map->hdr_base == map->hdr_copy_buf->data);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_assert(map->records_count == map->hdr.messages_count);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Syncing the view invalidates all previous looked up records.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen Unreference the mappings this view keeps because of them. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenview_add_synced_transaction(struct mail_index_view *view,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen uint32_t log_file_seq, uoff_t log_file_offset)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic bool view_sync_area_find(ARRAY_TYPE(view_log_sync_area) *sync_arr,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const struct mail_index_view_log_sync_area *syncs;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen unsigned int i, count;
129db31de1780783a175633eba5811e44c361a81Timo Sirainen for (i = 0; i < count; i++) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen offset - syncs[i].log_file_offset < syncs[i].length &&
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenmail_index_view_sync_get_next_transaction(struct mail_index_view_sync_ctx *ctx)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct mail_transaction_log_view *log_view = ctx->view->log_view;
114162093a3eb36c23a4ce4d4f2a43541dc18cc2Timo Sirainen /* Get the next transaction from log. */
ca502ca792708238ac2c23481c3bf94eda1f743fTimo Sirainen ret = mail_transaction_log_view_next(log_view, &ctx->hdr,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* We skipped some (visible) transactions that were
114162093a3eb36c23a4ce4d4f2a43541dc18cc2Timo Sirainen outside our sync mask. Note that we may get here
ca502ca792708238ac2c23481c3bf94eda1f743fTimo Sirainen even when ret=0. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_transaction_log_view_get_prev_pos(log_view, &seq, &offset);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* We haven't skipped anything while syncing this view.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen Update this view's synced log offset. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen view->log_file_offset = offset + sizeof(*ctx->hdr) +
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* skip everything we've already synced */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (view_sync_area_find(&view->syncs_done, seq, offset))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* We've been skipping some transactions, which means
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen we'll go through these same transactions again
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen later. Since we're syncing this one, we don't want
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen to do it again. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen view_add_synced_transaction(view, seq, offset);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* if we started from a map that we didn't create ourself,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen some of the external transactions may already be synced.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen at the end of view sync we'll update the ext_offset in the
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen header so that this check always becomes FALSE for
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen subsequent syncs. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen synced_to_map = offset < view->hdr.log_file_index_ext_offset &&
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (ctx->hdr->type & MAIL_TRANSACTION_EXTERNAL) != 0;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* Apply transaction to view's mapping if needed (meaning we
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen didn't just re-map the view to head mapping). */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (mail_index_sync_record(&ctx->sync_map_ctx,
9c8f854d95d8d895022a75f140a0a500eb200d39Timo Sirainen if ((ctx->hdr->type & ctx->visible_sync_mask) == 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* non-visible change that we just wanted to update
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* skip changes committed by hidden transactions (eg. in IMAP
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen store +flags.silent command) */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (view_sync_area_find(&view->syncs_hidden, seq, offset))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ~(MAIL_INDEX_MAIL_FLAG_DIRTY | MAIL_RECENT)) == 0)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenmail_index_view_sync_get_rec(struct mail_index_view_sync_ctx *ctx,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const struct mail_transaction_header *hdr = ctx->hdr;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* data contains the appended records, but we don't care */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* data contains mail_transaction_expunge[] */
case MAIL_TRANSACTION_FLAG_UPDATE: {
case MAIL_TRANSACTION_KEYWORD_UPDATE: {
case MAIL_TRANSACTION_KEYWORD_RESET: {
i_unreached();
int ret;
if (ret <= 0)
return ret;
bool fast_clean)
unsigned int i, count;
for (i = 0; i < count; i++) {
#ifdef DEBUG
unsigned int length)