]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
major syncing code cleanups. the code finally looks almost readable. logic
authorTimo Sirainen <tss@iki.fi>
Mon, 14 Jun 2004 19:23:25 +0000 (22:23 +0300)
committerTimo Sirainen <tss@iki.fi>
Mon, 14 Jun 2004 19:23:25 +0000 (22:23 +0300)
should have stayed same.

--HG--
branch : HEAD

src/lib-storage/index/mbox/mbox-sync-private.h
src/lib-storage/index/mbox/mbox-sync-rewrite.c
src/lib-storage/index/mbox/mbox-sync-update.c
src/lib-storage/index/mbox/mbox-sync.c

index 4e3d991f1e55fcac50d8e6c30a99654590927faa..47b45ea49f35176f5ace5da5f6239028332c0bfc 100644 (file)
@@ -60,12 +60,24 @@ struct mbox_sync_mail_context {
 struct mbox_sync_context {
        struct index_mailbox *ibox;
        struct istream *input, *file_input;
+       unsigned int lock_id;
        int fd;
 
+       struct mail_index_sync_ctx *index_sync_ctx;
+       struct mail_index_view *sync_view;
+       struct mail_index_transaction *t;
+       const struct mail_index_header *hdr;
+
        string_t *header, *from_line;
        uint32_t base_uid_validity, base_uid_last;
+
+       /* state: */
+       buffer_t *mails, *syncs;
+       struct mail_index_sync_rec sync_rec;
+
        uint32_t prev_msg_uid, next_uid, first_uid;
-       off_t expunged_space;
+       uint32_t seq, idx_seq, need_space_seq;
+       off_t expunged_space, space_diff;
 };
 
 int mbox_sync(struct index_mailbox *ibox, int last_commit);
@@ -77,7 +89,7 @@ void mbox_sync_update_header(struct mbox_sync_mail_context *ctx,
 void mbox_sync_update_header_from(struct mbox_sync_mail_context *ctx,
                                  const struct mbox_sync_mail *mail);
 int mbox_sync_try_rewrite(struct mbox_sync_mail_context *ctx, off_t move_diff);
-int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx, buffer_t *mails_buf,
+int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx,
                      uint32_t first_seq, uint32_t last_seq, off_t extra_space);
 
 int mbox_move(struct mbox_sync_context *sync_ctx,
index 8e936b8f4bdef8e434b61a63d4723eb8e805ce1f..c37c101e312ae65c669ad72e4a5cfc6ba25eb05e 100644 (file)
@@ -37,6 +37,12 @@ int mbox_move(struct mbox_sync_context *sync_ctx,
                ret = ret == (off_t)size ? 0 : -1;
        }
 
+       if (ret < 0) {
+               errno = output->stream_errno;
+               mbox_set_syscall_error(sync_ctx->ibox,
+                                      "o_stream_send_istream()");
+       }
+
        o_stream_unref(output);
        return (int)ret;
 }
@@ -189,7 +195,7 @@ int mbox_sync_try_rewrite(struct mbox_sync_mail_context *ctx, off_t move_diff)
                        new_hdr_size - ctx->header_first_change,
                        ctx->hdr_offset + move_diff +
                        ctx->header_first_change) < 0) {
-               // FIXME: error handling
+               mbox_set_syscall_error(ctx->sync_ctx->ibox, "pwrite_full()");
                return -1;
        }
        istream_raw_mbox_flush(ctx->sync_ctx->input);
@@ -249,17 +255,15 @@ static int mbox_sync_read_and_move(struct mbox_sync_context *sync_ctx,
           headers. */
        offset = sync_ctx->file_input->v_offset;
        if (mbox_move(sync_ctx, offset + mails[idx+1].space, offset,
-                     *end_offset - offset - mails[idx+1].space) < 0) {
-               // FIXME: error handling
+                     *end_offset - offset - mails[idx+1].space) < 0)
                return -1;
-       }
        mails[idx+1].from_offset += mails[idx+1].space;
 
        *end_offset = offset + mails[idx+1].space - str_len(mail_ctx.header);
 
        if (pwrite_full(sync_ctx->fd, str_data(mail_ctx.header),
                        str_len(mail_ctx.header), *end_offset) < 0) {
-               // FIXME: error handling
+               mbox_set_syscall_error(sync_ctx->ibox, "pwrite_full()");
                return -1;
        }
 
@@ -300,7 +304,7 @@ static int mbox_sync_fill_leftover(struct mbox_sync_context *sync_ctx,
 
        if (pwrite_full(sync_ctx->fd, str_data(mail_ctx.header),
                        str_len(mail_ctx.header), start_offset) < 0) {
-               // FIXME: error handling
+               mbox_set_syscall_error(sync_ctx->ibox, "pwrite_full()");
                return -1;
        }
 
@@ -308,7 +312,7 @@ static int mbox_sync_fill_leftover(struct mbox_sync_context *sync_ctx,
        return 0;
 }
 
-int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx, buffer_t *mails_buf,
+int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx,
                      uint32_t first_seq, uint32_t last_seq, off_t extra_space)
 {
        struct mbox_sync_mail *mails;
@@ -320,7 +324,7 @@ int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx, buffer_t *mails_buf,
        i_assert(first_seq != last_seq);
        i_assert(sync_ctx->ibox->mbox_lock_type == F_WRLCK);
 
-       mails = buffer_get_modifyable_data(mails_buf, &size);
+       mails = buffer_get_modifyable_data(sync_ctx->mails, &size);
        i_assert(size / sizeof(*mails) == last_seq - first_seq + 1);
 
        /* if there's expunges in mails[], we would get more correct balancing
@@ -368,7 +372,6 @@ int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx, buffer_t *mails_buf,
                        dest_offset = offset + mails[idx+1].space;
                        if (mbox_move(sync_ctx, dest_offset, offset,
                                      end_offset - dest_offset) < 0) {
-                               // FIXME: error handling
                                ret = -1;
                                break;
                        }
@@ -390,10 +393,8 @@ int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx, buffer_t *mails_buf,
                           we need to move From-line to start_offset */
                        offset = mails[1].offset;
                        if (mbox_move(sync_ctx, start_offset, end_offset,
-                                     offset - end_offset) < 0) {
-                               // FIXME: error handling
+                                     offset - end_offset) < 0)
                                ret = -1;
-                       }
                        mails[1].from_offset -= end_offset - start_offset;
                        idx++;
 
