Mercurial > dovecot > core-2.2
view src/lib-index/mail-index-sync.c @ 2373:9c830d1a4ebf HEAD
Fixed recent flags with mbox.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 26 Jul 2004 21:52:06 +0300 |
parents | c68a3c9f6d73 |
children | d598c82a3c22 |
line wrap: on
line source
/* Copyright (C) 2003-2004 Timo Sirainen */ #include "lib.h" #include "buffer.h" #include "mail-index-view-private.h" #include "mail-index-sync-private.h" #include "mail-transaction-log.h" #include "mail-transaction-util.h" #include "mail-cache.h" #include <stdlib.h> static void mail_index_sync_sort_flags(buffer_t *dest_buf, const struct mail_transaction_flag_update *src, size_t src_size) { const struct mail_transaction_flag_update *src_end; struct mail_transaction_flag_update *dest; struct mail_transaction_flag_update new_update, tmp_update; size_t i, dest_count; int j; dest = buffer_get_modifyable_data(dest_buf, &dest_count); dest_count /= sizeof(*dest); if (dest_count == 0) { buffer_append(dest_buf, src, src_size); return; } src_end = PTR_OFFSET(src, src_size); for (i = 0; src != src_end; src++) { new_update = *src; /* insert it into buffer, split and merge it with existing updates if needed. */ for (; i < dest_count; i++) { if (new_update.uid1 > dest[i].uid2) continue; if (new_update.uid2 < dest[i].uid1) break; /* at least partially overlapping */ if (new_update.uid1 < dest[i].uid1) { /* { 5..6 } + { 1..5 } -> { 1..4 } + { 5..6 } */ tmp_update = new_update; tmp_update.uid2 = dest[i].uid1-1; new_update.uid1 = dest[i].uid1; buffer_insert(dest_buf, i * sizeof(tmp_update), &tmp_update, sizeof(tmp_update)); dest = buffer_get_modifyable_data(dest_buf, NULL); dest_count++; i++; } else if (new_update.uid1 > dest[i].uid1) { /* { 5..7 } + { 6..6 } -> split old to { 5..5 } + { 6..7 } */ tmp_update = dest[i]; tmp_update.uid2 = new_update.uid1-1; dest[i].uid1 = new_update.uid1; buffer_insert(dest_buf, i * sizeof(tmp_update), &tmp_update, sizeof(tmp_update)); dest = buffer_get_modifyable_data(dest_buf, NULL); dest_count++; i++; } i_assert(new_update.uid1 == dest[i].uid1); if (new_update.uid2 < dest[i].uid2) { /* { 5..7 } + { 5..6 } -> { 5..6 } + { 7..7 } */ tmp_update = dest[i]; tmp_update.uid1 = new_update.uid2+1; dest[i].uid2 = new_update.uid2; buffer_insert(dest_buf, (i+1) * sizeof(tmp_update), &tmp_update, sizeof(tmp_update)); dest = buffer_get_modifyable_data(dest_buf, NULL); dest_count++; new_update.uid2 = 0; } else { /* full match, or continues. */ new_update.uid1 = dest[i].uid2+1; } /* dest[i] now contains the overlapping area. merge them - new_update overrides old changes. */ dest[i].add_flags |= new_update.add_flags; dest[i].add_flags &= ~new_update.remove_flags; dest[i].remove_flags |= new_update.remove_flags; dest[i].remove_flags &= ~new_update.add_flags; for (j = 0; j < INDEX_KEYWORDS_BYTE_COUNT; j++) { dest[i].add_keywords[j] |= new_update.add_keywords[j]; dest[i].add_keywords[j] &= ~new_update.remove_keywords[j]; dest[i].remove_keywords[j] |= new_update.remove_keywords[j]; dest[i].remove_keywords[j] &= ~new_update.add_keywords[j]; } } if (new_update.uid1 <= new_update.uid2) { buffer_insert(dest_buf, i * sizeof(new_update), &new_update, sizeof(new_update)); dest = buffer_get_modifyable_data(dest_buf, NULL); dest_count++; } } } static void mail_index_sync_sort_transaction(struct mail_index_sync_ctx *ctx) { switch (ctx->hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE: mail_transaction_log_sort_expunges(ctx->expunges_buf, ctx->data, ctx->hdr->size); break; case MAIL_TRANSACTION_FLAG_UPDATE: mail_index_sync_sort_flags(ctx->updates_buf, ctx->data, ctx->hdr->size); break; case MAIL_TRANSACTION_APPEND: { const struct mail_transaction_append_header *hdr = ctx->data; const struct mail_index_record *rec = ctx->data; if (ctx->append_uid_first == 0 || rec->uid < ctx->append_uid_first) ctx->append_uid_first = rec->uid; rec = CONST_PTR_OFFSET(ctx->data, ctx->hdr->size - hdr->record_size); if (rec->uid > ctx->append_uid_last) ctx->append_uid_last = rec->uid; ctx->sync_appends = TRUE; break; } } } static int mail_index_sync_add_dirty_updates(struct mail_index_sync_ctx *ctx) { struct mail_transaction_flag_update update; const struct mail_index_record *rec; uint32_t seq, messages_count; int i; memset(&update, 0, sizeof(update)); messages_count = mail_index_view_get_message_count(ctx->view); for (seq = 1; seq <= messages_count; seq++) { if (mail_index_lookup(ctx->view, seq, &rec) < 0) return -1; if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) == 0) continue; update.uid1 = update.uid2 = rec->uid; update.add_flags = rec->flags; update.remove_flags = ~update.add_flags; memcpy(update.add_keywords, rec->keywords, INDEX_KEYWORDS_BYTE_COUNT); for (i = 0; i < INDEX_KEYWORDS_BYTE_COUNT; i++) update.remove_keywords[i] = ~update.add_keywords[i]; mail_index_sync_sort_flags(ctx->updates_buf, &update, sizeof(update)); } return 0; } static int mail_index_sync_add_recent_updates(struct mail_index_sync_ctx *ctx) { struct mail_transaction_flag_update update; const struct mail_index_record *rec; uint32_t seq, messages_count; memset(&update, 0, sizeof(update)); messages_count = mail_index_view_get_message_count(ctx->view); for (seq = 1; seq <= messages_count; seq++) { if (mail_index_lookup(ctx->view, seq, &rec) < 0) return -1; if ((rec->flags & MAIL_RECENT) == 0) continue; update.uid1 = update.uid2 = rec->uid; mail_index_sync_sort_flags(ctx->updates_buf, &update, sizeof(update)); } return 0; } static int mail_index_sync_read_and_sort(struct mail_index_sync_ctx *ctx, int sync_recent) { size_t size; int ret; if (ctx->view->map->hdr->flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) { /* show dirty flags as flag updates */ if (mail_index_sync_add_dirty_updates(ctx) < 0) return -1; } if (sync_recent) { if (mail_index_sync_add_recent_updates(ctx) < 0) return -1; } while ((ret = mail_transaction_log_view_next(ctx->view->log_view, &ctx->hdr, &ctx->data, NULL)) > 0) { if ((ctx->hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) mail_index_sync_sort_transaction(ctx); } ctx->expunges = buffer_get_data(ctx->expunges_buf, &size); ctx->expunges_count = size / sizeof(*ctx->expunges); ctx->updates = buffer_get_data(ctx->updates_buf, &size); ctx->updates_count = size / sizeof(*ctx->updates); return ret; } static int mail_index_need_lock(struct mail_index *index, int sync_recent, uint32_t log_file_seq, uoff_t log_file_offset) { if (sync_recent && index->hdr->recent_messages_count > 0) return 1; if (index->hdr->log_file_seq > log_file_seq || (index->hdr->log_file_seq == log_file_seq && index->hdr->log_file_offset >= log_file_offset)) { /* already synced */ return mail_cache_need_compress(index->cache); } return 1; } int mail_index_sync_begin(struct mail_index *index, struct mail_index_sync_ctx **ctx_r, struct mail_index_view **view_r, uint32_t log_file_seq, uoff_t log_file_offset, int sync_recent) { struct mail_index_sync_ctx *ctx; uint32_t seq; uoff_t offset; unsigned int lock_id; if (mail_transaction_log_sync_lock(index->log, &seq, &offset) < 0) return -1; if (mail_index_lock_shared(index, TRUE, &lock_id) < 0) { mail_transaction_log_sync_unlock(index->log); return -1; } if (mail_index_map(index, FALSE) <= 0) { mail_transaction_log_sync_unlock(index->log); mail_index_unlock(index, lock_id); return -1; } if (!mail_index_need_lock(index, sync_recent, log_file_seq, log_file_offset)) { mail_index_unlock(index, lock_id); mail_transaction_log_sync_unlock(index->log); return 0; } ctx = i_new(struct mail_index_sync_ctx, 1); ctx->index = index; ctx->lock_id = lock_id; ctx->view = mail_index_view_open(index); ctx->view->external = TRUE; if (mail_transaction_log_view_set(ctx->view->log_view, index->hdr->log_file_seq, index->hdr->log_file_offset, seq, offset, MAIL_TRANSACTION_TYPE_MASK) < 0) { mail_index_sync_end(ctx); return -1; } /* we need to have all the transactions sorted to optimize caller's mailbox access patterns */ ctx->expunges_buf = buffer_create_dynamic(default_pool, 1024, (size_t)-1); ctx->updates_buf = buffer_create_dynamic(default_pool, 1024, (size_t)-1); if (mail_index_sync_read_and_sort(ctx, sync_recent) < 0) { mail_index_sync_end(ctx); return -1; } *ctx_r = ctx; *view_r = ctx->view; return 1; } void mail_index_sync_get_expunge(struct mail_index_sync_rec *rec, const struct mail_transaction_expunge *exp) { rec->type = MAIL_INDEX_SYNC_TYPE_EXPUNGE; rec->uid1 = exp->uid1; rec->uid2 = exp->uid2; } void mail_index_sync_get_update(struct mail_index_sync_rec *rec, const struct mail_transaction_flag_update *update) { rec->type = MAIL_INDEX_SYNC_TYPE_FLAGS; rec->uid1 = update->uid1; rec->uid2 = update->uid2; rec->add_flags = update->add_flags; memcpy(rec->add_keywords, update->add_keywords, sizeof(rec->add_keywords)); rec->remove_flags = update->remove_flags; memcpy(rec->remove_keywords, update->remove_keywords, sizeof(rec->remove_keywords)); } static int mail_index_sync_rec_check(struct mail_index_view *view, struct mail_index_sync_rec *rec) { switch (rec->type) { case MAIL_INDEX_SYNC_TYPE_EXPUNGE: case MAIL_INDEX_SYNC_TYPE_FLAGS: if (rec->uid1 > rec->uid2 || rec->uid1 == 0) { mail_transaction_log_view_set_corrupted(view->log_view, "Broken UID range: %u..%u (type 0x%x)", rec->uid1, rec->uid2, rec->type); return FALSE; } break; case MAIL_INDEX_SYNC_TYPE_APPEND: break; } return TRUE; } int mail_index_sync_next(struct mail_index_sync_ctx *ctx, struct mail_index_sync_rec *sync_rec) { const struct mail_transaction_expunge *next_exp; const struct mail_transaction_flag_update *next_update; next_exp = ctx->expunge_idx == ctx->expunges_count ? NULL : &ctx->expunges[ctx->expunge_idx]; next_update = ctx->update_idx == ctx->updates_count ? NULL : &ctx->updates[ctx->update_idx]; if (next_update != NULL && (next_exp == NULL || next_update->uid1 < next_exp->uid1)) { mail_index_sync_get_update(sync_rec, next_update); if (next_exp != NULL && next_exp->uid1 <= next_update->uid2) { /* it's overlapping with next expunge */ sync_rec->uid2 = next_exp->uid1-1; } if (sync_rec->uid1 < ctx->next_uid) { /* overlapping with previous expunge */ sync_rec->uid1 = ctx->next_uid; } i_assert(sync_rec->uid1 <= sync_rec->uid2); ctx->update_idx++; return mail_index_sync_rec_check(ctx->view, sync_rec); } if (next_exp != NULL) { mail_index_sync_get_expunge(sync_rec, next_exp); if (mail_index_sync_rec_check(ctx->view, sync_rec) < 0) return -1; ctx->expunge_idx++; ctx->next_uid = next_exp->uid2+1; return 1; } if (ctx->sync_appends) { ctx->sync_appends = FALSE; sync_rec->type = MAIL_INDEX_SYNC_TYPE_APPEND; sync_rec->uid1 = ctx->append_uid_first; sync_rec->uid2 = ctx->append_uid_last; return 1; } return 0; } int mail_index_sync_have_more(struct mail_index_sync_ctx *ctx) { return (ctx->update_idx != ctx->updates_count) || (ctx->expunge_idx != ctx->expunges_count) || ctx->sync_appends; } int mail_index_sync_end(struct mail_index_sync_ctx *ctx) { const struct mail_index_header *hdr; uint32_t seq, seq2; uoff_t offset, offset2; int ret = 0; if (mail_transaction_log_view_is_corrupted(ctx->view->log_view)) ret = -1; mail_transaction_log_get_head(ctx->index->log, &seq, &offset); if (ret == 0) { hdr = ctx->index->hdr; if (mail_transaction_log_view_set(ctx->view->log_view, hdr->log_file_seq, hdr->log_file_offset, seq, offset, MAIL_TRANSACTION_TYPE_MASK) < 0) ret = -1; else if (mail_index_sync_update_index(ctx) < 0) ret = -1; } if (ret == 0 && mail_cache_need_compress(ctx->index->cache)) { if (mail_cache_compress(ctx->index->cache, ctx->view) < 0) ret = -1; else { /* cache_offsets have changed, sync them */ mail_transaction_log_get_head(ctx->index->log, &seq2, &offset2); if (mail_transaction_log_view_set(ctx->view->log_view, seq, offset, seq2, offset2, MAIL_TRANSACTION_TYPE_MASK) < 0) ret = -1; else if (mail_index_sync_update_index(ctx) < 0) ret = -1; } } mail_index_unlock(ctx->index, ctx->lock_id); i_assert(!ctx->index->map->write_to_disk); mail_transaction_log_sync_unlock(ctx->index->log); mail_index_view_close(ctx->view); if (ctx->expunges_buf != NULL) buffer_free(ctx->expunges_buf); if (ctx->updates_buf != NULL) buffer_free(ctx->updates_buf); i_free(ctx); return ret; } void mail_index_sync_flags_apply(const struct mail_index_sync_rec *sync_rec, uint8_t *flags, keywords_mask_t keywords) { int i; i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS); *flags = (*flags & ~sync_rec->remove_flags) | sync_rec->add_flags; for (i = 0; i < INDEX_KEYWORDS_BYTE_COUNT; i++) { keywords[i] = (keywords[i] & ~sync_rec->remove_keywords[i]) | sync_rec->add_keywords[i]; } }