struct mail_cache_header {
uint32_t indexid;
+ uint32_t sync_id;
+
uint32_t continued_record_count;
uint32_t used_file_size;
void *mmap_base;
size_t mmap_length;
uint32_t used_file_size;
+ uint32_t sync_id;
struct mail_cache_header *header;
if (cache->mmap_length < sizeof(struct mail_cache_header))
return mail_cache_set_corrupted(cache, "File too small");
cache->header = hdr = cache->mmap_base;
+ cache->sync_id = hdr->sync_id;
if (cache->header->indexid != cache->index->indexid) {
/* index id changed */
static int mmap_update_nocheck(struct mail_cache *cache,
size_t offset, size_t size)
{
- if (cache->header != NULL &&
- cache->header->indexid != cache->index->indexid) {
- /* indexid changed, most likely it was rebuilt.
- try reopening. */
+ struct stat st;
+
+ /* if sync id has changed, the file has to be reopened.
+ note that if main index isn't locked, it may change again */
+ if (cache->sync_id != cache->index->cache_sync_id &&
+ cache->mmap_base != NULL) {
if (!mail_cache_file_reopen(cache))
return -1;
-
- /* force mmap refresh */
- size = 0;
}
- if (size != 0 && offset < cache->mmap_length &&
- size <= cache->mmap_length - offset) {
+ if (offset < cache->mmap_length &&
+ size <= cache->mmap_length - offset &&
+ !cache->mmap_refresh) {
/* already mapped */
- if (!cache->mmap_refresh)
+ if (size != 0 || cache->anon_mmap)
return 1;
- cache->mmap_refresh = FALSE;
+ /* requesting the whole file - see if we need to
+ re-mmap */
+ if (fstat(cache->fd, &st) < 0) {
+ mail_cache_set_syscall_error(cache, "fstat()");
+ return -1;
+ }
+ if ((uoff_t)st.st_size == cache->mmap_length)
+ return 1;
}
+ cache->mmap_refresh = FALSE;
if (cache->anon_mmap)
return 1;
static int mmap_update(struct mail_cache *cache, size_t offset, size_t size)
{
- int ret;
+ int synced, ret;
- ret = mmap_update_nocheck(cache, offset, size);
- if (ret > 0)
- return TRUE;
- if (ret < 0)
- return FALSE;
- return mmap_verify_header(cache);
+ for (synced = FALSE;; synced = TRUE) {
+ ret = mmap_update_nocheck(cache, offset, size);
+ if (ret > 0)
+ return TRUE;
+ if (ret < 0)
+ return FALSE;
+
+ if (!mmap_verify_header(cache))
+ return FALSE;
+
+ /* see if cache file was rebuilt - do it only once to avoid
+ infinite looping */
+ if (cache->header->sync_id == cache->index->cache_sync_id ||
+ synced)
+ break;
+
+ if (!mail_cache_file_reopen(cache))
+ return FALSE;
+ }
+ return TRUE;
}
static int mail_cache_open_and_verify(struct mail_cache *cache, int silent)
memset(&hdr, 0, sizeof(hdr));
hdr.indexid = index->indexid;
+ hdr.sync_id = index->cache_sync_id;
hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
cache = i_new(struct mail_cache, 1);
/* update header */
hdr->indexid = cache->index->indexid;
+ hdr->sync_id = cache->sync_id = cache->index->cache_sync_id =
+ ++cache->index->header->cache_sync_id;
hdr->used_file_size = uint32_to_nbo(offset);
hdr->used_fields = used_fields;
hdr->field_usage_start = uint32_to_nbo(ioloop_time);
i_assert(cache->trans_ctx == NULL);
+ if (cache->anon_mmap)
+ return TRUE;
+
if (!cache->index->set_lock(cache->index, MAIL_LOCK_EXCLUSIVE))
return FALSE;
if (mail_cache_lock(cache, TRUE) <= 0)
return FALSE;
+#ifdef DEBUG
+ i_warning("Compressing cache file %s", cache->filepath);
+#endif
+
fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
MAIL_CACHE_LOCK_STALE_TIMEOUT, NULL, NULL);
if (fd == -1) {
struct mail_cache_header hdr;
int ret, fd;
+ i_assert(cache->index->lock_type == MAIL_LOCK_EXCLUSIVE);
+
memset(&hdr, 0, sizeof(hdr));
hdr.indexid = cache->index->indexid;
+ hdr.sync_id = cache->sync_id = cache->index->cache_sync_id
+ =++cache->index->header->cache_sync_id;
hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
cache->used_file_size = sizeof(hdr);
}
if (ret > 0) {
- if (!mmap_verify_header(cache)) {
+ if (!mmap_update(cache, 0, 0)) {
(void)mail_cache_unlock(cache);
- ret = -1;
+ return -1;
+ }
+ if (cache->sync_id != cache->index->cache_sync_id) {
+ /* we have the cache file locked and sync_id still
+ doesn't match. it means we crashed between updating
+ cache file and updating sync_id in index header.
+ just update the sync_ids so they match. */
+ i_warning("Updating broken sync_id in cache file %s",
+ cache->filepath);
+ cache->sync_id = cache->header->sync_id =
+ cache->index->cache_sync_id;
}
}
return ret;
return FALSE;
}
- index->sync_id = hdr->sync_id;
+ index->master_sync_id = hdr->master_sync_id;
+ index->cache_sync_id = hdr->cache_sync_id;
+ index->log_sync_id = hdr->log_sync_id;
index->sync_stamp = hdr->sync_stamp;
index->sync_size = hdr->sync_size;
index->mmap_used_length = hdr->used_file_size;
if (index->mmap_base != NULL) {
index->header = (struct mail_index_header *) index->mmap_base;
+ index->cache_sync_id = index->header->cache_sync_id;
+ index->log_sync_id = index->header->log_sync_id;
+
if (index->mmap_invalidate) {
if (msync(index->mmap_base,
index->mmap_used_length,
}
/* make sure file size hasn't changed */
- if (index->header->sync_id == index->sync_id) {
+ if (index->header->master_sync_id == index->master_sync_id) {
index->mmap_used_length = index->header->used_file_size;
if (index->mmap_used_length > index->mmap_full_length) {
i_panic("Index file size was grown without "
/* file size changed, let others know about it too by changing
sync_id in header. */
- index->header->sync_id++;
+ index->header->master_sync_id++;
if (!mail_index_mmap_update(index))
return FALSE;
uint8_t compat_data[4];
uint32_t indexid;
-
uint32_t used_file_size;
- uint32_t sync_id; /* re-mmap() when changed, required only
- if file size is shrinked */
+
+ /* file needs to be reopened if sync_ids change. */
+ uint32_t master_sync_id;
+ uint32_t cache_sync_id;
+ uint32_t log_sync_id;
uint32_t flags;
char *mailbox_path; /* file/directory for mailbox location */
char *control_dir; /* destination for control files */
unsigned int indexid;
- unsigned int sync_id;
+ unsigned int master_sync_id, cache_sync_id, log_sync_id;
/* updated whenever exclusive lock is set/unset */
unsigned int excl_lock_counter;
members.. */
#define MAIL_INDEX_PRIVATE_FILL \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, { 0, 0, 0 }, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, { 0, 0, 0 }, 0, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
#endif
/* defaults - same as above but prefixed with mail_index_. */