static int
get_modseq_next_offset_at(struct mail_transaction_log_file *file,
- uint64_t modseq,
+ uint64_t modseq, bool use_highest,
uoff_t *cur_offset, uint64_t *cur_modseq,
uoff_t *next_offset_r)
{
}
/* check sync_highest_modseq again in case sync_offset was updated */
- if (modseq >= file->sync_highest_modseq) {
+ if (modseq >= file->sync_highest_modseq && use_highest) {
*next_offset_r = file->sync_offset;
return 0;
}
cur_modseq = cache->highest_modseq;
}
- if ((ret = get_modseq_next_offset_at(file, modseq, &cur_offset,
+ if ((ret = get_modseq_next_offset_at(file, modseq, TRUE, &cur_offset,
&cur_modseq, next_offset_r)) <= 0)
return ret;
if (cur_offset == file->sync_offset) {
/* if we got to sync_offset, cur_modseq should be
sync_highest_modseq */
mail_index_set_error(file->log->index,
- "%s: Transaction log changed unexpectedly, "
- "can't get modseq", file->filepath);
- return -1;
+ "%s: Transaction log modseq tracking is corrupted - fixing",
+ file->filepath);
+ /* retry getting the offset by reading from the beginning
+ of the file */
+ cur_offset = file->hdr.hdr_size;
+ cur_modseq = file->hdr.initial_modseq;
+ ret = get_modseq_next_offset_at(file, modseq, FALSE,
+ &cur_offset, &cur_modseq,
+ next_offset_r);
+ if (ret < 0)
+ return -1;
+ i_assert(ret != 0);
+ /* get it fixed on the next sync */
+ file->log->index->need_recreate = TRUE;
+ file->need_rotate = TRUE;
+ /* clear cache, since it's unreliable */
+ memset(file->modseq_cache, 0, sizeof(file->modseq_cache));
}
/* @UNSAFE: cache the value */
test_end();
}
+static void
+test_mail_transaction_log_file_get_modseq_next_offset_inconsistency(void)
+{
+ test_begin("mail_transaction_log_file_get_modseq_next_offset() inconsistency");
+
+ struct mail_index *index = test_mail_index_open();
+ struct mail_transaction_log_file *file = index->log->head;
+ uint32_t seq;
+
+ /* add modseq=2 */
+ struct mail_index_view *view = mail_index_view_open(index);
+ struct mail_index_transaction *trans =
+ mail_index_transaction_begin(view, 0);
+ mail_index_append(trans, 1, &seq);
+ test_assert(mail_index_transaction_commit(&trans) == 0);
+ mail_index_view_close(&view);
+
+ /* emulate a broken mail_index_modseq_header header */
+ file->sync_highest_modseq = 3;
+
+ uoff_t next_offset;
+ test_expect_error_string("Transaction log modseq tracking is corrupted");
+ test_assert(mail_transaction_log_file_get_modseq_next_offset(file, 2, &next_offset) == 0);
+ test_expect_no_more_errors();
+ test_assert(next_offset == file->sync_offset);
+
+ mail_index_close(index);
+ mail_index_free(&index);
+ test_end();
+}
+
int main(void)
{
static void (*const test_functions[])(void) = {
test_mail_transaction_update_modseq,
test_mail_transaction_log_file_modseq_offsets,
+ test_mail_transaction_log_file_get_modseq_next_offset_inconsistency,
NULL
};
ioloop_time = 1;