This nicely isolates the modseq_cache code to its own file.
mail-transaction-log.c \
mail-transaction-log-append.c \
mail-transaction-log-file.c \
+ mail-transaction-log-modseq.c \
mail-transaction-log-view.c \
mailbox-log.c
}
}
-static struct modseq_cache *
-modseq_cache_hit(struct mail_transaction_log_file *file, unsigned int idx)
-{
- struct modseq_cache cache;
-
- if (idx > 0) {
- /* @UNSAFE: move it to top */
- cache = file->modseq_cache[idx];
- memmove(file->modseq_cache + 1, file->modseq_cache,
- sizeof(*file->modseq_cache) * idx);
- file->modseq_cache[0] = cache;
- }
- return &file->modseq_cache[0];
-}
-
-static struct modseq_cache *
-modseq_cache_get_offset(struct mail_transaction_log_file *file, uoff_t offset)
-{
- unsigned int i, best = UINT_MAX;
-
- for (i = 0; i < N_ELEMENTS(file->modseq_cache); i++) {
- if (offset < file->modseq_cache[i].offset)
- continue;
-
- if (file->modseq_cache[i].offset == 0)
- return NULL;
-
- if (offset == file->modseq_cache[i].offset) {
- /* exact cache hit */
- return modseq_cache_hit(file, i);
- }
-
- if (best == UINT_MAX ||
- file->modseq_cache[i].offset <
- file->modseq_cache[best].offset)
- best = i;
- }
- if (best == UINT_MAX)
- return NULL;
- return &file->modseq_cache[best];
-}
-
-static struct modseq_cache *
-modseq_cache_get_modseq(struct mail_transaction_log_file *file, uint64_t modseq)
-{
- unsigned int i, best = UINT_MAX;
-
- for (i = 0; i < N_ELEMENTS(file->modseq_cache); i++) {
- if (modseq < file->modseq_cache[i].highest_modseq)
- continue;
-
- if (file->modseq_cache[i].offset == 0)
- return NULL;
-
- if (modseq == file->modseq_cache[i].highest_modseq) {
- /* exact cache hit */
- return modseq_cache_hit(file, i);
- }
-
- if (best == UINT_MAX ||
- file->modseq_cache[i].highest_modseq <
- file->modseq_cache[best].highest_modseq)
- best = i;
- }
- if (best == UINT_MAX)
- return NULL;
- return &file->modseq_cache[best];
-}
-
-static int
-log_get_synced_record(struct mail_transaction_log_file *file, uoff_t *offset,
- const struct mail_transaction_header **hdr_r,
- const char **error_r)
-{
- const struct mail_transaction_header *hdr;
- uint32_t trans_size;
-
- hdr = CONST_PTR_OFFSET(file->buffer->data,
- *offset - file->buffer_offset);
-
- /* we've already synced this record at some point. it should
- be valid. */
- trans_size = mail_index_offset_to_uint32(hdr->size);
- if (trans_size < sizeof(*hdr) ||
- *offset - file->buffer_offset + trans_size > file->buffer->used) {
- *error_r = t_strdup_printf(
- "Transaction log corrupted unexpectedly at "
- "%"PRIuUOFF_T": Invalid size %u (type=%x)",
- *offset, trans_size, hdr->type);
- mail_transaction_log_file_set_corrupted(file, "%s", *error_r);
- return -1;
- }
- *offset += trans_size;
- *hdr_r = hdr;
- return 0;
-}
-
-int mail_transaction_log_file_get_highest_modseq_at(
- struct mail_transaction_log_file *file,
- uoff_t offset, uint64_t *highest_modseq_r,
- const char **error_r)
-{
- const struct mail_transaction_header *hdr;
- struct modseq_cache *cache;
- uoff_t cur_offset;
- uint64_t cur_modseq;
- const char *reason;
- int ret;
-
- i_assert(offset <= file->sync_offset);
-
- if (offset == file->sync_offset) {
- *highest_modseq_r = file->sync_highest_modseq;
- return 1;
- }
-
- cache = modseq_cache_get_offset(file, offset);
- if (cache == NULL) {
- /* nothing usable in cache - scan from beginning */
- cur_offset = file->hdr.hdr_size;
- cur_modseq = file->hdr.initial_modseq;
- } else if (cache->offset == offset) {
- /* exact cache hit */
- *highest_modseq_r = cache->highest_modseq;
- return 1;
- } else {
- /* use cache to skip over some records */
- cur_offset = cache->offset;
- cur_modseq = cache->highest_modseq;
- }
-
- ret = mail_transaction_log_file_map(file, cur_offset, offset, &reason);
- if (ret <= 0) {
- *error_r = t_strdup_printf(
- "Failed to map transaction log %s for getting modseq "
- "at offset=%"PRIuUOFF_T" with start_offset=%"PRIuUOFF_T": %s",
- file->filepath, offset, cur_offset, reason);
- return ret;
- }
-
- i_assert(cur_offset >= file->buffer_offset);
- i_assert(cur_offset + file->buffer->used >= offset);
- while (cur_offset < offset) {
- if (log_get_synced_record(file, &cur_offset, &hdr, error_r) < 0)
- return 0;
- mail_transaction_update_modseq(hdr, hdr + 1, &cur_modseq,
- MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr));
- }
-
- /* @UNSAFE: cache the value */
- memmove(file->modseq_cache + 1, file->modseq_cache,
- sizeof(*file->modseq_cache) *
- (N_ELEMENTS(file->modseq_cache) - 1));
- file->modseq_cache[0].offset = cur_offset;
- file->modseq_cache[0].highest_modseq = cur_modseq;
-
- *highest_modseq_r = cur_modseq;
- return 1;
-}
-
-static int
-get_modseq_next_offset_at(struct mail_transaction_log_file *file,
- uint64_t modseq, bool use_highest,
- uoff_t *cur_offset, uint64_t *cur_modseq,
- uoff_t *next_offset_r)
-{
- const struct mail_transaction_header *hdr;
- const char *reason;
- int ret;
-
- /* make sure we've read until end of file. this is especially important
- with non-head logs which might only have been opened without being
- synced. */
- ret = mail_transaction_log_file_map(file, *cur_offset, UOFF_T_MAX, &reason);
- if (ret <= 0) {
- mail_index_set_error(file->log->index,
- "Failed to map transaction log %s for getting offset "
- "for modseq=%"PRIu64" with start_offset=%"PRIuUOFF_T": %s",
- file->filepath, modseq, *cur_offset, reason);
- return -1;
- }
-
- /* check sync_highest_modseq again in case sync_offset was updated */
- if (modseq >= file->sync_highest_modseq && use_highest) {
- *next_offset_r = file->sync_offset;
- return 0;
- }
-
- i_assert(*cur_offset >= file->buffer_offset);
- while (*cur_offset < file->sync_offset) {
- if (log_get_synced_record(file, cur_offset, &hdr, &reason) < 0) {
- mail_index_set_error(file->log->index,
- "%s: %s", file->filepath, reason);
- return -1;
- }
- mail_transaction_update_modseq(hdr, hdr + 1, cur_modseq,
- MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr));
- if (*cur_modseq >= modseq)
- break;
- }
- return 1;
-}
-
-int mail_transaction_log_file_get_modseq_next_offset(
- struct mail_transaction_log_file *file,
- uint64_t modseq, uoff_t *next_offset_r)
-{
- struct modseq_cache *cache;
- uoff_t cur_offset;
- uint64_t cur_modseq;
- int ret;
-
- if (modseq == file->sync_highest_modseq) {
- *next_offset_r = file->sync_offset;
- return 0;
- }
- if (modseq == file->hdr.initial_modseq) {
- *next_offset_r = file->hdr.hdr_size;
- return 0;
- }
-
- cache = modseq_cache_get_modseq(file, modseq);
- if (cache == NULL) {
- /* nothing usable in cache - scan from beginning */
- cur_offset = file->hdr.hdr_size;
- cur_modseq = file->hdr.initial_modseq;
- } else if (cache->highest_modseq == modseq) {
- /* exact cache hit */
- *next_offset_r = cache->offset;
- return 0;
- } else {
- /* use cache to skip over some records */
- cur_offset = cache->offset;
- cur_modseq = cache->highest_modseq;
- }
-
- 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 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 */
- if (file->log->index->need_recreate == NULL) {
- file->log->index->need_recreate =
- i_strdup("modseq tracking is corrupted");
- }
- if (file->need_rotate == NULL) {
- file->need_rotate =
- i_strdup("modseq tracking is corrupted");
- }
- /* clear cache, since it's unreliable */
- memset(file->modseq_cache, 0, sizeof(file->modseq_cache));
- }
-
- /* @UNSAFE: cache the value */
- memmove(file->modseq_cache + 1, file->modseq_cache,
- sizeof(*file->modseq_cache) *
- (N_ELEMENTS(file->modseq_cache) - 1));
- file->modseq_cache[0].offset = cur_offset;
- file->modseq_cache[0].highest_modseq = cur_modseq;
-
- *next_offset_r = cur_offset;
- return 0;
-}
-
static int
log_file_track_sync(struct mail_transaction_log_file *file,
const struct mail_transaction_header *hdr,
--- /dev/null
+/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mail-index-private.h"
+#include "mail-transaction-log-private.h"
+
+static struct modseq_cache *
+modseq_cache_hit(struct mail_transaction_log_file *file, unsigned int idx)
+{
+ struct modseq_cache cache;
+
+ if (idx > 0) {
+ /* @UNSAFE: move it to top */
+ cache = file->modseq_cache[idx];
+ memmove(file->modseq_cache + 1, file->modseq_cache,
+ sizeof(*file->modseq_cache) * idx);
+ file->modseq_cache[0] = cache;
+ }
+ return &file->modseq_cache[0];
+}
+
+static struct modseq_cache *
+modseq_cache_get_offset(struct mail_transaction_log_file *file, uoff_t offset)
+{
+ unsigned int i, best = UINT_MAX;
+
+ for (i = 0; i < N_ELEMENTS(file->modseq_cache); i++) {
+ if (offset < file->modseq_cache[i].offset)
+ continue;
+
+ if (file->modseq_cache[i].offset == 0)
+ return NULL;
+
+ if (offset == file->modseq_cache[i].offset) {
+ /* exact cache hit */
+ return modseq_cache_hit(file, i);
+ }
+
+ if (best == UINT_MAX ||
+ file->modseq_cache[i].offset <
+ file->modseq_cache[best].offset)
+ best = i;
+ }
+ if (best == UINT_MAX)
+ return NULL;
+ return &file->modseq_cache[best];
+}
+
+static struct modseq_cache *
+modseq_cache_get_modseq(struct mail_transaction_log_file *file, uint64_t modseq)
+{
+ unsigned int i, best = UINT_MAX;
+
+ for (i = 0; i < N_ELEMENTS(file->modseq_cache); i++) {
+ if (modseq < file->modseq_cache[i].highest_modseq)
+ continue;
+
+ if (file->modseq_cache[i].offset == 0)
+ return NULL;
+
+ if (modseq == file->modseq_cache[i].highest_modseq) {
+ /* exact cache hit */
+ return modseq_cache_hit(file, i);
+ }
+
+ if (best == UINT_MAX ||
+ file->modseq_cache[i].highest_modseq <
+ file->modseq_cache[best].highest_modseq)
+ best = i;
+ }
+ if (best == UINT_MAX)
+ return NULL;
+ return &file->modseq_cache[best];
+}
+
+static int
+log_get_synced_record(struct mail_transaction_log_file *file, uoff_t *offset,
+ const struct mail_transaction_header **hdr_r,
+ const char **error_r)
+{
+ const struct mail_transaction_header *hdr;
+ uint32_t trans_size;
+
+ hdr = CONST_PTR_OFFSET(file->buffer->data,
+ *offset - file->buffer_offset);
+
+ /* we've already synced this record at some point. it should
+ be valid. */
+ trans_size = mail_index_offset_to_uint32(hdr->size);
+ if (trans_size < sizeof(*hdr) ||
+ *offset - file->buffer_offset + trans_size > file->buffer->used) {
+ *error_r = t_strdup_printf(
+ "Transaction log corrupted unexpectedly at "
+ "%"PRIuUOFF_T": Invalid size %u (type=%x)",
+ *offset, trans_size, hdr->type);
+ mail_transaction_log_file_set_corrupted(file, "%s", *error_r);
+ return -1;
+ }
+ *offset += trans_size;
+ *hdr_r = hdr;
+ return 0;
+}
+
+int mail_transaction_log_file_get_highest_modseq_at(
+ struct mail_transaction_log_file *file,
+ uoff_t offset, uint64_t *highest_modseq_r,
+ const char **error_r)
+{
+ const struct mail_transaction_header *hdr;
+ struct modseq_cache *cache;
+ uoff_t cur_offset;
+ uint64_t cur_modseq;
+ const char *reason;
+ int ret;
+
+ i_assert(offset <= file->sync_offset);
+
+ if (offset == file->sync_offset) {
+ *highest_modseq_r = file->sync_highest_modseq;
+ return 1;
+ }
+
+ cache = modseq_cache_get_offset(file, offset);
+ if (cache == NULL) {
+ /* nothing usable in cache - scan from beginning */
+ cur_offset = file->hdr.hdr_size;
+ cur_modseq = file->hdr.initial_modseq;
+ } else if (cache->offset == offset) {
+ /* exact cache hit */
+ *highest_modseq_r = cache->highest_modseq;
+ return 1;
+ } else {
+ /* use cache to skip over some records */
+ cur_offset = cache->offset;
+ cur_modseq = cache->highest_modseq;
+ }
+
+ ret = mail_transaction_log_file_map(file, cur_offset, offset, &reason);
+ if (ret <= 0) {
+ *error_r = t_strdup_printf(
+ "Failed to map transaction log %s for getting modseq "
+ "at offset=%"PRIuUOFF_T" with start_offset=%"PRIuUOFF_T": %s",
+ file->filepath, offset, cur_offset, reason);
+ return ret;
+ }
+
+ i_assert(cur_offset >= file->buffer_offset);
+ i_assert(cur_offset + file->buffer->used >= offset);
+ while (cur_offset < offset) {
+ if (log_get_synced_record(file, &cur_offset, &hdr, error_r) < 0)
+ return 0;
+ mail_transaction_update_modseq(hdr, hdr + 1, &cur_modseq,
+ MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr));
+ }
+
+ /* @UNSAFE: cache the value */
+ memmove(file->modseq_cache + 1, file->modseq_cache,
+ sizeof(*file->modseq_cache) *
+ (N_ELEMENTS(file->modseq_cache) - 1));
+ file->modseq_cache[0].offset = cur_offset;
+ file->modseq_cache[0].highest_modseq = cur_modseq;
+
+ *highest_modseq_r = cur_modseq;
+ return 1;
+}
+
+static int
+get_modseq_next_offset_at(struct mail_transaction_log_file *file,
+ uint64_t modseq, bool use_highest,
+ uoff_t *cur_offset, uint64_t *cur_modseq,
+ uoff_t *next_offset_r)
+{
+ const struct mail_transaction_header *hdr;
+ const char *reason;
+ int ret;
+
+ /* make sure we've read until end of file. this is especially important
+ with non-head logs which might only have been opened without being
+ synced. */
+ ret = mail_transaction_log_file_map(file, *cur_offset, UOFF_T_MAX, &reason);
+ if (ret <= 0) {
+ mail_index_set_error(file->log->index,
+ "Failed to map transaction log %s for getting offset "
+ "for modseq=%"PRIu64" with start_offset=%"PRIuUOFF_T": %s",
+ file->filepath, modseq, *cur_offset, reason);
+ return -1;
+ }
+
+ /* check sync_highest_modseq again in case sync_offset was updated */
+ if (modseq >= file->sync_highest_modseq && use_highest) {
+ *next_offset_r = file->sync_offset;
+ return 0;
+ }
+
+ i_assert(*cur_offset >= file->buffer_offset);
+ while (*cur_offset < file->sync_offset) {
+ if (log_get_synced_record(file, cur_offset, &hdr, &reason) < 0) {
+ mail_index_set_error(file->log->index,
+ "%s: %s", file->filepath, reason);
+ return -1;
+ }
+ mail_transaction_update_modseq(hdr, hdr + 1, cur_modseq,
+ MAIL_TRANSACTION_LOG_HDR_VERSION(&file->hdr));
+ if (*cur_modseq >= modseq)
+ break;
+ }
+ return 1;
+}
+
+int mail_transaction_log_file_get_modseq_next_offset(
+ struct mail_transaction_log_file *file,
+ uint64_t modseq, uoff_t *next_offset_r)
+{
+ struct modseq_cache *cache;
+ uoff_t cur_offset;
+ uint64_t cur_modseq;
+ int ret;
+
+ if (modseq == file->sync_highest_modseq) {
+ *next_offset_r = file->sync_offset;
+ return 0;
+ }
+ if (modseq == file->hdr.initial_modseq) {
+ *next_offset_r = file->hdr.hdr_size;
+ return 0;
+ }
+
+ cache = modseq_cache_get_modseq(file, modseq);
+ if (cache == NULL) {
+ /* nothing usable in cache - scan from beginning */
+ cur_offset = file->hdr.hdr_size;
+ cur_modseq = file->hdr.initial_modseq;
+ } else if (cache->highest_modseq == modseq) {
+ /* exact cache hit */
+ *next_offset_r = cache->offset;
+ return 0;
+ } else {
+ /* use cache to skip over some records */
+ cur_offset = cache->offset;
+ cur_modseq = cache->highest_modseq;
+ }
+
+ 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 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 */
+ if (file->log->index->need_recreate == NULL) {
+ file->log->index->need_recreate =
+ i_strdup("modseq tracking is corrupted");
+ }
+ if (file->need_rotate == NULL) {
+ file->need_rotate =
+ i_strdup("modseq tracking is corrupted");
+ }
+ /* clear cache, since it's unreliable */
+ memset(file->modseq_cache, 0, sizeof(file->modseq_cache));
+ }
+
+ /* @UNSAFE: cache the value */
+ memmove(file->modseq_cache + 1, file->modseq_cache,
+ sizeof(*file->modseq_cache) *
+ (N_ELEMENTS(file->modseq_cache) - 1));
+ file->modseq_cache[0].offset = cur_offset;
+ file->modseq_cache[0].highest_modseq = cur_modseq;
+
+ *next_offset_r = cur_offset;
+ return 0;
+}