mail-index-sync-update.c revision 1b317fe22572738681d34d80da8012638e1e1281
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek/* Copyright (C) 2004 Timo Sirainen */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekmail_index_sync_update_log_offset(struct mail_index_sync_map_ctx *ctx,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mail_transaction_log_view_get_prev_pos(ctx->view->log_view,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (prev_offset == ctx->ext_intro_end_offset &&
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* previous transaction was an extension introduction.
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek we probably came here from
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mail_index_sync_ext_reset(). if there are any more
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek views which want to continue syncing it needs the
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek intro. so back up a bit more.
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek don't do this in case the last transaction in the
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek log is the extension intro, so we don't keep trying
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek to sync it over and over again. */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek i_assert(ctx->view->index->log->head->hdr.file_seq == prev_seq);
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek#if 0 // FIXME: can we / do we want to support this?
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekmail_index_map_msync(struct mail_index *index, struct mail_index_map *map)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek buffer_write(map->hdr_copy_buf, 0, &map->hdr, sizeof(map->hdr));
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek I_MIN(map->hdr.base_header_size, sizeof(map->hdr)));
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek memcpy(PTR_OFFSET(map->mmap_base, map->hdr.base_header_size),
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek CONST_PTR_OFFSET(map->hdr_base, map->hdr.base_header_size),
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek map->hdr.header_size - map->hdr.base_header_size);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (msync(map->mmap_base, map->mmap_used_size, MS_SYNC) < 0) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mail_index_set_syscall_error(index, "msync()");
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic void mail_index_sync_replace_map(struct mail_index_sync_map_ctx *ctx,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mail_index_sync_update_log_offset(ctx, view->map, FALSE);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#if 0 // FIXME
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov /* we could have already updated some of the records, so make sure
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek that other views (in possibly other processes) will see this map's
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek header in a valid state. */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek (void)mail_index_map_msync(view->index, view->map);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekvoid mail_index_sync_move_to_private(struct mail_index_sync_map_ctx *ctx)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekmail_index_sync_get_atomic_map(struct mail_index_sync_map_ctx *ctx)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekmail_index_header_update_counts(struct mail_index_header *hdr,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char **error_r)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (((old_flags ^ new_flags) & MAIL_RECENT) != 0) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* different recent-flag */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (hdr->recent_messages_count > hdr->messages_count) {
4cae8609b513c267af11c0409bfe1d17d3a5da2fMichal Zidek hdr->recent_messages_count > hdr->messages_count) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek hdr->first_recent_uid_lowwater = hdr->next_uid;
4cae8609b513c267af11c0409bfe1d17d3a5da2fMichal Zidek if (((old_flags ^ new_flags) & MAIL_SEEN) != 0) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* different seen-flag */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (hdr->seen_messages_count >= hdr->messages_count) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (++hdr->seen_messages_count == hdr->messages_count)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek hdr->first_unseen_uid_lowwater = hdr->next_uid;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (((old_flags ^ new_flags) & MAIL_DELETED) != 0) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* different deleted-flag */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (hdr->deleted_messages_count > hdr->messages_count) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek hdr->deleted_messages_count > hdr->messages_count) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek hdr->first_deleted_uid_lowwater = hdr->next_uid;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekmail_index_sync_header_update_counts(struct mail_index_sync_map_ctx *ctx,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (mail_index_header_update_counts(&ctx->view->map->hdr,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mail_index_sync_set_corrupted(ctx, "%s", error);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekmail_index_header_update_lowwaters(struct mail_index_header *hdr,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozeksync_expunge_call_handlers(struct mail_index_sync_map_ctx *ctx,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek unsigned int i, count;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* call expunge handlers only when syncing index file */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek eh = array_get(&ctx->expunge_handlers, &count);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek rec = MAIL_INDEX_MAP_IDX(ctx->view->map, seq1-1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozeksync_expunge(const struct mail_transaction_expunge *e, unsigned int count,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek unsigned int i;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* we don't ever want to move around data inside a memory mapped file.
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek it gets corrupted too easily if we crash in the middle. */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek // FIXME: it's necessary for current view code that we get atomic
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek // map even if these messages are already expunged, because the
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek // view code doesn't check that and our index_int_offset goes wrong
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek for (i = 0; i < count; i++, e++) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (mail_index_lookup_uid_range(ctx->view, e->uid1, e->uid2,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* everything expunged already */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (sync_expunge_call_handlers(ctx, seq1, seq2) < 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* @UNSAFE */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek (map->records_count - seq2) * map->hdr.record_size);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* lookup_uid_range() relies on this */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekvoid mail_index_sync_write_seq_update(struct mail_index_sync_map_ctx *ctx,
a7401bf72db3a6eb62b1628f9dd141f7118e3510Pavel Březinastatic int sync_append(const struct mail_index_record *rec,
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek "Append with UID %u, but next_uid = %u",
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek /* move to memory. the mapping is written when unlocking so we don't
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek waste time re-mmap()ing multiple times or waste space growing index
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek file too large */
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek /* don't rely on buffer->used being at the correct position.
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek at least expunges can move it */
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek append_pos = map->records_count * map->hdr.record_size;
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek dest = buffer_get_space_unsafe(map->buffer, append_pos,
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek map->records = buffer_get_modifiable_data(map->buffer, NULL);
3bd78eb2faf09635b8d307e4440ccb1420f80716Jakub Hrozek mail_index_sync_write_seq_update(ctx, map->hdr.messages_count,
3bd78eb2faf09635b8d307e4440ccb1420f80716Jakub Hrozek if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
3bd78eb2faf09635b8d307e4440ccb1420f80716Jakub Hrozek map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek mail_index_header_update_lowwaters(&map->hdr, rec);
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek mail_index_sync_header_update_counts(ctx, 0, rec->flags);
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozekstatic int sync_flag_update(const struct mail_transaction_flag_update *u,
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek if (mail_index_lookup_uid_range(view, u->uid1, u->uid2,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mail_index_sync_write_seq_update(ctx, seq1, seq2);
a95c006f748fa9df0dd81509b51974133d2786afLukas Slebodnik if ((u->add_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek (MAIL_SEEN | MAIL_DELETED | MAIL_RECENT)) == 0) {
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek /* we're not modifying any counted/lowwatered flags */
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek rec->flags = (rec->flags & flag_mask) | u->add_flags;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek rec->flags = (rec->flags & flag_mask) | u->add_flags;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mail_index_sync_header_update_counts(ctx, old_flags,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic int sync_header_update(const struct mail_transaction_header_update *u,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek uint32_t orig_log_file_tail_offset = map->hdr.log_file_tail_offset;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek u->offset + u->size > map->hdr.base_header_size) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek "Header update outside range: %u + %u > %u",
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek u->offset, u->size, map->hdr.base_header_size);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek buffer_write(map->hdr_copy_buf, u->offset, u + 1, u->size);
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek /* @UNSAFE */
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek if ((uint32_t)(u->offset + u->size) <= sizeof(map->hdr)) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek /* the tail offset updates are intended for internal transaction
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek log handling. we'll update the offset in the header only when
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek the sync is finished. */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek map->hdr.log_file_tail_offset = orig_log_file_tail_offset;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekint mail_index_sync_record(struct mail_index_sync_map_ctx *ctx,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const void *data)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
case MAIL_TRANSACTION_EXPUNGE:
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 i, record_size;
if (ret <= 0)
case MAIL_TRANSACTION_KEYWORD_UPDATE: {
case MAIL_TRANSACTION_KEYWORD_RESET: {
i_unreached();
t_pop();
return ret;
#ifdef DEBUG
del++;
recent++;
seen++;
const void *tdata;
int ret;
bool had_dirty;
if (!force) {
if (ret <= 0) {
if (had_dirty) {
&tdata)) > 0) {
if (had_dirty)
#ifdef DEBUG