mbox-sync.c revision a393d9d6dabdc46cf724f8cb004a652b4036d53d
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (C) 2004 Timo Sirainen */
785d9cca224d33ca3938e9166784f6483e8a27d7Timo 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:
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen - Update message flags in Status, X-Status and X-Keywords headers
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo 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
6fe91298731abf0b70dfd796ecc30d3be81fa5d1Timo Sirainen Here's how we do it:
436adac819e7cbeef04af08dcc6a4f3ecd4a1d9eMartti Rannanjärvi - 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,
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen remember how much extra each message has and offset to beginning of the
73a552a9ed06cd6017ad4ee4b252a8b38c8ac42dTimo 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
73a552a9ed06cd6017ad4ee4b252a8b38c8ac42dTimo 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
7ccdf78cd45aea9d14e048a5b9f077515c71978fTimo Sirainen missing space. If yes,
7ccdf78cd45aea9d14e048a5b9f077515c71978fTimo Sirainen - execute the rewrite plan
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen - forget all the messages before the expunged message. only remember
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen how much data we still have to move to cover the expunged message
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen - If we encounter end of file, grow the file and execute the rewrite plan
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen Rewrite plan goes:
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen - Start from the first message that needs more space
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen - If there's expunged messages before us, we have to write over them.
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen - Move all messages after it backwards to fill it
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen - Each moved message's X-Keywords header should have n bytes extra
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen space, unless there's not enough space to do it.
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen - If there's no expunged messages, we can move data either forward or
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen backward to get it. Calculate which requires less moving. Forward
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen counting may encounter more messages which require extra space, count
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen - If we decide to move forwards and we had to go through dirty
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen messages, do the moving from last to first dirty message
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen - If we encounter end of file, grow the file enough to get the required
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen amount of space plus enough space to fill X-Keywords headers full of
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainen/* returns -1 = error, 0 = mbox changed since previous lock, 1 = didn't */
e0cc94d52184944c5da76b509da184e8449b3a5cTimo Sirainenstatic int mbox_sync_lock(struct mbox_sync_context *sync_ctx, int lock_type)
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen i_assert(lock_type != F_WRLCK || !ibox->mbox_readonly);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (sync_ctx->lock_id == 0 || sync_ctx->input == NULL) {
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input);
1235bbd139a1ff97f641fa0e77205eb9adbb0400Timo Sirainen if (mbox_lock(ibox, lock_type, &sync_ctx->lock_id) <= 0)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen sync_ctx->file_input = sync_ctx->ibox->mbox_file_stream;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen sync_ctx->input = sync_ctx->ibox->mbox_stream;
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen /* we didn't have the file open before -> it changed */
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen if (st.st_mtime != old_st.st_mtime || st.st_size != old_st.st_size ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* same as before. we'll have to fix mbox stream to contain
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen correct from_offset, hdr_offset and body_offset. so, seek
825f6569a5276488133796c2f529c65128a09ba0Timo Sirainen to from_offset and read through the header. */
b0ab23e6fd425bf864cdc9fdab0631573d4d1b5fTimo Sirainen if (istream_raw_mbox_seek(sync_ctx->input, old_from_offset) < 0) {
b0ab23e6fd425bf864cdc9fdab0631573d4d1b5fTimo Sirainen "Message offset %s changed unexpectedly for mbox file "
825f6569a5276488133796c2f529c65128a09ba0Timo Sirainen "%s", dec2str(old_from_offset), sync_ctx->ibox->path);
b0ab23e6fd425bf864cdc9fdab0631573d4d1b5fTimo Sirainen (void)istream_raw_mbox_get_body_offset(sync_ctx->input);
825f6569a5276488133796c2f529c65128a09ba0Timo Sirainenint mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset)
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi if (istream_raw_mbox_seek(sync_ctx->input, from_offset) < 0) {
b0ab23e6fd425bf864cdc9fdab0631573d4d1b5fTimo Sirainen mail_storage_set_critical(sync_ctx->ibox->box.storage,
825f6569a5276488133796c2f529c65128a09ba0Timo Sirainen "Unexpectedly lost From-line at offset %"PRIuUOFF_T
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainenstatic int mbox_sync_grow_file(struct mbox_sync_context *sync_ctx,
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen /* put the extra space between last message's header and body */
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen file_size = i_stream_get_size(sync_ctx->file_input) + grow_size;
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen if (file_set_size(sync_ctx->fd, file_size) < 0) {
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen mbox_set_syscall_error(sync_ctx->ibox, "file_set_size()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mbox_move(sync_ctx, mail_ctx->body_offset, src_offset,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void mbox_sync_buffer_delete_old(buffer_t *syncs_buf, uint32_t uid)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sync = buffer_get_modifyable_data(syncs_buf, &size);
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen buffer_set_used_size(syncs_buf, dest * sizeof(*sync));
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainenmbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* get EOF */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input);
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen istream_raw_mbox_get_header_offset(sync_ctx->input);
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen if (mail_ctx->seq > 1 && sync_ctx->dest_first_mail) {
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen /* First message was expunged and this is the next one.
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen Skip \n header */
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen i_assert(sync_ctx->input->v_offset != mail_ctx->from_offset);
ce8244c05b9f89d7729a5abe6f4838c1dce46c64Timo Sirainen istream_raw_mbox_get_body_size(sync_ctx->input,
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen i_assert(mail_ctx->mail.body_size < OFF_T_MAX);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* save the offset permanently with recent flag state */
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen mail_ctx->mail.from_offset = mail_ctx->from_offset;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if ((mail_ctx->mail.flags & MBOX_NONRECENT) == 0) {
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* need to add 'O' flag to Status-header */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen // FIXME: save it somewhere
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainenstatic int mbox_sync_buf_have_expunges(buffer_t *syncs_buf)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen for (i = 0; i < size; i++) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (sync[i].type == MAIL_INDEX_SYNC_TYPE_EXPUNGE)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainenstatic int mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx,
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen struct mail_index_sync_rec *sync_rec = &sync_ctx->sync_rec;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (sync_ctx->ibox->mbox_readonly || sync_ctx->index_sync_ctx == NULL)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen /* nothing for this or the future ones */
6cfb1377b2c034cfc4bd37361cc1520f2d8acd6dTimo Sirainen mbox_sync_buffer_delete_old(sync_ctx->syncs, uid);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen sync_rec->type != MAIL_INDEX_SYNC_TYPE_APPEND) {
6cfb1377b2c034cfc4bd37361cc1520f2d8acd6dTimo Sirainen if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE)
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen ret = mail_index_sync_next(sync_ctx->index_sync_ctx, sync_rec);
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_APPEND) {
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen sync_ctx->update_base_uid_last = sync_rec->uid2;
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen *sync_expunge_r = mbox_sync_buf_have_expunges(sync_ctx->syncs);
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainenstatic void mbox_sync_apply_index_syncs(buffer_t *syncs_buf, uint8_t *flags,
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen for (i = 0; i < size; i++) {
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen if (sync[i].type != MAIL_INDEX_SYNC_TYPE_FLAGS)
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen mail_index_sync_flags_apply(&sync[i], flags, keywords);
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainenmbox_sync_read_index_rec(struct mbox_sync_context *sync_ctx,
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen uint32_t uid, const struct mail_index_record **rec_r)
if (ret < 0) {
return ret;
const void *data;
&data) < 0) {
int nocheck)
if (!nocheck) {
&offset) < 0)
INDEX_KEYWORDS_BYTE_COUNT) != 0) {
const unsigned char *data;
if (from_line_size == 0)
idx = 0;
if (idx_seq == 0) {
&offset);
int ret;
if (ret == 0)
int ret;
return ret;
int ret;
return ret;
if (ret > 0) {
return ret;
if (seq1 == 0)
if (min_message_count != 0)
ret = 0;
&expunged) < 0)
if (size == 0)
if (ret < 0)
if (ret == 0) {
if (ret < 0)
uid = 0;
if (uid == 0) {
if (!expunged) {
if (ret < 0) {
return ret;
rec) < 0)
if (!expunged) {
trailer_size) < 0)
int ret;
if (sync_header)
FALSE);
unsigned int lock_id = 0;
if (lock) {
if (sync_header)
ret = 0;
if (lock)
if (last_commit) {
if (ret <= 0) {
if (ret < 0)
return ret;
lock_id = 0;
if (ret < 0)
if (ret < 0)
&seq,
&offset) < 0) {
unsigned int lock_id = 0;
return ret;