mail-index-sync-update.c revision 945631faab2bf1aed8d95a1fd0c317a9ce153725
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (C) 2004 Timo Sirainen */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenmail_index_sync_update_log_offset(struct mail_index_sync_map_ctx *ctx,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen mail_transaction_log_view_get_prev_pos(ctx->view->log_view,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen else if (map->hdr.log_file_seq != prev_seq && map == ctx->view->map) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* log sequence changed. update internal offset to
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen beginning of the new file. */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ctx->view->index->log->head->hdr.prev_file_offset);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenmail_index_map_msync(struct mail_index *index, struct mail_index_map *map)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen map->mmap_used_size = index->hdr->header_size +
a5ddfd7a8b473f73135b93d5e081e470a87f0f7eTimo Sirainen if (msync(map->mmap_base, map->mmap_used_size, MS_SYNC) < 0) {
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen mail_index_set_syscall_error(index, "msync()");
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainenvoid mail_index_sync_replace_map(struct mail_index_sync_map_ctx *ctx,
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen /* if map still exists after this, it's only in views. */
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen /* keywords aren't parsed for the new map yet */
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen if ((ctx->type & (MAIL_INDEX_SYNC_HANDLER_FILE |
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen mail_index_unmap(view->index, &view->index->map);
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen if (ctx->type == MAIL_INDEX_SYNC_HANDLER_FILE) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen /* other processes may still have the same file mapped,
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen and since we could have already updated the records, make
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen sure we leave the header in a valid state as well */
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen mail_index_sync_update_log_offset(ctx, old_map);
8b16d3b00f051401c97568697ccdbba48663759aTimo Sirainen (void)mail_index_map_msync(view->index, old_map);
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen i_assert(view->hdr.messages_count == map->hdr.messages_count);
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainenmail_index_header_update_counts(struct mail_index_header *hdr,
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen const char **error_r)
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen if (((old_flags ^ new_flags) & MAIL_RECENT) != 0) {
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen /* different recent-flag */
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen if (hdr->recent_messages_count > hdr->messages_count) {
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen hdr->recent_messages_count > hdr->messages_count) {
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen hdr->first_recent_uid_lowwater = hdr->next_uid;
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen if (((old_flags ^ new_flags) & MAIL_SEEN) != 0) {
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen /* different seen-flag */
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen if (hdr->seen_messages_count >= hdr->messages_count) {
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen if (++hdr->seen_messages_count == hdr->messages_count)
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen hdr->first_unseen_uid_lowwater = hdr->next_uid;
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen if (((old_flags ^ new_flags) & MAIL_DELETED) != 0) {
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen /* different deleted-flag */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (hdr->deleted_messages_count > hdr->messages_count) {
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen hdr->deleted_messages_count > hdr->messages_count) {
4fda77c9e9fc68feb292c4dacae1fac49dd08165Timo Sirainen hdr->first_deleted_uid_lowwater = hdr->next_uid;
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainenvoid mail_index_view_recalc_counters(struct mail_index_view *view)
4fda77c9e9fc68feb292c4dacae1fac49dd08165Timo Sirainen unsigned int i;
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen for (i = 0; i < view->hdr.messages_count; i++) {
8c2b4a45f17a5cb13bb01058ca37798cf48d91baTimo Sirainen if (mail_index_header_update_counts(&map->hdr, 0, rec->flags,
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen i_panic("mail_index_view_recalc_counters(): %s", error);
ad404d294fedf792619aed432ed8de5174e9ce7cTimo Sirainen view->hdr.recent_messages_count = map->hdr.recent_messages_count;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen view->hdr.seen_messages_count = map->hdr.seen_messages_count;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen view->hdr.deleted_messages_count = map->hdr.deleted_messages_count;
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainenmail_index_header_update_lowwaters(struct mail_index_header *hdr,
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainenstatic int sync_expunge(const struct mail_transaction_expunge *e,
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen const struct mail_index_expunge_handler *expunge_handlers, *eh;
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen "Invalid UID range in expunge (%u .. %u)",
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen if (!view->map->write_to_disk || view->map->refcount != 1) {
009217abb57a24a4076092e8e4e165545747839eStephan Bosch /* expunges have to be atomic. so we'll have to copy
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen the mapping, do the changes there and then finally
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen replace the whole index file. to avoid extra disk
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen I/O we copy the index into memory rather than to
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen temporary file */
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen map = mail_index_map_clone(map, map->hdr.record_size);
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen /* we want atomic rename()ing */
e4194f4703eeec32b432371ae30fc8f25ab720d8Timo Sirainen if (mail_index_lookup_uid_range(view, e->uid1, e->uid2,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* call expunge handlers only when syncing index file */
cf9d67e4a9bfee31cf3be05244555d51a3d1b9feTimo Sirainen if (ctx->type == MAIL_INDEX_SYNC_HANDLER_FILE &&
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen if (ctx->type == MAIL_INDEX_SYNC_HANDLER_FILE &&
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainen expunge_handlers = array_get(&ctx->expunge_handlers,
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen if (ctx->unreliable_flags || view->broken_counters)
a4502a71879d6018bd2c64f13614bb619911dd9fTimo Sirainen if (mail_index_header_update_counts(&map->hdr,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen mail_index_sync_set_corrupted(ctx, "%s", error);
a4502a71879d6018bd2c64f13614bb619911dd9fTimo Sirainen for (i = 0; i < expunge_handlers_count; i++) {
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen /* @UNSAFE */
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen memmove(MAIL_INDEX_MAP_IDX(map, seq1-1), MAIL_INDEX_MAP_IDX(map, seq2),
87842f621233257b7a7945d994ba931508b34877Timo Sirainen (map->records_count - seq2) * map->hdr.record_size);
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen buffer_set_used_size(map->buffer, map->records_count *
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen map->records = buffer_get_modifiable_data(map->buffer, NULL);
a4502a71879d6018bd2c64f13614bb619911dd9fTimo Sirainenstatic void write_seq_update(struct mail_index_map *map,
a5ddfd7a8b473f73135b93d5e081e470a87f0f7eTimo Sirainenstatic int sync_append(const struct mail_index_record *rec,
a5ddfd7a8b473f73135b93d5e081e470a87f0f7eTimo Sirainen "Append with UID %u, but next_uid = %u",
28789c7ce1aa66ab21798bfb73ec64308b9a4de8Timo Sirainen i_assert(map->records_count * map->hdr.record_size ==
28789c7ce1aa66ab21798bfb73ec64308b9a4de8Timo Sirainen dest = buffer_append_space_unsafe(map->buffer,
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen map->records = buffer_get_modifiable_data(map->buffer, NULL);
87842f621233257b7a7945d994ba931508b34877Timo Sirainen i_assert((map->records_count+1) * map->hdr.record_size <=
a5ddfd7a8b473f73135b93d5e081e470a87f0f7eTimo Sirainen dest = MAIL_INDEX_MAP_IDX(map, map->records_count);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen write_seq_update(map, map->hdr.messages_count, map->hdr.messages_count);
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen mail_index_header_update_lowwaters(&map->hdr, rec);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen if (mail_index_header_update_counts(&map->hdr, 0, rec->flags,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen mail_index_sync_set_corrupted(ctx, "%s", error);
5e9bb72de1209cd39fdf3e95bdb26e047cc5594eTimo Sirainenstatic int sync_flag_update(const struct mail_transaction_flag_update *u,
9ffd3c8ca82bd2af8ca4f7e167339820ddcf1fe3Timo Sirainen "Invalid UID range in flag update (%u .. %u)",
5e9bb72de1209cd39fdf3e95bdb26e047cc5594eTimo Sirainen if (mail_index_lookup_uid_range(view, u->uid1, u->uid2,
5e9bb72de1209cd39fdf3e95bdb26e047cc5594eTimo Sirainen if ((u->add_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen (MAIL_SEEN | MAIL_DELETED | MAIL_RECENT)) == 0) {
5e9bb72de1209cd39fdf3e95bdb26e047cc5594eTimo Sirainen /* we're not modifying any counted/lowwatered flags */
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen } else if (view->broken_counters || ctx->unreliable_flags) {
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen rec->flags = (rec->flags & flag_mask) | u->add_flags;
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen rec->flags = (rec->flags & flag_mask) | u->add_flags;
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen if (mail_index_header_update_counts(hdr, old_flags,
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen mail_index_sync_set_corrupted(ctx, "%s", error);
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainenstatic int sync_header_update(const struct mail_transaction_header_update *u,
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen u->offset + u->size > map->hdr.base_header_size) {
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen "Header update outside range: %u + %u > %u",
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen u->offset, u->size, map->hdr.base_header_size);
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen buffer_write(map->hdr_copy_buf, u->offset, u + 1, u->size);
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen /* @UNSAFE */
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen if ((uint32_t)(u->offset + u->size) <= sizeof(map->hdr)) {
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainenstatic int mail_index_grow(struct mail_index *index, struct mail_index_map *map,
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen unsigned int count)
7bccaece91a0b1bc61111c30443d6bad6b22c6d0Timo Sirainen i_assert(!index->mapping); /* mail_index_sync_from_transactions() */
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen (map->records_count + count) * map->hdr.record_size;
b2024fa4e6ed39f9b5b6bb6c051f6d535fc0e011Timo Sirainen /* when we grow fast, do it exponentially */
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainen (map->records_count + count) * map->hdr.record_size;
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen if (file_set_size(index->fd, (off_t)size) < 0)
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen return mail_index_set_syscall_error(index, "file_set_size()");
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen /* we only wish to grow the file, but mail_index_map() updates the
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen headers as well and may break our modified hdr_copy. so, take
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen a backup of it and put it back afterwards */
09060303d565e15d54e42b4ef722f9d3c26f5336Timo Sirainen i_assert(map->hdr_copy_buf->used == map->hdr.header_size);
edd318d5866ac3fbc6e8df28fb24a4dfef93c884Timo Sirainen memcpy(hdr_copy, map->hdr_copy_buf->data, hdr_copy_size);
edd318d5866ac3fbc6e8df28fb24a4dfef93c884Timo Sirainen memcpy(hdr_copy, &map->hdr, sizeof(map->hdr));
edd318d5866ac3fbc6e8df28fb24a4dfef93c884Timo Sirainen buffer_append(map->hdr_copy_buf, hdr_copy, hdr_copy_size);
edd318d5866ac3fbc6e8df28fb24a4dfef93c884Timo Sirainen memcpy(&map->hdr, hdr_copy, sizeof(map->hdr));
edd318d5866ac3fbc6e8df28fb24a4dfef93c884Timo Sirainenmail_index_update_day_headers(struct mail_index_header *hdr, uint32_t uid)
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen sizeof(hdr->day_first_uid) / sizeof(hdr->day_first_uid[0]);
caa1fa99c79c568ce2e42477bc169e7024fb220bTimo Sirainen /* get beginning of today */
caa1fa99c79c568ce2e42477bc169e7024fb220bTimo Sirainen /* get number of days since last message */
caa1fa99c79c568ce2e42477bc169e7024fb220bTimo Sirainen /* @UNSAFE: move days forward and fill the missing days with old
caa1fa99c79c568ce2e42477bc169e7024fb220bTimo Sirainen day_first_uid[0]. */
caa1fa99c79c568ce2e42477bc169e7024fb220bTimo Sirainen hdr->day_first_uid[i] = hdr->day_first_uid[0];
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenint mail_index_sync_record(struct mail_index_sync_map_ctx *ctx,
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen const struct mail_transaction_expunge *rec, *end;
15ab2452b0220a115f4351ad9d7fd5ec70ae7966Timo Sirainen const struct mail_transaction_flag_update *rec, *end;
550d2fe097e95f12e8fa60ef52753ea7fe53d4eaTimo Sirainen const struct mail_transaction_header_update *rec;
7a60e1dc9e93ef3f7c7fe1af6385a0bfa1e31bc3Timo Sirainen unsigned int i;
550d2fe097e95f12e8fa60ef52753ea7fe53d4eaTimo Sirainen if ((i % 4) != 0)
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen const struct mail_transaction_ext_intro *rec = data;
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen unsigned int i;
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen /* should be just extra padding */
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen if ((i % 4) != 0)
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen const struct mail_transaction_ext_reset *rec = data;
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen const struct mail_transaction_ext_hdr_update *rec = data;
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen unsigned int i;
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen ret = mail_index_sync_ext_hdr_update(ctx, rec);
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen if ((i % 4) != 0)
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen const struct mail_transaction_ext_rec_update *rec, *end;
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen "Extension record updated "
9ba1a38e2fa5ffe9d0db83c4a14a6552bcff3181Timo Sirainen "without intro prefix");
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ext = array_idx(&ctx->view->map->extensions, ctx->cur_ext_id);
d2cadbf5445156fc12988506279d51d0e53b0449Timo Sirainen /* the record is padded to 32bits in the transaction log */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen record_size = (sizeof(*rec) + ext->record_size + 3) & ~3;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret = mail_index_sync_ext_rec_update(ctx, rec);
3c3001681da75afc68578a180ec8f8b2d6dfacfaTimo Sirainen const struct mail_transaction_keyword_update *rec = data;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret = mail_index_sync_keywords(ctx, hdr, rec);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen const struct mail_transaction_keyword_reset *rec = data;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen ret = mail_index_sync_keywords_reset(ctx, hdr, rec);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid mail_index_sync_map_init(struct mail_index_sync_map_ctx *sync_map_ctx,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen memset(sync_map_ctx, 0, sizeof(*sync_map_ctx));
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen /* make sure we re-read it in case it has changed */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen sync_map_ctx->view->map->keywords_read = FALSE;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid mail_index_sync_map_deinit(struct mail_index_sync_map_ctx *sync_map_ctx)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen mail_index_sync_deinit_expunge_handlers(sync_map_ctx);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen mail_index_sync_deinit_handlers(sync_map_ctx);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void mail_index_sync_remove_recent(struct mail_index_sync_ctx *sync_ctx)
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) {
if (first_append_uid != 0)
had_dirty) {
return ret;