mail-index-sync-update.c revision ccffbed92cb02c24fd717808a84138240bf1885b
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2004 Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_index_sync_update_log_offset(struct mail_index_sync_map_ctx *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_transaction_log_view_get_prev_pos(ctx->view->log_view,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (prev_offset == ctx->ext_intro_offset + ctx->ext_intro_size &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* previous transaction was an extension introduction.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen we probably came here from mail_index_sync_ext_reset().
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if there are any more views which want to continue syncing
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen it needs the intro. so back up a bit more.
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen don't do this in case the last transaction in the log is
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen the extension intro, so we don't keep trying to sync it
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen over and over again. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(prev_offset >= map->hdr.log_file_int_offset ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (map->hdr.log_file_seq != prev_seq) {
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen /* log sequence changed. update internal offset to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen beginning of the new file. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->view->index->log->head->hdr.prev_file_offset);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* we might be in the middle of syncing internal transactions, with
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen some of the following external transactions already synced. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen prev_offset >= map->hdr.log_file_ext_offset ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_index_map_msync(struct mail_index *index, struct mail_index_map *map)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr));
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen map->mmap_used_size = index->hdr->header_size +
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (msync(map->mmap_base, map->mmap_used_size, MS_SYNC) < 0) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_index_set_syscall_error(index, "msync()");
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenvoid mail_index_sync_replace_map(struct mail_index_sync_map_ctx *ctx,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* if map still exists after this, it's only in views. */
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* keywords aren't parsed for the new map yet */
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if ((ctx->type & (MAIL_INDEX_SYNC_HANDLER_FILE |
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_index_unmap(view->index, &view->index->map);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ctx->type == MAIL_INDEX_SYNC_HANDLER_FILE) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* some views may still use the same mapping, and since we could have
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen already updated the records, make sure we leave the header in a
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen valid state as well */
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen mail_index_sync_update_log_offset(ctx, old_map, FALSE);
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen (void)mail_index_map_msync(view->index, old_map);
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen i_assert(view->hdr.messages_count == map->hdr.messages_count);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainenmail_index_header_update_counts(struct mail_index_header *hdr,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **error_r)
37e6cf44d61a81c6839e3ab76234b54309d8d292Timo Sirainen if (((old_flags ^ new_flags) & MAIL_RECENT) != 0) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* different recent-flag */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (hdr->recent_messages_count > hdr->messages_count) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hdr->recent_messages_count > hdr->messages_count) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hdr->first_recent_uid_lowwater = hdr->next_uid;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (((old_flags ^ new_flags) & MAIL_SEEN) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* different seen-flag */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (hdr->seen_messages_count >= hdr->messages_count) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (++hdr->seen_messages_count == hdr->messages_count)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen hdr->first_unseen_uid_lowwater = hdr->next_uid;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (((old_flags ^ new_flags) & MAIL_DELETED) != 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* different deleted-flag */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (hdr->deleted_messages_count > hdr->messages_count) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hdr->deleted_messages_count > hdr->messages_count) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hdr->first_deleted_uid_lowwater = hdr->next_uid;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainenvoid mail_index_view_recalc_counters(struct mail_index_view *view)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int i;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < view->hdr.messages_count; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mail_index_header_update_counts(&map->hdr, 0, rec->flags,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_panic("mail_index_view_recalc_counters(): %s", error);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen view->hdr.recent_messages_count = map->hdr.recent_messages_count;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen view->hdr.seen_messages_count = map->hdr.seen_messages_count;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen view->hdr.deleted_messages_count = map->hdr.deleted_messages_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_index_header_update_lowwaters(struct mail_index_header *hdr,
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainenstatic int sync_expunge(const struct mail_transaction_expunge *e,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen const struct mail_index_expunge_handler *expunge_handlers, *eh;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Invalid UID range in expunge (%u .. %u)",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!view->map->write_to_disk || view->map->refcount != 1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* expunges have to be atomic. so we'll have to copy
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen the mapping, do the changes there and then finally
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen replace the whole index file. to avoid extra disk
befeac661293b8d4206118ac50b8be9751df8424Timo Sirainen I/O we copy the index into memory rather than to
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen temporary file */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen map = mail_index_map_clone(map, map->hdr.record_size);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* we want atomic rename()ing */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mail_index_lookup_uid_range(view, e->uid1, e->uid2,
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen /* call expunge handlers only when syncing index file */
69bd816e46fdee6182d0cb2e4c6be32399a555c8Timo Sirainen if (ctx->type == MAIL_INDEX_SYNC_HANDLER_FILE &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ctx->type == MAIL_INDEX_SYNC_HANDLER_FILE &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen expunge_handlers = array_get(&ctx->expunge_handlers,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ctx->unreliable_flags || view->broken_counters)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mail_index_header_update_counts(&map->hdr,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_sync_set_corrupted(ctx, "%s", error);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen for (i = 0; i < expunge_handlers_count; i++) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* @UNSAFE */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen memmove(MAIL_INDEX_MAP_IDX(map, seq1-1), MAIL_INDEX_MAP_IDX(map, seq2),
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen (map->records_count - seq2) * map->hdr.record_size);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen buffer_set_used_size(map->buffer, map->records_count *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen map->records = buffer_get_modifiable_data(map->buffer, NULL);
a53cb86b4d733d9c48ee4d285bed477c80825804Timo Sirainenstatic void write_seq_update(struct mail_index_map *map,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int sync_append(const struct mail_index_record *rec,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen "Append with UID %u, but next_uid = %u",
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen i_assert(map->records_count * map->hdr.record_size ==
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen dest = buffer_append_space_unsafe(map->buffer,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen map->records = buffer_get_modifiable_data(map->buffer, NULL);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen i_assert((map->records_count+1) * map->hdr.record_size <=
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dest = MAIL_INDEX_MAP_IDX(map, map->records_count);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen write_seq_update(map, map->hdr.messages_count, map->hdr.messages_count);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_header_update_lowwaters(&map->hdr, rec);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (mail_index_header_update_counts(&map->hdr, 0, rec->flags,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen mail_index_sync_set_corrupted(ctx, "%s", error);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenstatic int sync_flag_update(const struct mail_transaction_flag_update *u,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen "Invalid UID range in flag update (%u .. %u)",
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (mail_index_lookup_uid_range(view, u->uid1, u->uid2,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((u->add_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (MAIL_SEEN | MAIL_DELETED | MAIL_RECENT)) == 0) {
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen /* we're not modifying any counted/lowwatered flags */
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen rec->flags = (rec->flags & flag_mask) | u->add_flags;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (view->broken_counters || ctx->unreliable_flags) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen rec->flags = (rec->flags & flag_mask) | u->add_flags;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rec->flags = (rec->flags & flag_mask) | u->add_flags;
&error) < 0) {
unsigned int count)
void *hdr_copy;
t_push();
t_pop();
t_pop();
const int max_days =
int i, days;
const void *data)
int ret = 0;
t_push();
case MAIL_TRANSACTION_APPEND: {
if (ret <= 0)
case MAIL_TRANSACTION_EXPUNGE:
if (ret <= 0)
case MAIL_TRANSACTION_FLAG_UPDATE: {
if (ret <= 0)
case MAIL_TRANSACTION_HEADER_UPDATE: {
if (ret <= 0)
case MAIL_TRANSACTION_EXT_INTRO: {
if (ret <= 0)
case MAIL_TRANSACTION_EXT_RESET: {
case MAIL_TRANSACTION_EXT_HDR_UPDATE: {
if (ret <= 0)
case MAIL_TRANSACTION_EXT_REC_UPDATE: {
unsigned int record_size;
if (ret <= 0)
case MAIL_TRANSACTION_KEYWORD_UPDATE: {
case MAIL_TRANSACTION_KEYWORD_RESET: {
i_unreached();
t_pop();
return ret;
&prev_offset);
bool sync_only_external)
const void *data;
int ret;
if (had_dirty)
first_append_uid = 0;
if (sync_only_external) {
} else if (check_ext_offsets) {
if (first_append_uid == 0)
if (ret == 0) {
if (ret == 0) {
TRUE);
if (ret < 0) {
if (first_append_uid != 0)
had_dirty) {
return ret;