mbox-sync.c revision f12aabfeba81f0d741971d2b7e4a5008eb4383ca
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo 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:
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen - Start reading the mails from the beginning
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen - X-Keywords, X-UID and X-IMAPbase headers may contain padding at the end
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen of them, remember how much each message has and offset to beginning of the
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen - If header needs to be rewritten and there's enough space, do it
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen - If we didn't have enough space, remember how much was missing
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen - Continue reading and counting the padding in each message. If available
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen padding is enough to rewrite all the previous messages needing it, do it
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen - When we encounter expunged message, treat all of it as padding and
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen rewrite previous messages if needed (and there's enough space).
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen Afterwards keep moving messages backwards to fill the expunged space.
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen Moving is done by rewriting each message's headers, with possibly adding
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen missing Content-Length header and padding. Message bodies are moved
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen without modifications.
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen - If we encounter end of file, grow the file and rewrite needed messages
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen - Rewriting is done by moving message body forward, rewriting message's
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen header and doing the same for previous message, until all of them are
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen/* The text below was taken exactly as c-client wrote it to my mailbox,
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen so it's probably copyrighted by University of Washington. */
f7d43647acc6dc80064c8c4cacf5bf86f754c530Timo Sirainen"This text is part of the internal format of your mail folder, and is not\n" \
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen"a real message. It is created automatically by the mail system software.\n" \
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen"If deleted, important folder data will be lost, and it will be re-created\n" \
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen"with the data reset to initial values.\n"
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainenvoid mbox_sync_set_critical(struct mbox_sync_context *sync_ctx,
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen const char *fmt, ...)
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen "mbox file %s was modified while we were syncing, "
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen "check your locking settings", sync_ctx->mbox->path);
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainenint mbox_sync_seek(struct mbox_sync_context *sync_ctx, uoff_t from_offset)
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen if (istream_raw_mbox_seek(sync_ctx->input, from_offset) < 0) {
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen "Unexpectedly lost From-line at offset %"PRIuUOFF_T
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainenvoid mbox_sync_file_update_ext_modified(struct mbox_sync_context *sync_ctx)
299183fbb6ec5d0828a0880da372540421ac4665Timo Sirainen /* Do this even if ext_modified is already set. Expunging code relies
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen on last_stat being updated. */
299183fbb6ec5d0828a0880da372540421ac4665Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
299183fbb6ec5d0828a0880da372540421ac4665Timo Sirainen if (st.st_size != sync_ctx->last_stat.st_size ||
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen /* nanoseconds give better precision to this check if they're
299183fbb6ec5d0828a0880da372540421ac4665Timo Sirainen supported by the OS */
299183fbb6ec5d0828a0880da372540421ac4665Timo Sirainen || st.st_mtim.tv_nsec != sync_ctx->last_stat.st_mtim.tv_nsec
299183fbb6ec5d0828a0880da372540421ac4665Timo Sirainenvoid mbox_sync_file_updated(struct mbox_sync_context *sync_ctx, bool dirty)
299183fbb6ec5d0828a0880da372540421ac4665Timo Sirainen /* just mark the stat as dirty. */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (fstat(sync_ctx->write_fd, &sync_ctx->last_stat) < 0)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "fstat()");
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void mbox_sync_array_delete_to(ARRAY_TYPE(sync_recs) *syncs_arr,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen syncs = array_get_modifiable(syncs_arr, &count);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* keep it */
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainenmbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
1e76a5b92f9d82d557f81f080f3dfad1c9d8f200Timo Sirainen /* get EOF */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input);
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen istream_raw_mbox_get_header_offset(sync_ctx->input);
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx);
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen i_assert(sync_ctx->input->v_offset != mail_ctx->mail.from_offset ||
022412398e56a8f31ef111cfd7271498d64af9a9Timo Sirainen istream_raw_mbox_get_body_size(sync_ctx->input,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen i_assert(mail_ctx->mail.body_size < OFF_T_MAX);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if ((mail_ctx->mail.flags & MAIL_RECENT) != 0 &&
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* need to add 'O' flag to Status-header */
f6699a08521aacc4c2bb5b6175691dad5f715f8cTimo Sirainenstatic bool mbox_sync_buf_have_expunges(ARRAY_TYPE(sync_recs) *syncs_arr)
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen unsigned int i, count;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for (i = 0; i < count; i++) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (syncs[i].type == MAIL_INDEX_SYNC_TYPE_EXPUNGE)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic int mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx,
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen struct mail_index_sync_rec *sync_rec = &sync_ctx->sync_rec;
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen /* nothing for this or the future ones */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen mbox_sync_array_delete_to(&sync_ctx->syncs, uid);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sync_rec->type != MAIL_INDEX_SYNC_TYPE_APPEND &&
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen (sync_rec->type != MAIL_INDEX_SYNC_TYPE_EXPUNGE ||
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen ret = mail_index_sync_next(sync_ctx->index_sync_ctx, sync_rec);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen /* we're not going to write these yet */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen *sync_expunge_r = mbox_sync_buf_have_expunges(&sync_ctx->syncs);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenvoid mbox_sync_apply_index_syncs(struct mbox_sync_context *sync_ctx,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen unsigned int i, count;
61f5256ef248d35459b53534ae428bf6d016e1c5Timo Sirainen for (i = 0; i < count; i++) {
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen mail_index_sync_flags_apply(&syncs[i], &mail->flags);
a0b0d629931773c17a236f6214adbe0e13b9b3fdTimo Sirainen /* no existing keywords */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* adding, create the array */
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainenmbox_sync_read_index_rec(struct mbox_sync_context *sync_ctx,
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen uint32_t uid, const struct mail_index_record **rec_r)
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen mail_index_view_get_messages_count(sync_ctx->sync_view);
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen i_assert(ret != 0); /* we should be looking at head index */
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen /* externally expunged message, remove from index */
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen if (rec == NULL && uid < sync_ctx->idx_next_uid) {
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen /* this UID was already in index and it was expunged */
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen "mbox sync: Expunged message reappeared in mailbox %s "
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen "(UID %u < %u, seq=%u, idx_msgs=%u)",
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen sync_ctx->mbox->path, uid, sync_ctx->idx_next_uid,
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen /* new UID in the middle of the mailbox - shouldn't happen */
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen "mbox sync: UID inserted in the middle of mailbox %s "
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen "(%u > %u, seq=%u, idx_msgs=%u)", sync_ctx->mbox->path,
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen rec->uid, uid, sync_ctx->seq, messages_count);
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainenstatic int mbox_sync_find_index_md5(struct mbox_sync_context *sync_ctx,
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen unsigned char hdr_md5_sum[],
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen mail_index_view_get_messages_count(sync_ctx->sync_view);
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (mail_index_lookup_ext(sync_ctx->sync_view,
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen if (data != NULL && memcmp(data, hdr_md5_sum, 16) == 0)
d0d7fcf3ce44f26fdf34c1542a25cec644c5c4c7Timo Sirainen /* externally expunged message, remove from index */
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainenmbox_sync_update_from_offset(struct mbox_sync_context *sync_ctx,
3f26c5aced2e71efc783f26bb8a7ac53f7504622Timo Sirainen /* see if from_offset needs updating */
3f26c5aced2e71efc783f26bb8a7ac53f7504622Timo Sirainen if (mail_index_lookup_ext(sync_ctx->sync_view,
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen *((const uint64_t *)data) == mail->from_offset)
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenmbox_sync_update_index_keywords(struct mbox_sync_mail_context *mail_ctx)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
c84985f66c5c83db642d136aefa8864cb45158cfTimo Sirainen keywords = !array_is_created(&mail_ctx->mail.keywords) ?
c84985f66c5c83db642d136aefa8864cb45158cfTimo Sirainen mail_index_keywords_create(sync_ctx->t, NULL) :
c84985f66c5c83db642d136aefa8864cb45158cfTimo Sirainen mail_index_keywords_create_from_indexes(sync_ctx->t,
a8b8cce8569753ed47c94782283a24fb11c5ab52Timo Sirainen mail_index_update_keywords(sync_ctx->t, sync_ctx->idx_seq,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenmbox_sync_update_md5_if_changed(struct mbox_sync_mail_context *mail_ctx)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (mail_index_lookup_ext(sync_ctx->sync_view, sync_ctx->idx_seq,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen memcmp(mail_ctx->hdr_md5_sum, ext_data, 16) != 0) {
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenstatic int mbox_sync_update_index(struct mbox_sync_mail_context *mail_ctx,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen struct mbox_sync_mail *mail = &mail_ctx->mail;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen struct mailbox *box = &sync_ctx->mbox->ibox.box;
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen /* new message */
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen mail_index_append(sync_ctx->t, mail->uid, &sync_ctx->idx_seq);
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq,
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen mail_index_update_ext(sync_ctx->t, sync_ctx->idx_seq,
16aba431c576c1dbd99cbaae4f9d65eea9ad73c2Timo Sirainen /* see if we need to update flags in index file. the flags in
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen sync records are automatically applied to rec->flags at the
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen end of index syncing, so calculate those new flags first */
16aba431c576c1dbd99cbaae4f9d65eea9ad73c2Timo Sirainen /* get old keywords */
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen if (mail_index_lookup_keywords(sync_ctx->sync_view,
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen mbox_sync_apply_index_syncs(sync_ctx, &idx_mail,
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen if (sync_type != 0 && box->v.sync_notify != NULL)
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen#define SYNC_FLAGS (MAIL_RECENT | MAIL_INDEX_MAIL_FLAG_DIRTY)
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen if ((idx_mail.flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen /* flags are dirty. ignore whatever was in the mbox,
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen but update recent/dirty flag states if needed. */
299183fbb6ec5d0828a0880da372540421ac4665Timo Sirainen /* keep index's internal flags */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* flags other than recent/dirty have changed */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* drop recent flag (it can only be dropped) */
8907d617ce7c4f390c0f42f6f694db2fecdd5775Timo Sirainen /* dirty flag state changed */
1e76a5b92f9d82d557f81f080f3dfad1c9d8f200Timo Sirainen if ((idx_mail.flags & MAIL_INDEX_MAIL_FLAG_DIRTY) == 0 &&
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* see if we need to update md5 sum. */
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen if (mbox_sync_update_md5_if_changed(mail_ctx) < 0)
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen (rec == NULL || (rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) == 0 ||
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen index_mailbox_set_recent(&sync_ctx->mbox->ibox,
c35d0c3eb4ba89432896e1f9770ab31cc63c232bTimo Sirainen /* update from_offsets, but not if we're going to rewrite this message.
1e76a5b92f9d82d557f81f080f3dfad1c9d8f200Timo Sirainen rewriting would just move it anyway. */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen bool nocheck = rec == NULL || sync_ctx->expunged_space > 0;
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen if (mbox_sync_update_from_offset(sync_ctx, mail, nocheck) < 0)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic int mbox_read_from_line(struct mbox_sync_mail_context *ctx)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct istream *input = ctx->sync_ctx->file_input;
c35d0c3eb4ba89432896e1f9770ab31cc63c232bTimo Sirainen const unsigned char *data;
c35d0c3eb4ba89432896e1f9770ab31cc63c232bTimo Sirainen buffer_set_used_size(ctx->sync_ctx->from_line, 0);
2be66b9eddad3841a1195fe9aeb1eaf0f28f1116Timo Sirainen from_line_size = ctx->hdr_offset - ctx->mail.from_offset;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen buffer_append(ctx->sync_ctx->from_line, data, size);
7fd72a47d7ddfbd38c8697e228b6951f495dfb61Timo Sirainenstatic int mbox_rewrite_base_uid_last(struct mbox_sync_context *sync_ctx)
c35d0c3eb4ba89432896e1f9770ab31cc63c232bTimo Sirainen const char *str;
7fd72a47d7ddfbd38c8697e228b6951f495dfb61Timo Sirainen unsigned int i;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_assert(sync_ctx->base_uid_last_offset != 0);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* first check that the 10 bytes are there and they're exactly as
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen expected. just an extra safety check to make sure we never write
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen to wrong location in the mbox file. */
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen ret = pread_full(sync_ctx->write_fd, buf, sizeof(buf),
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "pread_full()");
c35d0c3eb4ba89432896e1f9770ab31cc63c232bTimo Sirainen "X-IMAPbase uid-last unexpectedly points outside "
7fd72a47d7ddfbd38c8697e228b6951f495dfb61Timo Sirainen for (i = 0, uid_last = 0; i < sizeof(buf); i++) {
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen "X-IMAPbase uid-last unexpectedly lost in mbox file %s",
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen /* and write it */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen str = t_strdup_printf("%010u", sync_ctx->next_uid - 1);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()");
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sync_ctx->base_uid_last = sync_ctx->next_uid - 1;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenmbox_write_from_line(struct mbox_sync_mail_context *ctx)
36816b5af1472ae76a1909ae3cf29fd614b2ebfcTimo Sirainen if (pwrite_full(ctx->sync_ctx->write_fd, str_data(str), str_len(str),
36816b5af1472ae76a1909ae3cf29fd614b2ebfcTimo Sirainen mbox_set_syscall_error(ctx->sync_ctx->mbox, "pwrite_full()");
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void update_from_offsets(struct mbox_sync_context *sync_ctx)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen unsigned int i, count;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for (i = 0; i < count; i++) {
4214b59ac7f3899f8d887d055ef519f5a622d249Timo Sirainen if (mails[i].idx_seq == 0 || mails[i].expunged)
2cb565cd978aafd5714792b5161889986d49e431Timo Sirainen mail_index_update_ext(sync_ctx->t, mails[i].idx_seq,
5486e1e123484f3a8d7d895e8ac41156f2a96305Timo Sirainenstatic void mbox_sync_handle_expunge(struct mbox_sync_mail_context *mail_ctx)
5486e1e123484f3a8d7d895e8ac41156f2a96305Timo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
5486e1e123484f3a8d7d895e8ac41156f2a96305Timo Sirainen struct mailbox *box = &sync_ctx->mbox->ibox.box;
dc8552739fa29f011ab71ec383ec6d580a5a9661Timo Sirainen mail_ctx->mail.offset = mail_ctx->mail.from_offset;
dc8552739fa29f011ab71ec383ec6d580a5a9661Timo Sirainen mail_ctx->body_offset - mail_ctx->mail.from_offset +
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen /* expunging first message, fix space to contain next
4214b59ac7f3899f8d887d055ef519f5a622d249Timo Sirainen message's \n header too since it will be removed. */
4214b59ac7f3899f8d887d055ef519f5a622d249Timo Sirainen if (istream_raw_mbox_has_crlf_ending(sync_ctx->input)) {
4214b59ac7f3899f8d887d055ef519f5a622d249Timo Sirainen /* uid-last offset is invalid now */
930dcf1576f99057ad572420d9c75f3212e46a2eTimo Sirainen sync_ctx->expunged_space += mail_ctx->mail.space;
930dcf1576f99057ad572420d9c75f3212e46a2eTimo Sirainenstatic int mbox_sync_handle_header(struct mbox_sync_mail_context *mail_ctx)
829c036d4ddfbd9ea49bd8a7c54e3057177d346eTimo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen if (sync_ctx->expunged_space > 0 && sync_ctx->need_space_seq == 0) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* move the header backwards to fill expunged space */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen orig_from_offset = mail_ctx->mail.from_offset;
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen /* we're moving this mail to beginning of file.
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen skip the initial \n (it's already counted in
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen expunged_space) */
a614397cf1a4dde152eb1a38493a6ec3d817da16Timo Sirainen /* read the From-line before rewriting overwrites it */
8907d617ce7c4f390c0f42f6f694db2fecdd5775Timo Sirainen ret = mbox_sync_try_rewrite(mail_ctx, move_diff);
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen /* rewrite successful, write From-line to
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen new location */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* didn't have enough space, move the offset
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen back so seeking into it doesn't fail */
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen mail_ctx->mail.from_offset = orig_from_offset;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen /* mark it dirty and do it later */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if ((ret = mbox_sync_try_rewrite(mail_ctx, 0)) < 0)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* nothing to do */
f12aabfeba81f0d741971d2b7e4a5008eb4383caTimo Sirainen if (ret == 0 && sync_ctx->need_space_seq == 0) {
f12aabfeba81f0d741971d2b7e4a5008eb4383caTimo Sirainen /* first mail with no space to write it */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* create dummy message to describe the expunged data */
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen sync_ctx->space_diff = sync_ctx->expunged_space;
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen i_assert(sync_ctx->space_diff < -mail_ctx->mail.space);
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainenmbox_sync_handle_missing_space(struct mbox_sync_mail_context *mail_ctx)
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen uoff_t end_offset, move_diff, extra_space, needed_space;
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen i_assert(mail_ctx->mail.uid == 0 || mail_ctx->mail.space > 0 ||
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen mail_ctx->mail.offset == mail_ctx->hdr_offset);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (array_is_created(&mail_ctx->mail.keywords)) {
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* mail's keywords are allocated from a pool that's cleared
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen for each mail. we'll need to copy it to something more
a10e5606a9e93f49cf13b3a35c8dc3f5d6ab5909Timo Sirainen permanent. */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen p_array_init(&keywords_copy, sync_ctx->saved_keywords_pool,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen array_append_array(&keywords_copy, &mail_ctx->mail.keywords);
3da614c39dd29f536c485089e67839b4cf89fed3Timo Sirainen array_append(&sync_ctx->mails, &mail_ctx->mail, 1);
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen /* we have enough space now */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* this message was expunged. fill more or less of the space.
0a6f8311541ae59381171620b77f82be58be562eTimo Sirainen space_diff now consists of a negative "bytes needed" sum,
0a6f8311541ae59381171620b77f82be58be562eTimo Sirainen plus the expunged space of this message. so it contains how
a10e5606a9e93f49cf13b3a35c8dc3f5d6ab5909Timo Sirainen many bytes of _extra_ space we have. */
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen i_assert(mail_ctx->mail.space >= sync_ctx->space_diff);
0a6f8311541ae59381171620b77f82be58be562eTimo Sirainen (sync_ctx->seq - sync_ctx->need_space_seq + 1);
0a6f8311541ae59381171620b77f82be58be562eTimo Sirainen needed_space = mail_ctx->mail.space - sync_ctx->space_diff;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if ((uoff_t)sync_ctx->space_diff > needed_space + extra_space) {
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* don't waste too much on padding */
022412398e56a8f31ef111cfd7271498d64af9a9Timo Sirainen /* this message gave enough space from headers. rewriting stops
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen at the end of this message's headers. */
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen /* mail_ctx may contain wrong data after rewrite, so make sure we
022412398e56a8f31ef111cfd7271498d64af9a9Timo Sirainen don't try to access it */
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainenmbox_sync_seek_to_seq(struct mbox_sync_context *sync_ctx, uint32_t seq)
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen if (istream_raw_mbox_seek(mbox->mbox_stream, 0) < 0) {
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen mail_storage_set_error(&mbox->storage->storage,
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen "Mailbox isn't a valid mbox file");
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen old_offset = istream_raw_mbox_get_start_offset(sync_ctx->input);
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen ret = mbox_file_seek(mbox, sync_ctx->sync_view, seq, &deleted);
f6699a08521aacc4c2bb5b6175691dad5f715f8cTimo Sirainen "Message was expunged unexpectedly "
f6699a08521aacc4c2bb5b6175691dad5f715f8cTimo Sirainen "Error seeking back to original "
f6699a08521aacc4c2bb5b6175691dad5f715f8cTimo Sirainen "offset %s in mbox file %s",
d31c4d7c161f9d7efa59964c7c958e83e05b218cTimo Sirainen else if (mail_index_lookup_uid(sync_ctx->sync_view, seq-1, &uid) < 0) {
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen /* set to -1, since it's always increased later */
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen istream_raw_mbox_get_start_offset(sync_ctx->input) != 0) {
a486ed03dce069ff60ab5a65d0ae24a1862f22fcTimo Sirainen /* this mbox has pseudo mail which contains the X-IMAP header */
a486ed03dce069ff60ab5a65d0ae24a1862f22fcTimo Sirainen sync_ctx->dest_first_mail = sync_ctx->seq == 0;
a486ed03dce069ff60ab5a65d0ae24a1862f22fcTimo Sirainen (void)istream_raw_mbox_get_body_offset(sync_ctx->input);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenmbox_sync_seek_to_uid(struct mbox_sync_context *sync_ctx, uint32_t uid)
f6699a08521aacc4c2bb5b6175691dad5f715f8cTimo Sirainen struct mail_index_view *sync_view = sync_ctx->sync_view;
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen if (mail_index_lookup_uid_range(sync_view, uid, (uint32_t)-1,
912e870dcab2dadac8a6b20b42367999cdd8b5a1Timo Sirainen mail_storage_set_index_error(&sync_ctx->mbox->ibox);
b8765f6093ab35fc2345293d78132d35794cbff5Timo Sirainen /* doesn't exist anymore, seek to end of file */
a2637488c8d514ec1ac3914811deee814f9761b3Timo Sirainen st = i_stream_stat(sync_ctx->file_input, TRUE);
b8765f6093ab35fc2345293d78132d35794cbff5Timo Sirainen "i_stream_stat()");
d31c4d7c161f9d7efa59964c7c958e83e05b218cTimo Sirainen if (istream_raw_mbox_seek(sync_ctx->mbox->mbox_stream,
1e76a5b92f9d82d557f81f080f3dfad1c9d8f200Timo Sirainen "Error seeking to end of mbox file %s",
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen mail_index_view_get_messages_count(sync_view) + 1;
cdaf255d6a3daeef0ac85edaa60bfa6d1f945bffTimo Sirainenstatic int mbox_sync_partial_seek_next(struct mbox_sync_context *sync_ctx,
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* delete sync records up to next message. so if there's still
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen something left in array, it means the next message needs modifying */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen mbox_sync_array_delete_to(&sync_ctx->syncs, next_uid);
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen /* we can skip forward to next record which needs updating. */
b08b33d1f5ce3721dc2d83586c9cb0ca141331fdTimo Sirainen ret = mbox_sync_seek_to_uid(sync_ctx, next_uid);
c9fe52d819c608b890620f7fe36ff509b14eb350Timo Sirainen /* if there's no sync records left, we can stop. except if
1e76a5b92f9d82d557f81f080f3dfad1c9d8f200Timo Sirainen this is a dirty sync, check if there are new messages. */
c9fe52d819c608b890620f7fe36ff509b14eb350Timo Sirainen mail_index_view_get_messages_count(sync_ctx->sync_view);
e9d68b41c007f0e545de361f8012f6f231bfec8bTimo Sirainen ret = mbox_sync_seek_to_seq(sync_ctx, messages_count);
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen /* seek failed because the offset is dirty. just ignore and
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen continue from where we are now. */
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainenstatic int mbox_sync_loop(struct mbox_sync_context *sync_ctx,
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen mail_index_view_get_messages_count(sync_ctx->sync_view);
b428c5993b01f4cffacdd24377ca2873fcc12184Timo Sirainen /* always start from first message so we can read X-IMAP or
d0e5f8252516c4d4df2fbcdae4d37f9e5f931199Timo Sirainen X-IMAPbase header */
8907d617ce7c4f390c0f42f6f694db2fecdd5775Timo Sirainen /* expunge everything */
1cd97699af9c77d8f5920832ec3374884544fd68Timo Sirainen while ((ret = mbox_sync_read_next_mail(sync_ctx, mail_ctx)) > 0) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (mail_ctx->seq == 1 && sync_ctx->base_uid_validity != 0 &&
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "UIDVALIDITY changed (%u -> %u) "
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "in mbox file %s",
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen mail_index_mark_corrupted(sync_ctx->mbox->ibox.index);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen /* UID ordering problems, resync everything to make
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen sure we get everything right */
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen "UIDs broken with partial sync in mbox file %s",
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen ret = mbox_sync_read_index_rec(sync_ctx, uid, &rec);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* UID found but it's broken */
d31c4d7c161f9d7efa59964c7c958e83e05b218cTimo Sirainen } else if (uid == 0 &&
d31c4d7c161f9d7efa59964c7c958e83e05b218cTimo Sirainen /* If we can't use/store X-UID header, use MD5 sum.
d31c4d7c161f9d7efa59964c7c958e83e05b218cTimo Sirainen Also check for existing MD5 sums when we're actually
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen able to write X-UIDs. */
9d2040fbb941f411d57fd850b4cdc3b1cccc1168Timo Sirainen /* get all sync records related to this message. with pseudo
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen message just get the first sync record so we can jump to
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen it with partial seeking. */
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen /* if it was set, it was for the next message */
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen /* message wasn't found from index. we have to
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen read everything from now on, no skipping */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* missing/broken X-UID. all the rest of the mails
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen need new UIDs. */
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen /* oh no, we're out of UIDs. this shouldn't
2be66b9eddad3841a1195fe9aeb1eaf0f28f1116Timo Sirainen happen normally, so just try to get it fixed
2be66b9eddad3841a1195fe9aeb1eaf0f28f1116Timo Sirainen without crashing. */
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen "Out of UIDs, renumbering them in mbox "
b225c3c65f360d7b833f09f9b2fb3035ed5ea600Timo Sirainen if (mbox_sync_update_index(mail_ctx, rec) < 0)
09b9cbde9e7a0f9adea1fb054a7c62f35ad901e1Timo Sirainen offset = istream_raw_mbox_get_start_offset(sync_ctx->input);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mbox_sync_handle_missing_space(mail_ctx) < 0)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* move the body */
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen } else if (partial) {
b225c3c65f360d7b833f09f9b2fb3035ed5ea600Timo Sirainen ret = mbox_sync_partial_seek_next(sync_ctx, uid + 1,
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen if (istream_raw_mbox_is_eof(sync_ctx->input)) {
f4b93a46e140823a64d88763ea6ef9f03c49844eTimo Sirainen /* rest of the messages in index don't exist -> expunge them */
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq++);
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen /* once we get around to writing the changes, we'll need to do
343a527f805ca5cce78496b959d6def70e5d0cd4Timo Sirainen a full sync to avoid the "UIDs broken in partial sync"
b225c3c65f360d7b833f09f9b2fb3035ed5ea600Timo Sirainenstatic int mbox_write_pseudo(struct mbox_sync_context *sync_ctx)
b397a802ec245a9169dab6b62efa4f7f877c07f6Timo Sirainen uid_validity = sync_ctx->base_uid_validity != 0 ?
b397a802ec245a9169dab6b62efa4f7f877c07f6Timo Sirainen sync_ctx->base_uid_validity : sync_ctx->hdr->uid_validity;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "From: Mail System Internal Data <MAILER-DAEMON@%s>\n"
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA"
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "\nMessage-ID: <%s@%s>\n"
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "X-IMAP: %u %010u\n"
b225c3c65f360d7b833f09f9b2fb3035ed5ea600Timo Sirainen "Status: RO\n"
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mbox_from_create("MAILER_DAEMON", ioloop_time),
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen my_hostname, dec2str(ioloop_time), my_hostname,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "pwrite_full()");
af6d4a24cb6d18e50d172540cf49b1448a6f9872Timo Sirainen /* out of disk space, truncate to empty */
b225c3c65f360d7b833f09f9b2fb3035ed5ea600Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "ftruncate()");
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen sync_ctx->base_uid_last_offset = 0; /* don't bother calculating */
b225c3c65f360d7b833f09f9b2fb3035ed5ea600Timo Sirainen sync_ctx->base_uid_last = sync_ctx->next_uid-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int mbox_sync_handle_eof_updates(struct mbox_sync_context *sync_ctx,
e05ea8311ae16687295048e88ca205dfe29fbcbfTimo Sirainen uoff_t file_size, offset, padding, trailer_size;
e05ea8311ae16687295048e88ca205dfe29fbcbfTimo Sirainen if (!istream_raw_mbox_is_eof(sync_ctx->input)) {
e05ea8311ae16687295048e88ca205dfe29fbcbfTimo Sirainen st = i_stream_stat(sync_ctx->file_input, TRUE);
e05ea8311ae16687295048e88ca205dfe29fbcbfTimo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()");
e05ea8311ae16687295048e88ca205dfe29fbcbfTimo Sirainen if (file_size < sync_ctx->file_input->v_offset) {
e05ea8311ae16687295048e88ca205dfe29fbcbfTimo Sirainen "file size unexpectedly shrinked in mbox file %s "
e05ea8311ae16687295048e88ca205dfe29fbcbfTimo Sirainen trailer_size = file_size - sync_ctx->file_input->v_offset;
dc42ce2d44e84d9d05a9310c11f8764f319eb3abTimo Sirainen (sync_ctx->seq - sync_ctx->need_space_seq + 1);
06fc140d5f0b03524e63a15d45d1cdc8b691372cTimo Sirainen i_assert(sync_ctx->expunged_space <= -sync_ctx->space_diff);
06fc140d5f0b03524e63a15d45d1cdc8b691372cTimo Sirainen sync_ctx->space_diff += sync_ctx->expunged_space;
178511b57faa7c3f8203dd8b7e4059d00cbfc23aTimo Sirainen "file_set_size()");
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen if (ftruncate(sync_ctx->write_fd, file_size) < 0) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "ftruncate()");
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (mbox_sync_rewrite(sync_ctx, mail_ctx, file_size,
ed14c2db5436d6d89aa65bc8e09af0c316359553Timo Sirainen /* copy trailer, then truncate the file */
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen if (file_size == (uoff_t)sync_ctx->expunged_space) {
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen /* everything deleted, the trailer_size still contains
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen the \n trailer though */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen i_assert(file_size >= sync_ctx->expunged_space + trailer_size);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen offset = file_size - sync_ctx->expunged_space - trailer_size;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "ftruncate()");
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainenstatic int mbox_sync_update_index_header(struct mbox_sync_context *sync_ctx)
c263e92d73889da530b308c9ab28b4b74031550eTimo Sirainen st = i_stream_stat(sync_ctx->file_input, FALSE);
c263e92d73889da530b308c9ab28b4b74031550eTimo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()");
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen ((uint64_t)st->st_size == sync_ctx->hdr->sync_size ||
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen (uint64_t)st->st_size == sync_ctx->orig_size)) {
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* We moved messages inside the mbox file without changing
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen the file's size. If mtime doesn't change, another process
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen not using the same index file as us can't know that the file
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen was changed. So make sure the mtime changes. This should
b225c3c65f360d7b833f09f9b2fb3035ed5ea600Timo Sirainen happen rarely enough that the sleeping doesn't become a
a486ed03dce069ff60ab5a65d0ae24a1862f22fcTimo Sirainen performance problem.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen Note that to do this perfectly safe we should do this wait
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen whenever mails are moved or expunged, regardless of whether
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen the file's size changed. That however could become a
34d2ee1fa2b299267fcefd378f80690e7f601dfbTimo Sirainen performance problem and the consequences of being wrong are
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen quite minimal (an extra logged error message). */
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen while (sync_ctx->orig_mtime == st->st_mtime) {
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen st = i_stream_stat(sync_ctx->file_input, FALSE);
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen "i_stream_stat()");
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen /* only reason not to have UID validity at this point is if the file
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen is entirely empty. In that case just make up a new one if needed. */
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen i_assert(sync_ctx->base_uid_validity != 0 || st->st_size == 0);
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen if (sync_ctx->base_uid_validity != sync_ctx->hdr->uid_validity ||
84e1634acc701d14e358e27f1beff5ad74f5004aTimo Sirainen (unsigned int)ioloop_time;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen offsetof(struct mail_index_header, uid_validity),
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (istream_raw_mbox_is_eof(sync_ctx->input) &&
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen sync_ctx->next_uid != sync_ctx->hdr->next_uid) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen &sync_ctx->next_uid, sizeof(sync_ctx->next_uid), FALSE);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if ((uint32_t)st->st_mtime != sync_ctx->hdr->sync_stamp &&
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen offsetof(struct mail_index_header, sync_stamp),
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen if ((uint64_t)st->st_size != sync_ctx->hdr->sync_size &&
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen offsetof(struct mail_index_header, sync_size),
32d9a75612a5df455e4169b56538bb31dfe359e4Timo Sirainen sync_ctx->mbox->mbox_dirty_stamp = st->st_mtime;
32d9a75612a5df455e4169b56538bb31dfe359e4Timo Sirainen sync_ctx->mbox->mbox_dirty_size = st->st_size;
32d9a75612a5df455e4169b56538bb31dfe359e4Timo Sirainenstatic void mbox_sync_restart(struct mbox_sync_context *sync_ctx)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen memset(&sync_ctx->sync_rec, 0, sizeof(sync_ctx->sync_rec));
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen mail_index_sync_reset(sync_ctx->index_sync_ctx);
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen sync_ctx->idx_next_uid = sync_ctx->hdr->next_uid;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstatic int mbox_sync_do(struct mbox_sync_context *sync_ctx,
e05ea8311ae16687295048e88ca205dfe29fbcbfTimo Sirainen unsigned int i;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen st = i_stream_stat(sync_ctx->file_input, FALSE);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox_set_syscall_error(sync_ctx->mbox, "i_stream_stat()");
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen /* forcing a full sync. assume file has changed. */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen } else if ((uint32_t)st->st_mtime == sync_ctx->hdr->sync_stamp &&
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen (uint64_t)st->st_size == sync_ctx->hdr->sync_size) {
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen /* file is fully synced */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen } else if ((flags & MBOX_SYNC_UNDIRTY) != 0 ||
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen (uint64_t)st->st_size == sync_ctx->hdr->sync_size) {
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen /* we want to do full syncing. always do this if
aa0647f2debf0d48d504a321186f66c85596aaf4Timo Sirainen file size hasn't changed but timestamp has. it most
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen likely means that someone had modified some header
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen and we probably want to know about it */
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen /* see if we can delay syncing the whole file.
7c6ad06e2bed011e3c0425bf1ceb5df3dc33b638Timo Sirainen normally we only notice expunges and appends
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen in partial syncing. */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen for (i = 0; i < 3; i++) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ret = mbox_sync_loop(sync_ctx, &mail_ctx, partial);
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen /* partial syncing didn't work, do it again. we get here
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen also if we ran out of UIDs. */
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen if (mbox_sync_handle_eof_updates(sync_ctx, &mail_ctx) < 0)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* only syncs left should be just appends (and their updates)
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen which weren't synced yet for some reason (crash). we'll just
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen ignore them, as we've overwritten them above. */
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen memset(&sync_ctx->sync_rec, 0, sizeof(sync_ctx->sync_rec));
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen if (mbox_sync_update_index_header(sync_ctx) < 0)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenint mbox_sync_has_changed(struct mbox_mailbox *mbox, bool leave_dirty)
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen if (mbox->mbox_file_stream != NULL && mbox->mbox_fd == -1) {
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen /* read-only stream */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen st = i_stream_stat(mbox->mbox_file_stream, FALSE);
30975737820f5855e2c26d81b574ae5f03a05407Timo Sirainen mbox_set_syscall_error(mbox, "i_stream_stat()");
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen if ((uint32_t)st->st_mtime == hdr->sync_stamp &&
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen /* fully synced */
d6a1fa1d65c6d1996937802c2482c0f14dd821a7Timo Sirainen return st->st_mtime != mbox->mbox_dirty_stamp ||
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainenstatic void mbox_sync_context_free(struct mbox_sync_context *sync_ctx)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mail_index_sync_rollback(&sync_ctx->index_sync_ctx);
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainenstatic int mbox_sync_int(struct mbox_mailbox *mbox, enum mbox_sync_flags flags)
4d10cf8c7879ccd377e7fb136913b2a258ba8d93Timo Sirainen unsigned int lock_id = 0;
343a527f805ca5cce78496b959d6def70e5d0cd4Timo Sirainen bool leave_dirty = (flags & MBOX_SYNC_UNDIRTY) == 0;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen if ((changed = mbox_sync_has_changed(mbox, leave_dirty)) < 0) {
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen /* we just want to lock it for reading. if mbox hasn't been
022412398e56a8f31ef111cfd7271498d64af9a9Timo Sirainen modified don't do any syncing. */
022412398e56a8f31ef111cfd7271498d64af9a9Timo Sirainen /* have to sync to make sure offsets have stayed the same */
022412398e56a8f31ef111cfd7271498d64af9a9Timo Sirainen /* reopen input stream to make sure it has nothing buffered */
022412398e56a8f31ef111cfd7271498d64af9a9Timo Sirainen /* we're most likely modifying the mbox while syncing, just
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen lock it for writing immediately. the mbox must be locked
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen before index syncing is started to avoid deadlocks, so we
7e0bb2b365cc38645313a3513fa79bd32d34b63cTimo Sirainen don't have much choice either (well, easy ones anyway). */
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen int lock_type = mbox->mbox_readonly ? F_RDLCK : F_WRLCK;
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen if (mbox_lock(mbox, lock_type, &lock_id) <= 0)
ccb70ccfd9a25e490aab46d15d9b8323ad9ea3bfTimo Sirainen ret = mail_index_sync_begin(mbox->ibox.index, &index_sync_ctx,
aa0647f2debf0d48d504a321186f66c85596aaf4Timo Sirainen if (!changed && !mail_index_sync_have_more(index_sync_ctx)) {
aa0647f2debf0d48d504a321186f66c85596aaf4Timo Sirainen /* nothing to do */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* index may need to do internal syncing though, so commit
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen instead of rollbacking. */
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen if (mail_index_sync_commit(&index_sync_ctx) < 0) {
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen sync_ctx.hdr = mail_index_get_header(sync_view);
bfea67960b2c2b479f9869b7df9a0d179370ce5fTimo Sirainen sync_ctx.from_line = str_new(default_pool, 256);
bfea67960b2c2b479f9869b7df9a0d179370ce5fTimo Sirainen sync_ctx.header = str_new(default_pool, 4096);
9d0e1fa945103f2875cdf6d18b2013809f566ea7Timo Sirainen pool_alloconly_create("mbox saved keywords", 4096);
9d0e1fa945103f2875cdf6d18b2013809f566ea7Timo Sirainen /* make sure we've read the latest keywords in index */
9d0e1fa945103f2875cdf6d18b2013809f566ea7Timo Sirainen (void)mail_index_get_keywords(mbox->ibox.index);
181c1aff950e6f8e0556f8974e79d0747845ac0fTimo Sirainen sync_ctx.delay_writes = delay_writes || sync_ctx.mbox->mbox_readonly;
181c1aff950e6f8e0556f8974e79d0747845ac0fTimo Sirainen /* if we have only flag changes, we don't need to open the
181c1aff950e6f8e0556f8974e79d0747845ac0fTimo Sirainen if (mbox_sync_read_index_syncs(&sync_ctx, 1, &expunged) < 0)
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen /* ok, we have something to do but no locks. we'll have to
2c7ab05ef98c46eb70c8ba6ea85e49749aafb2a3Timo Sirainen restart syncing to avoid deadlocking. */
4b41116563110d00330896a568eff1078c382827Timo Sirainen sync_ctx.file_input = sync_ctx.mbox->mbox_file_stream;
4b41116563110d00330896a568eff1078c382827Timo Sirainen sync_ctx.write_fd = sync_ctx.mbox->mbox_lock_type != F_WRLCK ? -1 :
4b41116563110d00330896a568eff1078c382827Timo Sirainen else if (mail_index_sync_commit(&index_sync_ctx) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (sync_ctx.base_uid_last != sync_ctx.next_uid-1 &&
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen /* Rewrite uid_last in X-IMAPbase header if we've seen it
b7ad29db8cf906da01ee4626730868881087950bTimo Sirainen (ie. the file isn't empty) */
95ed89440faab05cbb4f2473f2f4af19e848bde8Timo Sirainen /* drop to read lock */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen unsigned int read_lock_id = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mbox_lock(mbox, F_RDLCK, &read_lock_id) <= 0)
return ret;
int ret;
return ret;
struct mailbox_sync_context *
int ret = 0;
ioloop_time) {