mail-index-sync-update.c revision 6ef7e31619edfaa17ed044b45861d106a86191ef
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2004 Timo Sirainen */
a0d34d3982507f513a9d800082481e9faeb9a943Timo Sirainenvoid mail_index_sync_replace_map(struct mail_index_sync_map_ctx *ctx,
d7095f3a4466fbb78b2d5eb3d322bc15a5b0ab1fTimo Sirainen /* if map still exists after this, it's only in views. */
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen mail_index_unmap(view->index, view->index->map);
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen if (ctx->type == MAIL_INDEX_SYNC_HANDLER_INDEX) {
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainenmail_index_header_update_counts(struct mail_index *index,
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen if (((old_flags ^ new_flags) & MAIL_RECENT) != 0) {
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen /* different recent-flag */
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen hdr->recent_messages_count > hdr->messages_count) {
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen "Recent counter wrong in index file %s",
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen hdr->first_recent_uid_lowwater = hdr->next_uid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (((old_flags ^ new_flags) & MAIL_SEEN) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* different seen-flag */
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen else if (hdr->seen_messages_count >= hdr->messages_count) {
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen "Seen counter wrong in index file %s",
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen } else if (++hdr->seen_messages_count == hdr->messages_count)
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen hdr->first_unseen_uid_lowwater = hdr->next_uid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (((old_flags ^ new_flags) & MAIL_DELETED) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* different deleted-flag */
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen hdr->deleted_messages_count > hdr->messages_count) {
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen "Deleted counter wrong in index file %s",
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen } else if (--hdr->deleted_messages_count == 0)
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen hdr->first_deleted_uid_lowwater = hdr->next_uid;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenmail_index_header_update_lowwaters(struct mail_index_header *hdr,
cd5ee8630497fdbd853ef588a858b4ef619a5e03Timo Sirainenstatic int sync_expunge(const struct mail_transaction_expunge *e,
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen const struct mail_index_expunge_handler *expunge_handlers, *eh;
03dbd273251103f745c08966f1809c02870390ffTimo Sirainen mail_transaction_log_view_set_corrupted(ctx->view->log_view,
03dbd273251103f745c08966f1809c02870390ffTimo Sirainen "Invalid UID range in expunge (%u .. %u)",
d7095f3a4466fbb78b2d5eb3d322bc15a5b0ab1fTimo Sirainen if (!view->map->write_to_disk || view->map->refcount != 1) {
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen /* expunges have to be atomic. so we'll have to copy
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen the mapping, do the changes there and then finally
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen replace the whole index file. to avoid extra disk
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen I/O we copy the index into memory rather than to
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen temporary file */
d7095f3a4466fbb78b2d5eb3d322bc15a5b0ab1fTimo Sirainen map = mail_index_map_clone(map, map->hdr.record_size);
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen /* we want atomic rename()ing */
567e57b09a49bbb2a146b13f8617698eb56237feTimo Sirainen if (mail_index_lookup_uid_range(view, e->uid1, e->uid2,
3393dabdd2123699241b26b3f07345b6842d9d75Timo Sirainen /* don't call expunge handlers if we're syncing view */
7889c9f65e23c83fc31cecf304cab4ab070d6aa1Timo Sirainen if (ctx->type != MAIL_INDEX_SYNC_HANDLER_VIEW &&
553dd384ceb91847ea10de023f026ebfdc2d782dTimo Sirainen if (ctx->type != MAIL_INDEX_SYNC_HANDLER_VIEW &&
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen expunge_handlers = array_get(&ctx->expunge_handlers,
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen mail_index_header_update_counts(view->index, &map->hdr,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen for (i = 0; i < expunge_handlers_count; i++) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* @UNSAFE */
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen memmove(MAIL_INDEX_MAP_IDX(map, seq1-1), MAIL_INDEX_MAP_IDX(map, seq2),
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen (map->records_count - seq2) * map->hdr.record_size);
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen buffer_set_used_size(map->buffer, map->records_count *
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen map->records = buffer_get_modifyable_data(map->buffer, NULL);
cd5ee8630497fdbd853ef588a858b4ef619a5e03Timo Sirainenstatic int sync_append(const struct mail_index_record *rec,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen mail_transaction_log_view_set_corrupted(view->log_view,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen "Append with UID %u, but next_uid = %u",
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen i_assert(map->records_count * map->hdr.record_size ==
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen dest = buffer_append_space_unsafe(map->buffer,
ace3c14e47a5a865df8aeea2fabc993b609dd163Timo Sirainen map->records = buffer_get_modifyable_data(map->buffer, NULL);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen i_assert((map->records_count+1) * map->hdr.record_size <=
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen dest = MAIL_INDEX_MAP_IDX(map, map->records_count);
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen map->write_seq_first = map->hdr.messages_count;
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen map->write_seq_last = map->hdr.messages_count;
dd8de60250511cc729b67249e61dfc6b4debff11Timo Sirainen if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen mail_index_header_update_counts(view->index, &map->hdr, 0, rec->flags);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen mail_index_header_update_lowwaters(&map->hdr, rec);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenstatic int sync_flag_update(const struct mail_transaction_flag_update *u,
03dbd273251103f745c08966f1809c02870390ffTimo Sirainen mail_transaction_log_view_set_corrupted(ctx->view->log_view,
03dbd273251103f745c08966f1809c02870390ffTimo Sirainen "Invalid UID range in flag update (%u .. %u)",
567e57b09a49bbb2a146b13f8617698eb56237feTimo Sirainen if (mail_index_lookup_uid_range(view, u->uid1, u->uid2,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if ((u->add_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen rec->flags = (rec->flags & flag_mask) | u->add_flags;
e12648867876aaec17e06ee4caef0bb60363449dTimo Sirainen mail_index_header_update_counts(view->index, hdr,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenstatic int sync_header_update(const struct mail_transaction_header_update *u,
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen u->offset + u->size > map->hdr.base_header_size) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen mail_transaction_log_view_set_corrupted(ctx->view->log_view,
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen "Header update outside range: %u + %u > %u",
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen u->offset, u->size, map->hdr.base_header_size);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen buffer_write(map->hdr_copy_buf, u->offset, u + 1, u->size);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen /* @UNSAFE */
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen if ((uint32_t)(u->offset + u->size) <= sizeof(map->hdr)) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenstatic int mail_index_grow(struct mail_index *index, struct mail_index_map *map,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen unsigned int count)
321241fc56909ccb69d77c6a11686e0c25b4c4d4Timo Sirainen i_assert(!index->mapping); /* mail_index_sync_from_transactions() */
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen (map->records_count + count) * map->hdr.record_size;
690af4a90eaf8611c2573d34126bb7a852c50a44Timo Sirainen /* when we grow fast, do it exponentially */
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen (map->records_count + count) * map->hdr.record_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (file_set_size(index->fd, (off_t)size) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return mail_index_set_syscall_error(index, "file_set_size()");
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen /* we only wish to grow the file, but mail_index_map() updates the
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen headers as well and may break our modified hdr_copy. so, take
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen a backup of it and put it back afterwards */
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen i_assert(map->hdr_copy_buf->used == map->hdr.header_size);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen memcpy(hdr_copy, map->hdr_copy_buf->data, hdr_copy_size);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen memcpy(hdr_copy, &map->hdr, sizeof(map->hdr));
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen buffer_append(map->hdr_copy_buf, hdr_copy, hdr_copy_size);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen memcpy(&map->hdr, hdr_copy, sizeof(map->hdr));
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainenmail_index_update_day_headers(struct mail_index_header *hdr, uint32_t uid)
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen sizeof(hdr->day_first_uid) / sizeof(hdr->day_first_uid[0]);
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen /* get beginning of today */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen /* get number of days since last message */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen /* @UNSAFE: move days forward and fill the missing days with old
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen day_first_uid[0]. */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen hdr->day_first_uid[i] = hdr->day_first_uid[0];
d143077bd518de129b8d446fb58e003903e50867Timo Sirainenint mail_index_sync_record(struct mail_index_sync_map_ctx *ctx,
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: {
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen const struct mail_transaction_expunge *rec, *end;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen const struct mail_transaction_flag_update *rec, *end;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen const struct mail_transaction_header_update *rec;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen unsigned int i;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen if ((i % 4) != 0)
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen const struct mail_transaction_ext_intro *rec = data;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen unsigned int i;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen /* should be just extra padding */
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen if ((i % 4) != 0)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen const struct mail_transaction_ext_reset *rec = data;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen const struct mail_transaction_ext_hdr_update *rec = data;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen unsigned int i;
a0d34d3982507f513a9d800082481e9faeb9a943Timo Sirainen ret = mail_index_sync_ext_hdr_update(ctx, rec);
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen if ((i % 4) != 0)
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen const struct mail_transaction_ext_rec_update *rec, *end;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen "Extension record update update "
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen "without intro prefix");
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen ext = array_idx(&ctx->view->map->extensions, ctx->cur_ext_id);
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen record_size = sizeof(*rec) + ext->record_size;
a0d34d3982507f513a9d800082481e9faeb9a943Timo Sirainen ret = mail_index_sync_ext_rec_update(ctx, rec);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen const struct mail_transaction_keyword_update *rec = data;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen ret = mail_index_sync_keywords(ctx, hdr, rec);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen const struct mail_transaction_keyword_reset *rec = data;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen ret = mail_index_sync_keywords_reset(ctx, hdr, rec);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenvoid mail_index_sync_map_init(struct mail_index_sync_map_ctx *sync_map_ctx,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen memset(sync_map_ctx, 0, sizeof(*sync_map_ctx));
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainenvoid mail_index_sync_map_deinit(struct mail_index_sync_map_ctx *sync_map_ctx)
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen mail_index_sync_deinit_expunge_handlers(sync_map_ctx);
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen mail_index_sync_deinit_handlers(sync_map_ctx);
6aa38bc4f4d4626a4a79abb59a9d415e4bd9b6b5Timo Sirainenstatic void mail_index_sync_remove_recent(struct mail_index_sync_ctx *sync_ctx)
6aa38bc4f4d4626a4a79abb59a9d415e4bd9b6b5Timo Sirainen struct mail_index_map *map = sync_ctx->view->map;
6aa38bc4f4d4626a4a79abb59a9d415e4bd9b6b5Timo Sirainen unsigned int i;
4cf90a2ba0a57a115d1e2c416df5c0d47bdcf8c3Timo Sirainen map->hdr.first_recent_uid_lowwater = map->hdr.next_uid;
2a734f36105e33ab452d057df6bc7a2b7d9f96f0Timo Sirainenint mail_index_sync_update_index(struct mail_index_sync_ctx *sync_ctx,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct mail_index_view *view = sync_ctx->view;
fec0e90484c5f2c9da9cdc62c0897408023d4c6eTimo Sirainen /* we'll have to update view->lock_id to avoid mail_index_view_lock()
fec0e90484c5f2c9da9cdc62c0897408023d4c6eTimo Sirainen trying to update the file later. */
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen if (mail_index_lock_exclusive(index, &view->lock_id) < 0)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* NOTE: locking may change index->map so make sure the assignment is
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen after locking */
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen if (map->hdr_base != map->hdr_copy_buf->data) {
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen buffer_append(map->hdr_copy_buf, map->hdr_base,
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen i_assert(map->hdr.base_header_size >= sizeof(map->hdr));
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen had_dirty = (map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0;
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen map->hdr.flags &= ~MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
6aa38bc4f4d4626a4a79abb59a9d415e4bd9b6b5Timo Sirainen /* mark all messages non-recent */
b9ce555e8624a5593b3bfd81b572b7d2e1e1fca5Timo Sirainen /* make sure we don't go doing fsck while modifying the index */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen while ((ret = mail_transaction_log_view_next(view->log_view, &thdr,
2a734f36105e33ab452d057df6bc7a2b7d9f96f0Timo Sirainen if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
2a734f36105e33ab452d057df6bc7a2b7d9f96f0Timo Sirainen /* we're syncing only external changes. */
2a734f36105e33ab452d057df6bc7a2b7d9f96f0Timo Sirainen mail_transaction_log_view_get_prev_pos(view->log_view,
2a734f36105e33ab452d057df6bc7a2b7d9f96f0Timo Sirainen /* we have already synced this change */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if ((thdr->type & MAIL_TRANSACTION_APPEND) != 0) {
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen if (mail_index_sync_record(&sync_map_ctx, thdr, data) < 0) {
fbd3abcd7b5cf11362440fcb1c73d77960227ba4Timo Sirainen /* mail_index_sync_record() might have changed map to anything.
fbd3abcd7b5cf11362440fcb1c73d77960227ba4Timo Sirainen make sure we don't accidentally try to use it. */
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen i_assert(map->records_count == map->hdr.messages_count);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen mail_transaction_log_get_head(index->log, &seq, &offset);
463e82bdf0e990f4f2252d2b53ea23a5abe5883cTimo Sirainen /* log sequence changed. update internal offset to
463e82bdf0e990f4f2252d2b53ea23a5abe5883cTimo Sirainen beginning of the new file. */
463e82bdf0e990f4f2252d2b53ea23a5abe5883cTimo Sirainen map->hdr.log_file_int_offset = index->log->head->hdr.hdr_size;
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen mail_index_update_day_headers(&map->hdr, first_append_uid);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen if ((map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) == 0 &&
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen /* do we have dirty flags anymore? */
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr));
b2105c78f0fd58281317e6d777ded860f33153a3Timo Sirainen map->mmap_used_size = index->hdr->header_size +
b2105c78f0fd58281317e6d777ded860f33153a3Timo Sirainen if (msync(map->mmap_base, map->mmap_used_size, MS_SYNC) < 0) {