mbox-sync.c revision e06c0b65c16ccce69bbee009ead14d7d3d17a256
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2004 Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen Modifying mbox can be slow, so we try to do it all at once minimizing the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen required disk I/O. We may need to:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Update message flags in Status, X-Status and X-Keywords headers
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Write missing X-UID and X-IMAPbase headers
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Write missing or broken Content-Length header if there's space
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Expunge specified messages
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen Here's how we do it:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Start reading the mails mail headers from the beginning
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - X-Keywords and X-UID headers may contain extra spaces at the end of them,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen remember how much extra each message has and offset to beginning of the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - If message flags are dirty and there's enough space to write them, do it
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - If we didn't have enough space, remember how much was missing and keep
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen the total amount of them
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - When we encounter expunged message, check if the amount of empty space in
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen previous messages plus size of expunged message is enough to cover the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen missing space. If yes,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - execute the rewrite plan
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - forget all the messages before the expunged message. only remember
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen how much data we still have to move to cover the expunged message
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - If we encounter end of file, grow the file and execute the rewrite plan
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen Rewrite plan goes:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Start from the first message that needs more space
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - If there's expunged messages before us, we have to write over them.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Move all messages after it backwards to fill it
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - Each moved message's X-Keywords header should have n bytes extra
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen space, unless there's not enough space to do it.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - If there's no expunged messages, we can move data either forward or
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen backward to get it. Calculate which requires less moving. Forward
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen counting may encounter more messages which require extra space, count
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - If we decide to move forwards and we had to go through dirty
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen messages, do the moving from last to first dirty message
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - If we encounter end of file, grow the file enough to get the required
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen amount of space plus enough space to fill X-Keywords headers full of
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int mbox_sync_grow_file(struct mbox_sync_context *sync_ctx,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* put the extra space between last message's header and body */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen file_size = i_stream_get_size(sync_ctx->file_input) + grow_size;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (file_set_size(sync_ctx->fd, file_size) < 0)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (mbox_move(sync_ctx, mail_ctx->body_offset, src_offset,
659fe5d24825b160cae512538088020d97a60239Timo Sirainenstatic void mbox_sync_buffer_delete_old(buffer_t *syncs_buf, uint32_t uid)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen sync = buffer_get_modifyable_data(syncs_buf, &size);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen buffer_set_used_size(syncs_buf, dest * sizeof(*sync));
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainenmbox_sync_next_mail(struct mbox_sync_context *sync_ctx,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen struct mbox_sync_mail_context *mail_ctx, uint32_t seq)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen from_offset = istream_raw_mbox_get_start_offset(sync_ctx->input);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen istream_raw_mbox_get_header_offset(sync_ctx->input);
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx, FALSE);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen i_assert(sync_ctx->input->v_offset != from_offset);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen istream_raw_mbox_get_body_size(sync_ctx->input,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* save the offset permanently with recent flag state */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if ((mail_ctx->mail.flags & MBOX_NONRECENT) == 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* need to add 'O' flag to Status-header */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen buffer_append(sync_ctx->ibox->mbox_data_buf, &from_offset,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenstatic void mbox_sync_apply_index_syncs(buffer_t *syncs_buf, uint8_t *flags,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen for (i = 0; i < size; i++)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_index_sync_flags_apply(&sync[i], flags, keywords);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenstatic int mbox_sync_do(struct index_mailbox *ibox,
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen uint32_t seq, need_space_seq, idx_seq, messages_count;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen t = mail_index_transaction_begin(sync_view, FALSE);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen buffer_create_dynamic(default_pool, 512, (size_t)-1);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen sync_ctx.header = str_new(default_pool, 4096);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen mails = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen syncs = buffer_create_dynamic(default_pool, 256, (size_t)-1);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen messages_count = mail_index_view_get_message_count(sync_view);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen space_diff = 0; need_space_seq = 0; idx_seq = 0; rec = NULL;
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen for (seq = 0;;) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* set input->eof */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen (void)istream_raw_mbox_get_header_offset(input);
659fe5d24825b160cae512538088020d97a60239Timo Sirainen mbox_sync_next_mail(&sync_ctx, &mail_ctx, seq);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* get all sync records related to this message */
659fe5d24825b160cae512538088020d97a60239Timo Sirainen mbox_sync_buffer_delete_old(syncs, mail_ctx.mail.uid);
659fe5d24825b160cae512538088020d97a60239Timo Sirainen while (mail_ctx.mail.uid >= sync_rec.uid1 && ret > 0) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen ret = mail_index_sync_next(index_sync_ctx, &sync_rec);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (seq == 1 && sync_ctx.base_uid_validity == 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (mail_index_get_header(sync_view, &hdr) < 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen sync_ctx.base_uid_validity = hdr->uid_validity;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen buffer_get_used_size(syncs) != 0) && !ibox->readonly) {
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if ((ret = mbox_sync_try_rewrite(&mail_ctx)) < 0)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* first mail with no space to write it */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* update index */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (rec != NULL && rec->uid >= mail_ctx.mail.uid)
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen ret = mail_index_lookup(sync_view, ++idx_seq, &rec);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen } while (ret == 0);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (rec != NULL && rec->uid != mail_ctx.mail.uid) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* new UID in the middle of the mailbox -
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen shouldn't happen */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen "mbox sync: UID inserted in the middle "
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen "of mailbox (%u > %u)",
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* see if flags changed */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mbox_sync_apply_index_syncs(syncs, &old_flags,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* we used this record */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* new message */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen mail_index_append(t, mail_ctx.mail.uid, &idx_seq);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen mail_index_update_flags(t, idx_seq, MODIFY_REPLACE,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen istream_raw_mbox_next(input, mail_ctx.mail.body_size);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen offset = istream_raw_mbox_get_start_offset(input);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we have enough space now */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* mail_ctx may contain wrong data after
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen rewrite, so make sure we don't try to access
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen else if (mbox_sync_try_rewrite(&mail_ctx) < 0)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (mbox_sync_rewrite(&sync_ctx, mails, need_space_seq,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (sync_ctx.base_uid_last+1 != sync_ctx.next_uid) {
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen // FIXME: rewrite X-IMAPbase header
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* only syncs left should be just appends which weren't synced
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen yet. we'll just ignore them, as we've overwritten those
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen while ((ret = mail_index_sync_next(index_sync_ctx,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen i_assert(sync_rec.type == MAIL_INDEX_SYNC_TYPE_APPEND);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (mail_index_get_header(sync_view, &hdr) < 0)
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if ((uint32_t)st.st_mtime != hdr->sync_stamp) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen offsetof(struct mail_index_header, sync_stamp),
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if ((uint64_t)st.st_mtime != hdr->sync_size) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen offsetof(struct mail_index_header, sync_size),
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (mail_index_transaction_commit(t, &seq, &offset) < 0)
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen else if (seq != 0) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen ibox->mbox_data = buffer_get_data(ibox->mbox_data_buf, &size);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen ibox->mbox_data_count = size / sizeof(*ibox->mbox_data);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenint mbox_sync(struct index_mailbox *ibox, int last_commit)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int lock_id;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen ret = mail_index_sync_begin(ibox->index, &index_sync_ctx, &sync_view,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen lock_type = mail_index_sync_have_more(index_sync_ctx) ?
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (mbox_lock(ibox, lock_type, &lock_id) > 0 &&
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen ret = mbox_sync_do(ibox, index_sync_ctx, sync_view);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* read lock -> write lock. do it again. */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenint mbox_storage_sync(struct mailbox *box, enum mailbox_sync_flags flags)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen struct index_mailbox *ibox = (struct index_mailbox *)box;