/* 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;
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 =
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++;
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,
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;
{
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;
}
}
}
-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)
{
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;
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;
}