index 5faba2c9843da587f31f9f168d2b07fcc0b1aa85..72b5efa18c9f2ad7b24b8c678ec8fa47a2f8cd09 100644 (file)
@@ -1,4 +1,5 @@
 #include "lib.h"
+#include "ioloop.h"
 #include "buffer.h"
 #include "str.h"
 #include "message-parser.h"
@@ -89,6 +90,13 @@ static void mbox_sync_add_missing_headers(struct mbox_sync_mail_context *ctx)
 
        if (ctx->mail.uid == ctx->sync_ctx->first_uid &&
            ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] == (size_t)-1) {
+               if (ctx->sync_ctx->base_uid_validity == 0) {
+                       ctx->sync_ctx->base_uid_validity =
+                               ctx->sync_ctx->hdr->uid_validity == 0 ?
+                               (uint32_t)ioloop_time :
+                               ctx->sync_ctx->hdr->uid_validity;
+               }
+
                ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] = str_len(ctx->header);
                str_printfa(ctx->header, "X-IMAPbase: %u %010u",
                            ctx->sync_ctx->base_uid_validity,
@@ -197,6 +205,9 @@ void mbox_sync_update_header(struct mbox_sync_mail_context *ctx,
                memcpy(old_keywords, ctx->mail.keywords, sizeof(old_keywords));
 
                for (i = 0; i < size; i++) {
+                       if (sync[i].type != MAIL_INDEX_SYNC_TYPE_FLAGS)
+                               continue;
+
                        mail_index_sync_flags_apply(&sync[i], &ctx->mail.flags,
                                                    ctx->mail.keywords);
                }
index cda172fb0b3532dc711c8888dbcde47620729fe3..e3115de0ea59c336c467a55af6bff7ad77b8ca26 100644 (file)
@@ -70,8 +70,10 @@ static int mbox_sync_grow_file(struct mbox_sync_context *sync_ctx,
 
        /* put the extra space between last message's header and body */
        file_size = i_stream_get_size(sync_ctx->file_input) + grow_size;
-       if (file_set_size(sync_ctx->fd, file_size) < 0)
+       if (file_set_size(sync_ctx->fd, file_size) < 0) {
+               mbox_set_syscall_error(sync_ctx->ibox, "file_set_size()");
                return -1;
+       }
 
        src_offset = mail_ctx->body_offset;
        mail_ctx->body_offset += grow_size;
@@ -102,13 +104,18 @@ static void mbox_sync_buffer_delete_old(buffer_t *syncs_buf, uint32_t uid)
        buffer_set_used_size(syncs_buf, dest * sizeof(*sync));
 }
 
-static void
-mbox_sync_next_mail(struct mbox_sync_context *sync_ctx,
-                   struct mbox_sync_mail_context *mail_ctx, uint32_t seq)
+static int
+mbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
+                        struct mbox_sync_mail_context *mail_ctx)
 {
+       /* set input->eof */
+       (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
+       if (sync_ctx->input->eof)
+               return 0;
+
        memset(mail_ctx, 0, sizeof(*mail_ctx));
        mail_ctx->sync_ctx = sync_ctx;
-       mail_ctx->seq = seq;
+       mail_ctx->seq = ++sync_ctx->seq;
        mail_ctx->header = sync_ctx->header;
 
        mail_ctx->from_offset =
@@ -116,7 +123,7 @@ mbox_sync_next_mail(struct mbox_sync_context *sync_ctx,
        mail_ctx->mail.offset =
                istream_raw_mbox_get_header_offset(sync_ctx->input);
 
-       if (seq > 1 && sync_ctx->first_uid == mail_ctx->mail.uid) {
+       if (mail_ctx->seq > 1 && sync_ctx->first_uid == mail_ctx->mail.uid) {
                /* First message was expunged and this is the next one.
                   Skip \n header */
                mail_ctx->from_offset++;
@@ -136,6 +143,42 @@ mbox_sync_next_mail(struct mbox_sync_context *sync_ctx,
                mail_ctx->need_rewrite = TRUE;
                // FIXME: save it somewhere
        }
+       return 1;
+}
+
+static int mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx,
+                                     uint32_t uid, int *sync_expunge_r)
+{
+       struct mail_index_sync_rec *sync_rec = &sync_ctx->sync_rec;
+       int ret;
+
+       *sync_expunge_r = FALSE;
+
+       if (sync_ctx->ibox->readonly)
+               return 0;
+
+       mbox_sync_buffer_delete_old(sync_ctx->syncs, uid);
+       while (uid >= sync_rec->uid1) {
+               if (sync_rec->uid1 != 0) {
+                       i_assert(uid <= sync_rec->uid2);
+                       buffer_append(sync_ctx->syncs, sync_rec,
+                                     sizeof(*sync_rec));
+
+                       if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE)
+                               *sync_expunge_r = TRUE;
+               }
+
+               ret = mail_index_sync_next(sync_ctx->index_sync_ctx, sync_rec);
+               if (ret < 0)
+                       return -1;
+
+               if (ret == 0) {
+                       memset(sync_rec, 0, sizeof(*sync_rec));
+                       break;
+               }
+       }
+
+       return 0;
 }
 
 static void mbox_sync_apply_index_syncs(buffer_t *syncs_buf, uint8_t *flags,
@@ -151,6 +194,115 @@ static void mbox_sync_apply_index_syncs(buffer_t *syncs_buf, uint8_t *flags,
                mail_index_sync_flags_apply(&sync[i], flags, keywords);
 }
 
+static int
+mbox_sync_read_index_rec(struct mbox_sync_context *sync_ctx,
+                        uint32_t uid, const struct mail_index_record **rec_r)
+{
+        const struct mail_index_record *rec = NULL;
+       uint32_t messages_count;
+
+       messages_count = mail_index_view_get_message_count(sync_ctx->sync_view);
+       while (sync_ctx->idx_seq < messages_count) {
+               if (mail_index_lookup(sync_ctx->sync_view,
+                                     ++sync_ctx->idx_seq, &rec) < 0) {
+                       mail_storage_set_index_error(sync_ctx->ibox);
+                       return -1;
+               }
+
+               if (uid <= rec->uid)
+                       break;
+
+               /* externally expunged message, remove from index */
+               mail_index_expunge(sync_ctx->t, sync_ctx->idx_seq);
+               rec = NULL;
+       }
+
+       if (rec != NULL && rec->uid != uid) {
+               /* new UID in the middle of the mailbox - shouldn't happen */
+               mail_storage_set_critical(sync_ctx->ibox->box.storage,
+                       "mbox sync: UID inserted in the middle of mailbox "
+                       "(%u > %u)", rec->uid, uid);
+               mail_index_mark_corrupted(sync_ctx->ibox->index);
+               return -1;
+       }
+
+       *rec_r = rec;
+       return 0;
+}
+
+static int
+mbox_sync_update_from_offset(struct mbox_sync_context *sync_ctx,
+                             struct mbox_sync_mail *mail,
+                            int nocheck)
+{
+       const void *data;
+       uint64_t offset;
+
+       if (!nocheck) {
+               /* see if from_offset needs updating */
+               if (mail_index_lookup_extra(sync_ctx->sync_view,
+                                           sync_ctx->idx_seq,
+                                           sync_ctx->ibox->mbox_extra_idx,
+                                           &data) < 0) {
+                       mail_storage_set_index_error(sync_ctx->ibox);
+                       return -1;
+               }
+
+               offset = *((const uint64_t *)data);
+               if (offset == mail->from_offset)
+                       return 0;
+       } else {
+               offset = mail->from_offset;
+       }
+
+       mail_index_update_extra_rec(sync_ctx->t, sync_ctx->idx_seq,
+                                   sync_ctx->ibox->mbox_extra_idx, &offset);
+       return 0;
+}
+
+static int mbox_sync_update_index(struct mbox_sync_context *sync_ctx,
+                                 struct mbox_sync_mail *mail,
+                                 const struct mail_index_record *rec)
+{
+       keywords_mask_t idx_keywords;
+       uint8_t idx_flags, mbox_flags;
+
+       if (rec == NULL) {
+               /* new message */
+               mail_index_append(sync_ctx->t, mail->uid, &sync_ctx->idx_seq);
+               mbox_flags = mail->flags & (MAIL_FLAGS_MASK^MAIL_RECENT);
+               mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq,
+                                       MODIFY_REPLACE, mbox_flags,
+                                       mail->keywords);
+       } else {
+               /* see if flags changed */
+               idx_flags = rec->flags;
+               memcpy(idx_keywords, rec->keywords, INDEX_KEYWORDS_BYTE_COUNT);
+               mbox_sync_apply_index_syncs(sync_ctx->syncs,
+                                           &idx_flags, idx_keywords);
+
+               mbox_flags = (rec->flags & ~MAIL_FLAGS_MASK) |
+                       (mail->flags & (MAIL_FLAGS_MASK^MAIL_RECENT));
+
+               if (idx_flags != mbox_flags ||
+                   memcmp(idx_keywords, mail->keywords,
+                          INDEX_KEYWORDS_BYTE_COUNT) != 0) {
+                       mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq,
+                                               MODIFY_REPLACE, mbox_flags,
+                                               mail->keywords);
+               }
+       }
+
+       /* update from_offsets, but not if we're going to rewrite this message.
+          rewriting would just move it anyway. */
+       if (sync_ctx->need_space_seq == 0) {
+               int nocheck = rec == NULL || sync_ctx->expunged_space > 0;
+               if (mbox_sync_update_from_offset(sync_ctx, mail, nocheck) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
 static int mbox_read_from_line(struct mbox_sync_mail_context *ctx)
 {
        struct istream *input = ctx->sync_ctx->file_input;
@@ -185,12 +337,9 @@ mbox_write_from_line(struct mbox_sync_mail_context *ctx, off_t move_diff)
 {
        string_t *str = ctx->sync_ctx->from_line;
 
-       if (move_diff == 0)
-               return 0;
-
        if (pwrite_full(ctx->sync_ctx->fd, str_data(str), str_len(str),
                        ctx->from_offset + move_diff) < 0) {
-               // FIXME: error handling
+               mbox_set_syscall_error(ctx->sync_ctx->ibox, "pwrite_full()");
                return -1;
        }
 
@@ -218,451 +367,381 @@ update_from_offsets(struct index_mailbox *ibox,
        }
 }
 
-static int mbox_sync_do(struct index_mailbox *ibox,
-                       struct mail_index_sync_ctx *index_sync_ctx,
-                       struct mail_index_view *sync_view,
-                       buffer_t *syncs, struct mail_index_sync_rec *sync_rec,
-                       int index_synced)
+static int mbox_sync_handle_expunge(struct mbox_sync_mail_context *mail_ctx)
 {
-       /* a horrible function. needs some serious cleanups. */
-       struct mbox_sync_context sync_ctx;
-       struct mbox_sync_mail_context mail_ctx;
-       struct mail_index_transaction *t;
-       const struct mail_index_header *hdr;
-       const struct mail_index_record *rec;
-       struct istream *input;
-       uint32_t seq, need_space_seq, idx_seq, messages_count;
-       uint8_t new_flags;
-       off_t space_diff, move_diff;
-       uoff_t offset, extra_space, trailer_size;
-       buffer_t *mails;
-       struct stat st;
-       int sync_expunge, update_from_offset, ret = 0;
+       if (mail_ctx->sync_ctx->ibox->mbox_lock_type == F_RDLCK)
+               return -2;
+
+       mail_ctx->mail.offset = mail_ctx->from_offset;
+       mail_ctx->mail.space =
+               mail_ctx->body_offset - mail_ctx->from_offset +
+               mail_ctx->mail.body_size;
+       mail_ctx->mail.body_size = 0;
+
+       if (mail_ctx->sync_ctx->seq == 1) {
+               /* expunging first message, fix space to contain next
+                  message's \n header too since it will be removed. */
+               mail_ctx->mail.space++;
+       }
 
-       if (mail_index_get_header(sync_view, &hdr) < 0)
-               return -1;
+       mail_ctx->sync_ctx->expunged_space += mail_ctx->mail.space;
+       return 0;
+}
 
-       t = mail_index_transaction_begin(sync_view, FALSE);
+static int mbox_sync_handle_header(struct mbox_sync_mail_context *mail_ctx)
+{
+       struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
+       off_t move_diff;
+       int ret;
 
-       memset(&sync_ctx, 0, sizeof(sync_ctx));
-       sync_ctx.ibox = ibox;
-       sync_ctx.file_input = ibox->mbox_file_stream;
-       sync_ctx.input = ibox->mbox_stream;
-       sync_ctx.fd = ibox->mbox_fd;
-       sync_ctx.from_line = str_new(default_pool, 256);
-       sync_ctx.header = str_new(default_pool, 4096);
-       sync_ctx.next_uid = 1;
+       if (sync_ctx->first_uid == 0)
+               sync_ctx->first_uid = mail_ctx->mail.uid;
 
-       input = sync_ctx.input;
-       istream_raw_mbox_seek(input, 0);
+       if (sync_ctx->ibox->readonly)
+               return 0;
 
-       mails = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+       if (sync_ctx->expunged_space > 0 && sync_ctx->need_space_seq == 0) {
+               /* move the header backwards to fill expunged space */
+               if (sync_ctx->ibox->mbox_lock_type == F_RDLCK)
+                       return -2;
 
-       messages_count = mail_index_view_get_message_count(sync_view);
+               move_diff = -sync_ctx->expunged_space;
 
-       space_diff = 0; need_space_seq = 0; idx_seq = 0; rec = NULL;
-       for (seq = 0;;) {
-               /* set input->eof */
-               (void)istream_raw_mbox_get_header_offset(input);
-               if (input->eof)
-                       break;
-               seq++;
+               /* read the From-line before rewriting overwrites it */
+               if (mbox_read_from_line(mail_ctx) < 0)
+                       return -1;
 
-               mbox_sync_next_mail(&sync_ctx, &mail_ctx, seq);
+               mbox_sync_update_header(mail_ctx, sync_ctx->syncs);
+               if ((ret = mbox_sync_try_rewrite(mail_ctx, move_diff)) < 0)
+                       return -1;
 
-               /* get all sync records related to this message */
-               ret = 1; sync_expunge = FALSE;
-               mbox_sync_buffer_delete_old(syncs, mail_ctx.mail.uid);
-               while (mail_ctx.mail.uid >= sync_rec->uid1 && ret > 0) {
-                       if (sync_rec->uid1 != 0) {
-                               i_assert(mail_ctx.mail.uid <= sync_rec->uid2);
-                               buffer_append(syncs, sync_rec,
-                                             sizeof(*sync_rec));
-
-                               if (sync_rec->type ==
-                                   MAIL_INDEX_SYNC_TYPE_EXPUNGE)
-                                       sync_expunge = TRUE;
-                       }
-                       ret = mail_index_sync_next(index_sync_ctx, sync_rec);
-                       if (ret == 0)
-                               memset(sync_rec, 0, sizeof(*sync_rec));
+               if (ret > 0) {
+                       /* rewrite successful, write From-line to
+                          new location */
+                       mail_ctx->mail.from_offset += move_diff;
+                       mail_ctx->mail.offset += move_diff;
+                       if (mbox_write_from_line(mail_ctx, move_diff) < 0)
+                               return -1;
                }
-               if (ret < 0)
-                       break;
+       } else if (mail_ctx->need_rewrite ||
+                  buffer_get_used_size(sync_ctx->syncs) != 0) {
+               if (sync_ctx->ibox->mbox_lock_type == F_RDLCK)
+                       return -2;
 
-               if (seq == 1 && sync_ctx.base_uid_validity == 0) {
-                       sync_ctx.base_uid_validity =
-                               hdr->uid_validity == 0 ? (uint32_t)ioloop_time :
-                               hdr->uid_validity;
-               }
+               mbox_sync_update_header(mail_ctx, sync_ctx->syncs);
+               if ((ret = mbox_sync_try_rewrite(mail_ctx, move_diff)) < 0)
+                       return -1;
+       } else {
+               /* nothing to do */
+               return 0;
+       }
 
-               if (!sync_expunge && sync_ctx.first_uid == 0)
-                       sync_ctx.first_uid = mail_ctx.mail.uid;
-
-               if ((mail_ctx.need_rewrite || sync_ctx.expunged_space > 0 ||
-                    buffer_get_used_size(syncs) != 0) && !ibox->readonly) {
-                       if (ibox->mbox_lock_type == F_RDLCK) {
-                               ret = -2;
-                               break;
-                       }
-
-                       if (sync_expunge) {
-                               ret = 1;
-                               mail_ctx.mail.offset = mail_ctx.from_offset;
-                               mail_ctx.mail.space =
-                                       mail_ctx.body_offset -
-                                       mail_ctx.from_offset +
-                                       mail_ctx.mail.body_size;
-                               mail_ctx.mail.body_size = 0;
-
-                               if (seq == 1)
-                                       mail_ctx.mail.space++;
-
-                               sync_ctx.expunged_space += mail_ctx.mail.space;
-                       } else {
-                               move_diff = need_space_seq != 0 ? 0 :
-                                       -sync_ctx.expunged_space;
-
-                               /* read the From-line */
-                               if (move_diff != 0 &&
-                                   mbox_read_from_line(&mail_ctx) < 0) {
-                                       ret = -1;
-                                       break;
-                               }
-
-                               mbox_sync_update_header(&mail_ctx, syncs);
-                               ret = mbox_sync_try_rewrite(&mail_ctx,
-                                                           move_diff);
-
-                               if (ret > 0) {
-                                       mail_ctx.mail.from_offset += move_diff;
-                                       mail_ctx.mail.offset += move_diff;
-                                       ret = mbox_write_from_line(&mail_ctx,
-                                                                  move_diff);
-                                       if (ret == 0)
-                                               ret = 1;
-                               }
-
-                               if (ret < 0)
-                                       break;
-                       }
-
-                       if (ret == 0 && need_space_seq == 0) {
-                               /* first mail with no space to write it */
-                               need_space_seq = seq;
-                               space_diff = 0;
-
-                               if (sync_ctx.expunged_space > 0) {
-                                       /* create dummy message to describe
-                                          the expunged data */
-                                       struct mbox_sync_mail mail;
-
-                                       memset(&mail, 0, sizeof(mail));
-                                       mail.offset = mail_ctx.from_offset -
-                                               sync_ctx.expunged_space;
-                                       mail.space = sync_ctx.expunged_space;
-
-                                       need_space_seq--;
-                                       buffer_append(mails, &mail,
-                                                     sizeof(mail));
-                               }
-                       }
+       if (ret == 0 && sync_ctx->need_space_seq == 0) {
+               /* first mail with no space to write it */
+               sync_ctx->need_space_seq = sync_ctx->seq;
+               sync_ctx->space_diff = 0;
+
+               if (sync_ctx->expunged_space > 0) {
+                       /* create dummy message to describe the expunged data */
+                       struct mbox_sync_mail mail;
+
+                       memset(&mail, 0, sizeof(mail));
+                       mail.offset = mail_ctx->from_offset -
+                               sync_ctx->expunged_space;
+                       mail.space = sync_ctx->expunged_space;
+
+                       sync_ctx->need_space_seq--;
+                       buffer_append(sync_ctx->mails, &mail, sizeof(mail));
                }
+       }
+       return 0;
+}
+
+static int
+mbox_sync_handle_missing_space(struct mbox_sync_mail_context *mail_ctx)
+{
+       struct mbox_sync_context *sync_ctx = mail_ctx->sync_ctx;
+       uoff_t extra_space;
 
-               /* update index */
-               do {
-                       if (rec != NULL && rec->uid >= mail_ctx.mail.uid)
-                               break;
+       buffer_append(sync_ctx->mails, &mail_ctx->mail, sizeof(mail_ctx->mail));
 
-                       if (idx_seq >= messages_count) {
-                               rec = NULL;
-                               break;
-                       }
+       sync_ctx->space_diff += mail_ctx->mail.space;
+       if (sync_ctx->space_diff < 0)
+               return 0;
 
-                       if (rec != NULL)
-                               mail_index_expunge(t, idx_seq);
+       /* we have enough space now */
+       extra_space = MBOX_HEADER_EXTRA_SPACE *
+               (sync_ctx->seq - sync_ctx->need_space_seq + 1);
 
-                       ret = mail_index_lookup(sync_view, ++idx_seq, &rec);
-               } while (ret == 0);
+       if (mail_ctx->mail.uid == 0 &&
+           (uoff_t)sync_ctx->space_diff > extra_space) {
+               /* don't waste too much on extra spacing */
+               sync_ctx->expunged_space = sync_ctx->space_diff - extra_space;
+               sync_ctx->space_diff = extra_space;
+       } else {
+               sync_ctx->expunged_space = 0;
+       }
 
-               if (ret < 0)
-                       break;
-               if (rec != NULL && rec->uid != mail_ctx.mail.uid) {
-                       /* new UID in the middle of the mailbox -
-                          shouldn't happen */
-                       mail_storage_set_critical(ibox->box.storage,
-                               "mbox sync: UID inserted in the middle "
-                               "of mailbox (%u > %u)",
-                               rec->uid, mail_ctx.mail.uid);
-                       mail_index_mark_corrupted(ibox->index);
-                       ret = -1;
-                       break;
-               }
+       if (mbox_sync_rewrite(sync_ctx, sync_ctx->need_space_seq, sync_ctx->seq,
+                             sync_ctx->space_diff) < 0)
+               return -1;
 
-               if (sync_expunge) {
-                       if (rec != NULL)
-                               mail_index_expunge(t, idx_seq);
-                       update_from_offset = FALSE;
-               } else if (rec != NULL) {
-                       /* see if flags changed */
-                       keywords_mask_t old_keywords;
-                       uint8_t old_flags;
-
-                       old_flags = rec->flags;
-                       memcpy(old_keywords, rec->keywords,
-                              INDEX_KEYWORDS_BYTE_COUNT);
-                       mbox_sync_apply_index_syncs(syncs, &old_flags,
-                                                   old_keywords);
-
-                       new_flags = (rec->flags & ~MAIL_FLAGS_MASK) |
-                               (mail_ctx.mail.flags &
-                                (MAIL_FLAGS_MASK^MAIL_RECENT));
-
-                       if (old_flags != new_flags ||
-                           memcmp(old_keywords, mail_ctx.mail.keywords,
-                                  INDEX_KEYWORDS_BYTE_COUNT) != 0) {
-                               mail_index_update_flags(t, idx_seq,
-                                                       MODIFY_REPLACE,
-                                                       new_flags,
-                                                       mail_ctx.mail.keywords);
-                       }
-
-                       update_from_offset = sync_ctx.expunged_space > 0;
-
-                       /* we used this record */
-                       rec = NULL;
-               } else {
-                       /* new message */
-                       mail_index_append(t, mail_ctx.mail.uid, &idx_seq);
-                       new_flags = mail_ctx.mail.flags &
-                               (MAIL_FLAGS_MASK^MAIL_RECENT);
-                       mail_index_update_flags(t, idx_seq, MODIFY_REPLACE,
-                                               new_flags,
-                                               mail_ctx.mail.keywords);
-                       update_from_offset = TRUE;
-               }
+       update_from_offsets(sync_ctx->ibox, sync_ctx->t, sync_ctx->mails,
+                           sync_ctx->need_space_seq, sync_ctx->seq);
+
+       /* mail_ctx may contain wrong data after rewrite, so make sure we
+          don't try to access it */
+       memset(mail_ctx, 0, sizeof(*mail_ctx));
 
-               if (update_from_offset) {
-                       /* from_offset needs updating */
-                       uint64_t offset;
-
-                       offset = mail_ctx.mail.from_offset;
-                       mail_index_update_extra_rec(t, idx_seq,
-                               ibox->mbox_extra_idx, &offset);
-               } else if (need_space_seq == 0 && !sync_expunge) {
-                       /* see if from_offset needs updating */
-                       const void *data;
-                       uint64_t offset;
-
-                       if (mail_index_lookup_extra(sync_view, idx_seq,
-                                                   ibox->mbox_extra_idx,
-                                                   &data) < 0) {
-                               ret = -1;
-                               break;
-                       }
-
-                       offset = *((const uint64_t *)data);
-                       if (offset != mail_ctx.mail.from_offset) {
-                               offset = mail_ctx.mail.from_offset;
-                               mail_index_update_extra_rec(t, idx_seq,
-                                       ibox->mbox_extra_idx, &offset);
-                       }
+       sync_ctx->need_space_seq = 0;
+       buffer_set_used_size(sync_ctx->mails, 0);
+       return 0;
+}
+
+static int mbox_sync_parse_all(struct mbox_sync_context *sync_ctx,
+                              struct mbox_sync_mail_context *mail_ctx)
+{
+       const struct mail_index_record *rec;
+       uint32_t uid, messages_count;
+       uoff_t offset;
+       int ret, expunged;
+
+       sync_ctx->file_input = sync_ctx->ibox->mbox_file_stream;
+       sync_ctx->input = sync_ctx->ibox->mbox_stream;
+       sync_ctx->fd = sync_ctx->ibox->mbox_fd;
+
+       istream_raw_mbox_seek(sync_ctx->input, 0);
+
+       while ((ret = mbox_sync_read_next_mail(sync_ctx, mail_ctx)) > 0) {
+               uid = mail_ctx->mail.uid;
+
+               /* get all sync records related to this message */
+               if (mbox_sync_read_index_syncs(sync_ctx, uid, &expunged) < 0)
+                       return -1;
+
+               if (!expunged)
+                       ret = mbox_sync_handle_header(mail_ctx);
+               else {
+                       mail_ctx->mail.uid = 0;
+                       ret = mbox_sync_handle_expunge(mail_ctx);
+               }
+               if (ret < 0) {
+                       /* -1 = error, -2 = need exclusive lock */
+                       return ret;
                }
 
-               istream_raw_mbox_next(input, mail_ctx.mail.body_size);
-               offset = istream_raw_mbox_get_start_offset(input);
+               if (mbox_sync_read_index_rec(sync_ctx, uid, &rec) < 0)
+                       return -1;
 
-               if (sync_ctx.expunged_space > 0 && !sync_expunge &&
-                   need_space_seq == 0) {
-                       /* move the body */
-                       if (mbox_move(&sync_ctx,
-                                     mail_ctx.body_offset -
-                                     sync_ctx.expunged_space,
-                                     mail_ctx.body_offset,
-                                     mail_ctx.mail.body_size) < 0) {
-                               ret = -1;
-                               break;
-                       }
-                       i_stream_seek(input, offset);
+               if (!expunged) {
+                       if (mbox_sync_update_index(sync_ctx, &mail_ctx->mail,
+                                                  rec) < 0)
+                               return -1;
                }
 
-               if (need_space_seq != 0) {
-                       if (sync_expunge)
-                               mail_ctx.mail.uid = 0;
-
-                       buffer_append(mails, &mail_ctx.mail,
-                                     sizeof(mail_ctx.mail));
-
-                       space_diff += mail_ctx.mail.space;
-                       if (space_diff >= 0) {
-                               /* we have enough space now */
-                               extra_space = MBOX_HEADER_EXTRA_SPACE *
-                                       (seq - need_space_seq + 1);
-                               if (sync_expunge &&
-                                   (size_t)space_diff > extra_space) {
-                                       /* don't waste too much on extra
-                                          spacing */
-                                       sync_ctx.expunged_space =
-                                               space_diff - extra_space;
-                                       space_diff = extra_space;
-                               } else {
-                                       sync_ctx.expunged_space = 0;
-                               }
-                               if (mbox_sync_rewrite(&sync_ctx, mails,
-                                                     need_space_seq, seq,
-                                                     space_diff) < 0) {
-                                       ret = -1;
-                                       break;
-                               }
-
-                               update_from_offsets(ibox, t, mails,
-                                                   need_space_seq, seq);
-
-                               /* mail_ctx may contain wrong data after
-                                  rewrite, so make sure we don't try to access
-                                  it */
-                               memset(&mail_ctx, 0, sizeof(mail_ctx));
-                               i_stream_seek(input, offset);
-
-                               need_space_seq = 0;
-                               buffer_set_used_size(mails, 0);
-                       }
+               istream_raw_mbox_next(sync_ctx->input,
+                                     mail_ctx->mail.body_size);
+               offset = istream_raw_mbox_get_start_offset(sync_ctx->input);
+
+               if (sync_ctx->need_space_seq != 0) {
+                       if (mbox_sync_handle_missing_space(mail_ctx) < 0)
+                               return -1;
+                       i_stream_seek(sync_ctx->input, offset);
+               } else if (sync_ctx->expunged_space > 0 && !expunged) {
+                       /* move the body */
+                       if (mbox_move(sync_ctx,
+                                     mail_ctx->body_offset -
+                                     sync_ctx->expunged_space,
+                                     mail_ctx->body_offset,
+                                     mail_ctx->mail.body_size) < 0)
+                               return -1;
+                       i_stream_seek(sync_ctx->input, offset);
                }
        }
 
-       trailer_size = i_stream_get_size(sync_ctx.file_input) - offset;
-       if (need_space_seq != 0 && ret >= 0) {
-               i_assert(space_diff < 0);
-               extra_space = MBOX_HEADER_EXTRA_SPACE *
-                       (seq - need_space_seq + 1);
-               space_diff -= extra_space;
+       /* rest of the messages in index don't exist -> expunge them */
+       messages_count = mail_index_view_get_message_count(sync_ctx->sync_view);
+       while (sync_ctx->idx_seq < messages_count)
+               mail_index_expunge(sync_ctx->t, ++sync_ctx->idx_seq);
 
-               space_diff += sync_ctx.expunged_space;
-               sync_ctx.expunged_space -= -space_diff;
+       return 0;
+}
 
-               if (mail_ctx.have_eoh && !mail_ctx.updated)
-                       str_append_c(mail_ctx.header, '\n');
+static int mbox_sync_handle_eof_updates(struct mbox_sync_context *sync_ctx,
+                                       struct mbox_sync_mail_context *mail_ctx)
+{
+       uoff_t offset, extra_space, trailer_size;
 
-               if (space_diff < 0 &&
-                   mbox_sync_grow_file(&sync_ctx, &mail_ctx, -space_diff) < 0)
-                       ret = -1;
-               else if (mbox_sync_try_rewrite(&mail_ctx, 0) < 0)
-                       ret = -1;
-               else if (seq != need_space_seq) {
-                       buffer_set_used_size(mails, (seq-need_space_seq) *
-                                            sizeof(mail_ctx.mail));
-                       buffer_append(mails, &mail_ctx.mail,
-                                     sizeof(mail_ctx.mail));
-
-                       if (mbox_sync_rewrite(&sync_ctx, mails, need_space_seq,
-                                             seq, extra_space) < 0)
-                               ret = -1;
-
-                       update_from_offsets(ibox, t, mails,
-                                           need_space_seq, seq);
-               }
-       }
+       trailer_size = i_stream_get_size(sync_ctx->file_input) -
+               sync_ctx->file_input->v_offset;
 
-       if (sync_ctx.expunged_space > 0) {
-               /* copy trailer, then truncate the file */
-               offset = i_stream_get_size(sync_ctx.file_input) -
-                       sync_ctx.expunged_space - trailer_size;
+       if (sync_ctx->need_space_seq != 0) {
+               i_assert(sync_ctx->space_diff < 0);
+               extra_space = MBOX_HEADER_EXTRA_SPACE *
+                       (sync_ctx->seq - sync_ctx->need_space_seq + 1);
+               sync_ctx->space_diff -= extra_space;
 
-               if (mbox_move(&sync_ctx, offset,
-                             offset + sync_ctx.expunged_space,
-                             trailer_size) < 0)
-                       ret = -1;
-               else if (ftruncate(ibox->mbox_fd, offset + trailer_size) < 0)
-                       ret = -1;
+               sync_ctx->space_diff += sync_ctx->expunged_space;
+               sync_ctx->expunged_space -= -sync_ctx->space_diff;
 
-               istream_raw_mbox_flush(input);
-       }
+               if (mail_ctx->have_eoh && !mail_ctx->updated)
+                       str_append_c(mail_ctx->header, '\n');
 
-       if (ret >= 0) {
-               if (rec != NULL)
-                       mail_index_expunge(t, idx_seq);
-               while (idx_seq < messages_count)
-                       mail_index_expunge(t, ++idx_seq);
+               if (sync_ctx->space_diff < 0 &&
+                   mbox_sync_grow_file(sync_ctx, mail_ctx,
+                                       -sync_ctx->space_diff) < 0)
+                       return -1;
 
-               if (sync_ctx.base_uid_last+1 != sync_ctx.next_uid) {
+               if (mbox_sync_try_rewrite(mail_ctx, 0) < 0)
+                       return -1;
 
-                       // FIXME: rewrite X-IMAPbase header
+               if (sync_ctx->seq != sync_ctx->need_space_seq) {
+                       buffer_set_used_size(sync_ctx->mails,
+                                            (sync_ctx->seq -
+                                             sync_ctx->need_space_seq) *
+                                            sizeof(mail_ctx->mail));
+                       buffer_append(sync_ctx->mails, &mail_ctx->mail,
+                                     sizeof(mail_ctx->mail));
+
+                       if (mbox_sync_rewrite(sync_ctx,
+                                             sync_ctx->need_space_seq,
+                                             sync_ctx->seq, extra_space) < 0)
+                               return -1;
+
+                       update_from_offsets(sync_ctx->ibox, sync_ctx->t,
+                                           sync_ctx->mails,
+                                           sync_ctx->need_space_seq,
+                                           sync_ctx->seq);
                }
        }
 
-       if (ret >= 0) {
-               /* only syncs left should be just appends (and their updates)
-                  which weren't synced yet for some reason (crash). we'll just
-                  ignore them, as we've overwritten them above. */
-               while (mail_index_sync_next(index_sync_ctx, sync_rec) > 0)
-                       ;
-       }
+       if (sync_ctx->expunged_space > 0) {
+               /* copy trailer, then truncate the file */
+               offset = i_stream_get_size(sync_ctx->file_input) -
+                       sync_ctx->expunged_space - trailer_size;
 
-       if (ret == 0) {
-               if (fstat(ibox->mbox_fd, &st) < 0) {
-                       mbox_set_syscall_error(ibox, "fstat()");
-                       ret = -1;
+               if (mbox_move(sync_ctx, offset,
+                             offset + sync_ctx->expunged_space,
+                             trailer_size) < 0)
+                       return -1;
+               if (ftruncate(sync_ctx->ibox->mbox_fd,
+                             offset + trailer_size) < 0) {
+                       mbox_set_syscall_error(sync_ctx->ibox, "ftruncate()");
+                       return -1;
                }
+
+               istream_raw_mbox_flush(sync_ctx->input);
        }
-       if (ret < 0) {
-               st.st_mtime = 0;
-               st.st_size = 0;
+       return 0;
+}
+
+static int mbox_sync_update_index_header(struct mbox_sync_context *sync_ctx)
+{
+       struct stat st;
+
+       if (fstat(sync_ctx->ibox->mbox_fd, &st) < 0) {
+               mbox_set_syscall_error(sync_ctx->ibox, "fstat()");
+               return -1;
        }
 
-       if (sync_ctx.base_uid_validity != hdr->uid_validity) {
-               mail_index_update_header(t,
+       if (sync_ctx->base_uid_validity != sync_ctx->hdr->uid_validity) {
+               mail_index_update_header(sync_ctx->t,
                        offsetof(struct mail_index_header, uid_validity),
-                       &sync_ctx.base_uid_validity,
-                       sizeof(sync_ctx.base_uid_validity));
+                       &sync_ctx->base_uid_validity,
+                       sizeof(sync_ctx->base_uid_validity));
        }
-       if (sync_ctx.next_uid != hdr->next_uid) {
-               mail_index_update_header(t,
+       if (sync_ctx->next_uid != sync_ctx->hdr->next_uid) {
+               mail_index_update_header(sync_ctx->t,
                        offsetof(struct mail_index_header, next_uid),
-                       &sync_ctx.next_uid, sizeof(sync_ctx.next_uid));
+                       &sync_ctx->next_uid, sizeof(sync_ctx->next_uid));
        }
 
-       if ((uint32_t)st.st_mtime != hdr->sync_stamp) {
+       if ((uint32_t)st.st_mtime != sync_ctx->hdr->sync_stamp) {
                uint32_t sync_stamp = st.st_mtime;
 
-               mail_index_update_header(t,
+               mail_index_update_header(sync_ctx->t,
                        offsetof(struct mail_index_header, sync_stamp),
                        &sync_stamp, sizeof(sync_stamp));
        }
-       if ((uint64_t)st.st_size != hdr->sync_size) {
+       if ((uint64_t)st.st_size != sync_ctx->hdr->sync_size) {
                uint64_t sync_size = st.st_size;
 
-               mail_index_update_header(t,
+               mail_index_update_header(sync_ctx->t,
                        offsetof(struct mail_index_header, sync_size),
                        &sync_size, sizeof(sync_size));
        }
 
-       if (ret < 0)
-               mail_index_transaction_rollback(t);
-       else {
-               if (mail_index_transaction_commit(t, &seq, &offset) < 0)
-                       ret = -1;
-               else if (seq != 0) {
-                       ibox->commit_log_file_seq = seq;
-                       ibox->commit_log_file_offset = offset;
-               }
-       }
+       return 0;
+}
 
-       if (ret != -2) {
-               if (mail_index_sync_end(index_sync_ctx) < 0)
-                       ret = -1;
+static void mbox_sync_restart(struct mbox_sync_context *sync_ctx)
+{
+       sync_ctx->base_uid_validity = 0;
+       sync_ctx->base_uid_last = 0;
+
+       sync_ctx->next_uid = 1;
+       sync_ctx->prev_msg_uid = sync_ctx->first_uid = 0;
+       sync_ctx->seq = sync_ctx->idx_seq = 0;
+
+       mail_index_transaction_rollback(sync_ctx->t);
+       sync_ctx->t = mail_index_transaction_begin(sync_ctx->sync_view, FALSE);
+}
+
+static int mbox_sync_do(struct mbox_sync_context *sync_ctx, int index_synced)
+{
+       struct index_mailbox *ibox = sync_ctx->ibox;
+       struct mbox_sync_mail_context mail_ctx;
+       int ret, lock_type;
+
+       lock_type = mail_index_sync_have_more(sync_ctx->index_sync_ctx) ?
+               F_WRLCK : F_RDLCK;
+       if ((ret = mbox_lock(ibox, lock_type, &sync_ctx->lock_id)) <= 0)
+               return ret;
+
+       if (mbox_file_open_stream(ibox) < 0)
+               return -1;
+
+       if ((ret = mbox_sync_parse_all(sync_ctx, &mail_ctx)) == -1)
+               return -1;
+
+       if (ret == -2) {
+               /* we want to modify mbox, get exclusive lock. this requires
+                  dropping the read lock first, so we have to parse the whole
+                  mbox again */
+               (void)mbox_unlock(ibox, sync_ctx->lock_id);
+               sync_ctx->lock_id = 0;
+
+               if ((ret = mbox_lock(ibox, F_WRLCK, &sync_ctx->lock_id)) <= 0)
+                       return ret;
+               if (mbox_file_open_stream(ibox) < 0)
+                       return -1;
+
+               /* FIXME: if mbox timestamp hasn't changed and it's older than
+                  2 secs, we could continue from where we left */
+               mbox_sync_restart(sync_ctx);
+
+               if (mbox_sync_parse_all(sync_ctx, &mail_ctx) < 0)
+                       return -1;
        }
 
-       if (ret == 0) {
-               ibox->commit_log_file_seq = 0;
-               ibox->commit_log_file_offset = 0;
-       } else {
-               mail_storage_set_index_error(ibox);
+       if (mbox_sync_handle_eof_updates(sync_ctx, &mail_ctx) < 0)
+               return -1;
+
+       if (sync_ctx->base_uid_last+1 != sync_ctx->next_uid) {
+               // FIXME: rewrite X-IMAPbase header
        }
 
-       str_free(sync_ctx.header);
-       str_free(sync_ctx.from_line);
-       buffer_free(mails);
-       return ret < 0 ? ret : 0;
+       /* only syncs left should be just appends (and their updates)
+          which weren't synced yet for some reason (crash). we'll just
+          ignore them, as we've overwritten them above. */
+       while (mail_index_sync_next(sync_ctx->index_sync_ctx,
+                                   &sync_ctx->sync_rec) > 0)
+               ;
+
+       if (mbox_sync_update_index_header(sync_ctx) < 0)
+               return -1;
+
+       return 0;
 }
 
 static int mbox_sync_has_changed(struct index_mailbox *ibox)
@@ -688,12 +767,10 @@ int mbox_sync(struct index_mailbox *ibox, int last_commit)
 {
        struct mail_index_sync_ctx *index_sync_ctx;
        struct mail_index_view *sync_view;
-       unsigned int lock_id;
+       struct mbox_sync_context sync_ctx;
        uint32_t seq;
        uoff_t offset;
-       struct mail_index_sync_rec sync_rec;
-       buffer_t *syncs;
-       int ret, lock_type, index_synced;
+       int ret, index_synced;
 
        if ((ret = mbox_sync_has_changed(ibox)) < 0)
                return -1;
@@ -717,40 +794,46 @@ int mbox_sync(struct index_mailbox *ibox, int last_commit)
                return ret;
        }
 
-       memset(&sync_rec, 0, sizeof(sync_rec));
-       syncs = buffer_create_dynamic(default_pool, 256, (size_t)-1);
+       memset(&sync_ctx, 0, sizeof(sync_ctx));
+       sync_ctx.ibox = ibox;
+       sync_ctx.from_line = str_new(default_pool, 256);
+       sync_ctx.header = str_new(default_pool, 4096);
+
+       sync_ctx.index_sync_ctx = index_sync_ctx;
+       sync_ctx.sync_view = sync_view;
+       sync_ctx.t = mail_index_transaction_begin(sync_view, FALSE);
 
-       lock_type = mail_index_sync_have_more(index_sync_ctx) ?
-               F_WRLCK : F_RDLCK;
-       if (mbox_lock(ibox, lock_type, &lock_id) > 0 &&
-           mbox_file_open_stream(ibox) == 0) {
-               ret = mbox_sync_do(ibox, index_sync_ctx, sync_view,
-                                  syncs, &sync_rec, index_synced);
-               if (ret == -2) {
-                       /* read lock -> write lock. do it again. */
-                       (void)mbox_unlock(ibox, lock_id);
-                       lock_id = 0;
-                       if (mbox_lock(ibox, F_WRLCK, &lock_id) <= 0)
-                               ret = -1;
-                       else if (mbox_file_open_stream(ibox) < 0)
-                               ret = -1;
-                       else {
-                               ret = mbox_sync_do(ibox, index_sync_ctx,
-                                                  sync_view, syncs, &sync_rec,
-                                                  FALSE);
-                       }
-               }
-       } else {
-               (void)mail_index_sync_end(index_sync_ctx);
+       sync_ctx.mails = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+       sync_ctx.syncs = buffer_create_dynamic(default_pool, 256, (size_t)-1);
+       sync_ctx.next_uid = 1;
+
+       ret = mail_index_get_header(sync_view, &sync_ctx.hdr);
+       i_assert(ret == 0);
+
+       if (mbox_sync_do(&sync_ctx, index_synced) < 0)
                ret = -1;
+
+       if (ret < 0)
+               mail_index_transaction_rollback(sync_ctx.t);
+       else if (mail_index_transaction_commit(sync_ctx.t, &seq, &offset) < 0)
+               ret = -1;
+       else {
+               ibox->commit_log_file_seq = 0;
+               ibox->commit_log_file_offset = 0;
        }
 
-       if (lock_id != 0) {
-               if (mbox_unlock(ibox, lock_id) < 0)
+       if (mail_index_sync_end(index_sync_ctx) < 0)
+               ret = -1;
+
+       if (sync_ctx.lock_id != 0) {
+               if (mbox_unlock(ibox, sync_ctx.lock_id) < 0)
                        ret = -1;
        }
 
-       buffer_free(syncs);
+       str_free(sync_ctx.header);
+       str_free(sync_ctx.from_line);
+       buffer_free(sync_ctx.mails);
+       buffer_free(sync_ctx.syncs);
        return ret;
 }