changeset 2200:97bb7b127617 HEAD

Beginnings of getting cache file working again. Easy to break currently, but basics seem to work.
author Timo Sirainen <tss@iki.fi>
date Sun, 20 Jun 2004 11:05:41 +0300
parents 89001f106f8d
children 7bdef5ea4591
files src/lib-index/mail-cache-compress.c src/lib-index/mail-cache-lookup.c src/lib-index/mail-cache-old.c src/lib-index/mail-cache-private.h src/lib-index/mail-cache-transaction.c src/lib-index/mail-cache.c src/lib-index/mail-cache.h src/lib-index/mail-index.c src/lib-storage/index/index-mail.c src/lib-storage/index/index-storage.c src/lib-storage/index/index-transaction.c
diffstat 11 files changed, 176 insertions(+), 1181 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-cache-compress.c	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-index/mail-cache-compress.c	Sun Jun 20 11:05:41 2004 +0300
@@ -241,7 +241,7 @@
 			  MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE);
 	}
 
-	if (!mail_cache_unlock(cache))
+	if (mail_cache_unlock(cache) < 0)
 		ret = FALSE;
 
 	return ret;
--- a/src/lib-index/mail-cache-lookup.c	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-index/mail-cache-lookup.c	Sun Jun 20 11:05:41 2004 +0300
@@ -5,7 +5,6 @@
 #include "byteorder.h"
 #include "mail-cache-private.h"
 
-#if 0
 const char *
 mail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx)
 {
@@ -17,7 +16,7 @@
 	if (offset == 0)
 		return NULL;
 
-	if (!mmap_update(cache, offset, 1024))
+	if (mail_cache_mmap_update(cache, offset, 1024) < 0)
 		return NULL;
 
 	if (offset + sizeof(data_size) > cache->mmap_length) {
@@ -37,7 +36,7 @@
 		return NULL;
 	}
 
-	if (!mmap_update(cache, offset, data_size))
+	if (mail_cache_mmap_update(cache, offset, data_size) < 0)
 		return NULL;
 
 	if (offset + data_size > cache->mmap_length) {
@@ -107,17 +106,20 @@
 }
 
 struct mail_cache_record *
-mail_cache_get_record(struct mail_cache *cache, uint32_t offset)
+mail_cache_get_record(struct mail_cache *cache, uint32_t offset,
+		      int index_offset)
 {
 #define CACHE_PREFETCH 1024
 	struct mail_cache_record *cache_rec;
 	size_t size;
 
-	offset = mail_cache_offset_to_uint32(offset);
+	if (!index_offset)
+		offset = mail_cache_offset_to_uint32(offset);
 	if (offset == 0)
 		return NULL;
 
-	if (!mmap_update(cache, offset, sizeof(*cache_rec) + CACHE_PREFETCH))
+	if (mail_cache_mmap_update(cache, offset,
+				   sizeof(*cache_rec) + CACHE_PREFETCH) < 0)
 		return NULL;
 
 	if (offset + sizeof(*cache_rec) > cache->mmap_length) {
@@ -132,7 +134,7 @@
 		return NULL;
 	}
 	if (size > CACHE_PREFETCH) {
-		if (!mmap_update(cache, offset, size))
+		if (mail_cache_mmap_update(cache, offset, size) < 0)
 			return NULL;
 	}
 
@@ -149,7 +151,7 @@
 {
 	struct mail_cache_record *next;
 
-	next = mail_cache_get_record(cache, rec->next_offset);
+	next = mail_cache_get_record(cache, rec->next_offset, FALSE);
 	if (next != NULL && next <= rec) {
 		mail_cache_set_corrupted(cache, "next_offset points backwards");
 		return NULL;
@@ -165,11 +167,13 @@
 
 	if (mail_cache_transaction_autocommit(view, seq, fields) < 0)
 		return NULL;
-	// FIXME: check cache_offset in transaction
-	if (mail_index_lookup_latest(view->view, seq, &rec) < 0)
+	/* FIXME: check cache_offset in transaction
+	   FIXME: if rec doesn't point to header record, the file seq may
+	   be different and the offset wrong */
+	if (mail_index_lookup(view->view, seq, &rec) < 0)
 		return NULL;
 
-	return mail_cache_get_record(view->cache, rec->cache_offset);
+	return mail_cache_get_record(view->cache, rec->cache_offset, TRUE);
 }
 
 enum mail_cache_field
@@ -318,5 +322,10 @@
 	memcpy(buffer, data, buffer_size);
 	return TRUE;
 }
-#else
-#endif
+
+enum mail_cache_record_flag
+mail_cache_get_record_flags(struct mail_cache_view *view, uint32_t seq)
+{
+	// FIXME:
+	return 0;
+}
--- a/src/lib-index/mail-cache-old.c	Sun Jun 20 09:33:08 2004 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,935 +0,0 @@
-static const char *
-mail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx);
-static int mail_cache_write(struct mail_cache_transaction_ctx *ctx);
-static struct mail_cache_record *
-mail_cache_lookup(struct mail_cache *cache,
-		  const struct mail_index_record *rec,
-		  enum mail_cache_field fields);
-
-static void mail_cache_file_close(struct mail_cache *cache)
-{
-	if (cache->mmap_base != NULL) {
-		if (munmap(cache->mmap_base, cache->mmap_length) < 0)
-			mail_cache_set_syscall_error(cache, "munmap()");
-	}
-
-	cache->mmap_base = NULL;
-	cache->hdr = NULL;
-	cache->mmap_length = 0;
-
-	if (cache->fd != -1) {
-		if (close(cache->fd) < 0)
-			mail_cache_set_syscall_error(cache, "close()");
-		cache->fd = -1;
-	}
-}
-
-static int mail_cache_file_reopen(struct mail_cache *cache)
-{
-	int fd;
-
-	fd = open(cache->filepath, O_RDWR);
-	if (fd == -1) {
-		mail_cache_set_syscall_error(cache, "open()");
-		return -1;
-	}
-
-	mail_cache_file_close(cache);
-
-	cache->fd = fd;
-	return 0;
-}
-
-static int mmap_verify_header(struct mail_cache *cache)
-{
-	struct mail_cache_header *hdr;
-
-	/* check that the header is still ok */
-	if (cache->mmap_length < sizeof(struct mail_cache_header)) {
-		mail_cache_set_corrupted(cache, "File too small");
-		return 0;
-	}
-	cache->hdr = hdr = cache->mmap_base;
-
-	if (cache->hdr->indexid != cache->index->indexid) {
-		/* index id changed */
-		if (cache->hdr->indexid != 0)
-			mail_cache_set_corrupted(cache, "indexid changed");
-		return 0;
-	}
-
-	if (cache->trans_ctx != NULL) {
-		/* we've updated used_file_size, do nothing */
-		return 1;
-	}
-
-	cache->used_file_size = nbo_to_uint32(hdr->used_file_size);
-
-	/* only check the header if we're locked */
-	if (cache->locks == 0)
-		return 1;
-
-	if (cache->used_file_size < sizeof(struct mail_cache_header)) {
-		mail_cache_set_corrupted(cache, "used_file_size too small");
-		return 0;
-	}
-	if ((cache->used_file_size % sizeof(uint32_t)) != 0) {
-		mail_cache_set_corrupted(cache, "used_file_size not aligned");
-		return 0;
-	}
-
-	if (cache->used_file_size > cache->mmap_length) {
-		/* maybe a crash truncated the file - just fix it */
-		hdr->used_file_size = uint32_to_nbo(cache->mmap_length & ~3);
-		if (msync(cache->mmap_base, sizeof(*hdr), MS_SYNC) < 0) {
-			mail_cache_set_syscall_error(cache, "msync()");
-			return -1;
-		}
-	}
-	return 1;
-}
-
-static int mmap_update_nocheck(struct mail_cache *cache,
-			       size_t offset, size_t size)
-{
-	struct stat st;
-
-	/* if sequence has changed, the file has to be reopened.
-	   note that if main index isn't locked, it may change again */
-	if (cache->hdr->file_seq != cache->index->hdr->cache_file_seq &&
-	    cache->mmap_base != NULL) {
-		if (!mail_cache_file_reopen(cache))
-			return -1;
-	}
-
-	if (offset < cache->mmap_length &&
-	    size <= cache->mmap_length - offset &&
-	    !cache->mmap_refresh) {
-		/* already mapped */
-		if (size != 0 || cache->anon_mmap)
-			return 1;
-
-		/* 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;
-
-	if (cache->mmap_base != NULL) {
-		if (cache->locks != 0) {
-			/* in the middle of transaction - write the changes */
-			if (msync(cache->mmap_base, cache->mmap_length,
-				  MS_SYNC) < 0) {
-				mail_cache_set_syscall_error(cache, "msync()");
-				return -1;
-			}
-		}
-
-		if (munmap(cache->mmap_base, cache->mmap_length) < 0)
-			mail_cache_set_syscall_error(cache, "munmap()");
-	}
-
-	i_assert(cache->fd != -1);
-
-	/* map the whole file */
-	cache->hdr = NULL;
-	cache->mmap_length = 0;
-
-	cache->mmap_base = mmap_rw_file(cache->fd, &cache->mmap_length);
-	if (cache->mmap_base == MAP_FAILED) {
-		cache->mmap_base = NULL;
-		mail_cache_set_syscall_error(cache, "mmap()");
-		return -1;
-	}
-
-	/* re-mmaped, check header */
-	return 0;
-}
-
-static int mmap_update(struct mail_cache *cache, size_t offset, size_t size)
-{
-	int synced, ret;
-
-	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->hdr->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)
-{
-	struct stat st;
-
-	mail_cache_file_close(cache);
-
-	cache->fd = open(cache->filepath, O_RDWR);
-	if (cache->fd == -1) {
-		if (errno == ENOENT)
-			return 0;
-
-		mail_cache_set_syscall_error(cache, "open()");
-		return -1;
-	}
-
-	if (fstat(cache->fd, &st) < 0) {
-		mail_cache_set_syscall_error(cache, "fstat()");
-		return -1;
-	}
-
-	if (st.st_size < sizeof(struct mail_cache_header))
-		return 0;
-
-	cache->mmap_refresh = TRUE;
-	if (mmap_update_nocheck(cache, 0, sizeof(struct mail_cache_header)) < 0)
-		return -1;
-
-	/* verify that this really is the cache for wanted index */
-	cache->silent = silent;
-	if (!mmap_verify_header(cache)) {
-		cache->silent = FALSE;
-		return 0;
-	}
-
-	cache->silent = FALSE;
-	return 1;
-}
-
-static int mail_cache_open_or_create_file(struct mail_cache *cache,
-					  struct mail_cache_header *hdr)
-{
-	int ret, fd;
-
-	cache->filepath = i_strconcat(cache->index->filepath,
-				      MAIL_CACHE_FILE_PREFIX, NULL);
-
-	ret = mail_cache_open_and_verify(cache, FALSE);
-	if (ret != 0)
-		return ret > 0;
-
-	/* we'll have to clear cache_offsets which requires exclusive lock */
-	if (!mail_index_set_lock(cache->index, MAIL_LOCK_EXCLUSIVE))
-		return FALSE;
-
-	/* maybe a rebuild.. */
-	fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
-			       MAIL_CACHE_LOCK_CHANGE_TIMEOUT,
-			       MAIL_CACHE_LOCK_IMMEDIATE_TIMEOUT, NULL, NULL);
-	if (fd == -1) {
-		mail_cache_set_syscall_error(cache, "file_dotlock_open()");
-		return FALSE;
-	}
-
-	/* see if someone else just created the cache file */
-	ret = mail_cache_open_and_verify(cache, TRUE);
-	if (ret != 0) {
-		(void)file_dotlock_delete(cache->filepath, fd);
-		return ret > 0;
-	}
-
-	/* rebuild then */
-	if (write_full(fd, hdr, sizeof(*hdr)) < 0) {
-		mail_cache_set_syscall_error(cache, "write_full()");
-		(void)file_dotlock_delete(cache->filepath, fd);
-		return FALSE;
-	}
-	if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
-		mail_cache_set_syscall_error(cache, "file_set_size()");
-		(void)file_dotlock_delete(cache->filepath, fd);
-		return FALSE;
-	}
-
-	if (cache->index->hdr.cache_file_seq != 0) {
-		// FIXME: recreate index file with cache_offsets cleared
-	}
-
-	mail_cache_file_close(cache);
-	cache->fd = dup(fd);
-
-	if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
-		mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
-		return FALSE;
-	}
-
-	if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
-		return FALSE;
-
-	return TRUE;
-}
-
-int mail_cache_open_or_create(struct mail_index *index)
-{
-        struct mail_cache_header hdr;
-	struct mail_cache *cache;
-
-	memset(&hdr, 0, sizeof(hdr));
-	hdr.indexid = index->indexid;
-	hdr.sync_id = index->hdr->cache_file_seq; // FIXME
-	hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
-
-	cache = i_new(struct mail_cache, 1);
-	cache->index = index;
-	cache->fd = -1;
-        cache->split_header_pool = pool_alloconly_create("Headers", 512);
-
-	index->cache = cache;
-
-	/* we'll do anon-mmaping only if initially requested. if we fail
-	   because of out of disk space, we'll just let the main index code
-	   know it and fail. */
-	if (!mail_cache_open_or_create_file(cache, &hdr)) {
-		mail_cache_free(cache);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-void mail_cache_free(struct mail_cache *cache)
-{
-	i_assert(cache->trans_ctx == NULL);
-
-	cache->index->cache = NULL;
-
-	mail_cache_file_close(cache);
-
-	pool_unref(cache->split_header_pool);
-	i_free(cache->filepath);
-	i_free(cache);
-}
-
-void mail_cache_set_defaults(struct mail_cache *cache,
-			     enum mail_cache_field default_cache_fields,
-			     enum mail_cache_field never_cache_fields)
-{
-	cache->default_cache_fields = default_cache_fields;
-	cache->never_cache_fields = never_cache_fields;
-}
-
-int mail_cache_reset(struct mail_cache *cache)
-{
-	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->hdr->cache_sync_id;
-	hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
-	cache->used_file_size = sizeof(hdr);
-
-	fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
-			       MAIL_CACHE_LOCK_CHANGE_TIMEOUT,
-			       MAIL_CACHE_LOCK_IMMEDIATE_TIMEOUT, NULL, NULL);
-	if (fd == -1) {
-		mail_cache_set_syscall_error(cache, "file_dotlock_open()");
-		return -1;
-	}
-
-	if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
-		mail_cache_set_syscall_error(cache, "write_full()");
-		(void)file_dotlock_delete(cache->filepath, fd);
-		return -1;
-	}
-	if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
-		mail_cache_set_syscall_error(cache, "file_set_size()");
-		(void)file_dotlock_delete(cache->filepath, fd);
-		return -1;
-	}
-
-	mail_cache_file_close(cache);
-	cache->fd = dup(fd);
-
-	if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
-		mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
-		return -1;
-	}
-
-	cache->mmap_refresh = TRUE;
-	if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
-		return -1;
-
-	return 0;
-}
-
-int mail_cache_lock(struct mail_cache *cache, int nonblock)
-{
-	int ret;
-
-	if (cache->locks++ != 0)
-		return TRUE;
-
-	if (cache->anon_mmap)
-		return TRUE;
-
-	if (nonblock) {
-		ret = file_try_lock(cache->fd, F_WRLCK);
-		if (ret < 0)
-			mail_cache_set_syscall_error(cache, "file_try_lock()");
-	} else {
-		ret = file_wait_lock(cache->fd, F_WRLCK);
-		if (ret <= 0)
-			mail_cache_set_syscall_error(cache, "file_wait_lock()");
-	}
-
-	if (ret > 0) {
-		if (!mmap_update(cache, 0, 0)) {
-			(void)mail_cache_unlock(cache);
-			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->hdr->sync_id =
-				cache->index->cache_sync_id;
-		}
-	}
-	return ret;
-}
-
-int mail_cache_unlock(struct mail_cache *cache)
-{
-	if (--cache->locks > 0)
-		return TRUE;
-
-	if (cache->anon_mmap)
-		return TRUE;
-
-	if (file_wait_lock(cache->fd, F_UNLCK) <= 0) {
-		mail_cache_set_syscall_error(cache, "file_wait_lock(F_UNLCK)");
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-int mail_cache_is_locked(struct mail_cache *cache)
-{
-	return cache->locks > 0;
-}
-
-struct mail_cache_view *
-mail_cache_view_open(struct mail_cache *cache, struct mail_index_view *view)
-{
-	struct mail_cache_view *view;
-
-	view = i_new(struct mail_cache_view, 1);
-	view->cache = cache;
-	view->view = view;
-	return view;
-}
-
-void mail_cache_view_close(struct mail_cache_view *view)
-{
-	i_free(view);
-}
-
-static const char *
-mail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx)
-{
-	uint32_t offset, data_size;
-	unsigned char *buf;
-
-	offset = offset_to_uint32(cache->hdr->header_offsets[idx]);
-
-	if (offset == 0)
-		return NULL;
-
-	if (!mmap_update(cache, offset, 1024))
-		return NULL;
-
-	if (offset + sizeof(data_size) > cache->mmap_length) {
-		mail_cache_set_corrupted(cache, "Header %u points outside file",
-					 idx);
-		return NULL;
-	}
-
-	buf = cache->mmap_base;
-	memcpy(&data_size, buf + offset, sizeof(data_size));
-	data_size = nbo_to_uint32(data_size);
-	offset += sizeof(data_size);
-
-	if (data_size == 0) {
-		mail_cache_set_corrupted(cache,
-			"Header %u points to empty string", idx);
-		return NULL;
-	}
-
-	if (!mmap_update(cache, offset, data_size))
-		return NULL;
-
-	if (offset + data_size > cache->mmap_length) {
-		mail_cache_set_corrupted(cache, "Header %u points outside file",
-					 idx);
-		return NULL;
-	}
-
-	buf = cache->mmap_base;
-	if (buf[offset + data_size - 1] != '\0') {
-		mail_cache_set_corrupted(cache,
-			"Header %u points to invalid string", idx);
-		return NULL;
-	}
-
-	return buf + offset;
-}
-
-static const char *const *
-split_header(struct mail_cache *cache, const char *header)
-{
-	const char *const *arr, *const *tmp;
-	const char *null = NULL;
-	char *str;
-	buffer_t *buf;
-
-	if (header == NULL)
-		return NULL;
-
-	arr = t_strsplit(header, "\n");
-	buf = buffer_create_dynamic(cache->split_header_pool, 32, (size_t)-1);
-	for (tmp = arr; *tmp != NULL; tmp++) {
-		str = p_strdup(cache->split_header_pool, *tmp);
-		buffer_append(buf, &str, sizeof(str));
-	}
-	buffer_append(buf, &null, sizeof(null));
-
-	return buffer_get_data(buf, NULL);
-}
-
-const char *const *mail_cache_get_header_fields(struct mail_cache *cache,
-						unsigned int idx)
-{
-	const char *str;
-	int i;
-
-	i_assert(idx < MAIL_CACHE_HEADERS_COUNT);
-
-	/* t_strsplit() is a bit slow, so we cache it */
-	if (cache->hdr->header_offsets[idx] != cache->split_offsets[idx]) {
-		p_clear(cache->split_header_pool);
-
-		t_push();
-		for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
-			cache->split_offsets[i] =
-				cache->hdr->header_offsets[i];
-
-			str = mail_cache_get_header_fields_str(cache, i);
-			cache->split_headers[i] = split_header(cache, str);
-		}
-		t_pop();
-	}
-
-	return cache->split_headers[idx];
-}
-
-static const char *write_header_string(const char *const headers[],
-				       uint32_t *size_r)
-{
-	buffer_t *buffer;
-	size_t size;
-
-	buffer = buffer_create_dynamic(pool_datastack_create(),
-				       512, (size_t)-1);
-
-	while (*headers != NULL) {
-		if (buffer_get_used_size(buffer) != 0)
-			buffer_append(buffer, "\n", 1);
-		buffer_append(buffer, *headers, strlen(*headers));
-		headers++;
-	}
-	buffer_append(buffer, null4, 1);
-
-	size = buffer_get_used_size(buffer);
-	if ((size & 3) != 0) {
-		buffer_append(buffer, null4, 4 - (size & 3));
-		size += 4 - (size & 3);
-	}
-	*size_r = size;
-	return buffer_get_data(buffer, NULL);
-}
-
-int mail_cache_set_header_fields(struct mail_cache_transaction_ctx *ctx,
-				 unsigned int idx, const char *const headers[])
-{
-	struct mail_cache *cache = ctx->cache;
-	uint32_t offset, update_offset, size;
-	const char *header_str, *prev_str;
-
-	i_assert(*headers != NULL);
-	i_assert(idx < MAIL_CACHE_HEADERS_COUNT);
-	i_assert(idx >= ctx->next_unused_header_lowwater);
-	i_assert(offset_to_uint32(cache->hdr->header_offsets[idx]) == 0);
-
-	t_push();
-
-	header_str = write_header_string(headers, &size);
-	if (idx != 0) {
-		prev_str = mail_cache_get_header_fields_str(cache, idx-1);
-		if (prev_str == NULL) {
-			t_pop();
-			return FALSE;
-		}
-
-		i_assert(strcmp(header_str, prev_str) != 0);
-	}
-
-	offset = mail_cache_append_space(ctx, size + sizeof(uint32_t));
-	if (offset != 0) {
-		memcpy((char *) cache->mmap_base + offset + sizeof(uint32_t),
-		       header_str, size);
-
-		size = uint32_to_nbo(size);
-		memcpy((char *) cache->mmap_base + offset,
-		       &size, sizeof(uint32_t));
-
-		/* update cached headers */
-		cache->split_offsets[idx] = cache->hdr->header_offsets[idx];
-		cache->split_headers[idx] = split_header(cache, header_str);
-
-		/* mark used-bit to be updated later. not really needed for
-		   read-safety, but if transaction get rolled back we can't let
-		   this point to invalid location. */
-		update_offset = (char *) &cache->hdr->header_offsets[idx] -
-			(char *) cache->mmap_base;
-		mark_update(&ctx->cache_marks, update_offset,
-			    uint32_to_offset(offset));
-
-		/* make sure get_header_fields() still works for this header
-		   while the transaction isn't yet committed. */
-		ctx->next_unused_header_lowwater = idx + 1;
-	}
-
-	t_pop();
-	return offset > 0;
-}
-
-static struct mail_cache_record *
-cache_get_record(struct mail_cache *cache, uint32_t offset)
-{
-#define CACHE_PREFETCH 1024
-	struct mail_cache_record *cache_rec;
-	size_t size;
-
-	offset = offset_to_uint32(offset);
-	if (offset == 0)
-		return NULL;
-
-	if (!mmap_update(cache, offset, sizeof(*cache_rec) + CACHE_PREFETCH))
-		return NULL;
-
-	if (offset + sizeof(*cache_rec) > cache->mmap_length) {
-		mail_cache_set_corrupted(cache, "record points outside file");
-		return NULL;
-	}
-	cache_rec = CACHE_RECORD(cache, offset);
-
-	size = nbo_to_uint32(cache_rec->size);
-	if (size < sizeof(*cache_rec)) {
-		mail_cache_set_corrupted(cache, "invalid record size");
-		return NULL;
-	}
-	if (size > CACHE_PREFETCH) {
-		if (!mmap_update(cache, offset, size))
-			return NULL;
-	}
-
-	if (offset + size > cache->mmap_length) {
-		mail_cache_set_corrupted(cache, "record points outside file");
-		return NULL;
-	}
-	return cache_rec;
-}
-
-static struct mail_cache_record *
-cache_get_next_record(struct mail_cache *cache, struct mail_cache_record *rec)
-{
-	struct mail_cache_record *next;
-
-	next = cache_get_record(cache, rec->next_offset);
-	if (next != NULL && next <= rec) {
-		mail_cache_set_corrupted(cache, "next_offset points backwards");
-		return NULL;
-	}
-	return next;
-}
-
-static struct mail_cache_record *
-mail_cache_lookup(struct mail_cache *cache, const struct mail_index_record *rec,
-		  enum mail_cache_field fields)
-{
-	struct mail_cache_record *cache_rec;
-	unsigned int idx;
-
-	if (cache->trans_ctx != NULL &&
-	    cache->trans_ctx->first_uid <= rec->uid &&
-	    cache->trans_ctx->last_uid >= rec->uid &&
-	    (cache->trans_ctx->prev_uid != rec->uid || fields == 0 ||
-	     (cache->trans_ctx->prev_fields & fields) != 0)) {
-		/* we have to auto-commit since we're not capable of looking
-		   into uncommitted records. it would be possible by checking
-		   index_marks and cache_marks, but it's just more trouble
-		   than worth. */
-		idx = INDEX_RECORD_INDEX(cache->index, rec);
-		if (cache->trans_ctx->last_idx == idx) {
-			if (!mail_cache_write(cache->trans_ctx))
-				return NULL;
-		}
-
-		if (!mail_cache_transaction_commit(cache->trans_ctx))
-			return NULL;
-	}
-
-	cache_rec = cache_get_record(cache, rec->cache_offset);
-	if (cache_rec == NULL)
-		return NULL;
-
-	return cache_rec;
-}
-
-enum mail_cache_field
-mail_cache_get_fields(struct mail_cache *cache,
-		      const struct mail_index_record *rec)
-{
-	struct mail_cache_record *cache_rec;
-        enum mail_cache_field fields = 0;
-
-	cache_rec = mail_cache_lookup(cache, rec, 0);
-	while (cache_rec != NULL) {
-		fields |= cache_rec->fields;
-		cache_rec = cache_get_next_record(cache, cache_rec);
-	}
-
-	return fields;
-}
-
-static int cache_get_field(struct mail_cache *cache,
-			   struct mail_cache_record *cache_rec,
-			   enum mail_cache_field field,
-			   void **data_r, size_t *size_r)
-{
-	unsigned char *buf;
-	unsigned int mask;
-	uint32_t rec_size, data_size;
-	size_t offset, next_offset;
-	int i;
-
-	rec_size = nbo_to_uint32(cache_rec->size);
-	buf = (unsigned char *) cache_rec;
-	offset = sizeof(*cache_rec);
-
-	for (i = 0, mask = 1; i < 31; i++, mask <<= 1) {
-		if ((cache_rec->fields & mask) == 0)
-			continue;
-
-		/* all records are at least 32bit. we have to check this
-		   before getting data_size. */
-		if (offset + sizeof(uint32_t) > rec_size) {
-			mail_cache_set_corrupted(cache,
-				"Record continues outside it's allocated size");
-			return FALSE;
-		}
-
-		if ((mask & MAIL_CACHE_FIXED_MASK) != 0)
-			data_size = mail_cache_field_sizes[i];
-		else {
-			memcpy(&data_size, buf + offset, sizeof(data_size));
-			data_size = nbo_to_uint32(data_size);
-			offset += sizeof(data_size);
-		}
-
-		next_offset = offset + ((data_size + 3) & ~3);
-		if (next_offset > rec_size) {
-			mail_cache_set_corrupted(cache,
-				"Record continues outside it's allocated size");
-			return FALSE;
-		}
-
-		if (field == mask) {
-			if (data_size == 0) {
-				mail_cache_set_corrupted(cache,
-							 "Field size is 0");
-				return FALSE;
-			}
-			*data_r = buf + offset;
-			*size_r = data_size;
-			return TRUE;
-		}
-		offset = next_offset;
-	}
-
-	i_unreached();
-	return FALSE;
-}
-
-static int cache_lookup_field(struct mail_cache *cache,
-			      const struct mail_index_record *rec,
-			      enum mail_cache_field field,
-			      void **data_r, size_t *size_r)
-{
-	struct mail_cache_record *cache_rec;
-
-	cache_rec = mail_cache_lookup(cache, rec, field);
-	while (cache_rec != NULL) {
-		if ((cache_rec->fields & field) != 0) {
-			return cache_get_field(cache, cache_rec, field,
-					       data_r, size_r);
-		}
-		cache_rec = cache_get_next_record(cache, cache_rec);
-	}
-
-	return FALSE;
-}
-
-int mail_cache_lookup_field(struct mail_cache *cache,
-			    const struct mail_index_record *rec,
-			    enum mail_cache_field field,
-			    const void **data_r, size_t *size_r)
-{
-	void *data;
-
-	if (!cache_lookup_field(cache, rec, field, &data, size_r))
-		return FALSE;
-
-	*data_r = data;
-	return TRUE;
-}
-
-const char *mail_cache_lookup_string_field(struct mail_cache *cache,
-					   const struct mail_index_record *rec,
-					   enum mail_cache_field field)
-{
-	const void *data;
-	size_t size;
-
-	i_assert((field & MAIL_CACHE_STRING_MASK) != 0);
-
-	if (!mail_cache_lookup_field(cache, rec, field, &data, &size))
-		return NULL;
-
-	if (((const char *) data)[size-1] != '\0') {
-		mail_cache_set_corrupted(cache,
-			"String field %x doesn't end with NUL", field);
-		return NULL;
-	}
-	return data;
-}
-
-int mail_cache_copy_fixed_field(struct mail_cache *cache,
-				const struct mail_index_record *rec,
-				enum mail_cache_field field,
-				void *buffer, size_t buffer_size)
-{
-	const void *data;
-	size_t size;
-
-	i_assert((field & MAIL_CACHE_FIXED_MASK) != 0);
-
-	if (!mail_cache_lookup_field(cache, rec, field, &data, &size))
-		return FALSE;
-
-	if (buffer_size != size) {
-		i_panic("cache: fixed field %x wrong size "
-			"(%"PRIuSIZE_T" vs %"PRIuSIZE_T")",
-			field, size, buffer_size);
-	}
-
-	memcpy(buffer, data, buffer_size);
-	return TRUE;
-}
-
-void mail_cache_mark_missing(struct mail_cache *cache,
-			     enum mail_cache_field fields)
-{
-	// FIXME: count these
-}
-
-enum mail_index_record_flag
-mail_cache_get_index_flags(struct mail_cache *cache,
-			   const struct mail_index_record *rec)
-{
-	enum mail_index_record_flag flags;
-
-	if (!mail_cache_copy_fixed_field(cache, rec, MAIL_CACHE_INDEX_FLAGS,
-					 &flags, sizeof(flags)))
-		return 0;
-
-	return flags;
-}
-
-int mail_cache_update_index_flags(struct mail_cache *cache,
-				  const struct mail_index_record *rec,
-				  enum mail_index_record_flag flags)
-{
-	void *data;
-	size_t size;
-
-	i_assert(cache->locks > 0);
-
-	if (!cache_lookup_field(cache, rec, MAIL_CACHE_INDEX_FLAGS,
-				&data, &size)) {
-		mail_cache_set_corrupted(cache,
-			"Missing index flags for record %u", rec->uid);
-		return FALSE;
-	}
-
-	memcpy(data, &flags, sizeof(flags));
-	return TRUE;
-}
-
-int mail_cache_update_location_offset(struct mail_cache *cache,
-				      const struct mail_index_record *rec,
-				      uoff_t offset)
-{
-	void *data;
-	size_t size;
-
-	i_assert(cache->locks > 0);
-
-	if (!cache_lookup_field(cache, rec, MAIL_CACHE_LOCATION_OFFSET,
-				&data, &size)) {
-		mail_cache_set_corrupted(cache,
-			"Missing location offset for record %u", rec->uid);
-		return FALSE;
-	}
-
-	memcpy(data, &offset, sizeof(offset));
-	return TRUE;
-}
-
-void *mail_cache_get_mmaped(struct mail_cache *cache, size_t *size)
-{
-	if (!mmap_update(cache, 0, 0))
-		return NULL;
-
-	*size = cache->mmap_length;
-	return cache->mmap_base;
-}
--- a/src/lib-index/mail-cache-private.h	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-index/mail-cache-private.h	Sun Jun 20 11:05:41 2004 +0300
@@ -95,7 +95,8 @@
 mail_cache_split_header(struct mail_cache *cache, const char *header);
 
 struct mail_cache_record *
-mail_cache_get_record(struct mail_cache *cache, uint32_t offset);
+mail_cache_get_record(struct mail_cache *cache, uint32_t offset,
+		      int index_offset);
 struct mail_cache_record *
 mail_cache_get_next_record(struct mail_cache *cache,
 			   struct mail_cache_record *rec);
@@ -108,6 +109,9 @@
 mail_cache_transaction_autocommit(struct mail_cache_view *view,
 				  uint32_t seq, enum mail_cache_field fields);
 
+int mail_cache_mmap_update(struct mail_cache *cache,
+			   size_t offset, size_t size);
+
 void mail_cache_set_syscall_error(struct mail_cache *cache,
 				  const char *function);
 
--- a/src/lib-index/mail-cache-transaction.c	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-index/mail-cache-transaction.c	Sun Jun 20 11:05:41 2004 +0300
@@ -9,7 +9,6 @@
 
 #include <sys/stat.h>
 
-#if 0
 struct mail_cache_transaction_ctx {
 	struct mail_cache *cache;
 	struct mail_cache_view *view;
@@ -171,7 +170,7 @@
 
 	if (cache->used_file_size + size <= (uoff_t)st.st_size) {
 		/* no need to grow, just update mmap */
-		if (mmap_update(cache, 0, 0) < 0)
+		if (mail_cache_mmap_update(cache, 0, 0) < 0)
 			return -1;
 
 		i_assert(cache->mmap_length >= (uoff_t)st.st_size);
@@ -183,7 +182,7 @@
 		return -1;
 	}
 
-	return mmap_update(cache, 0, 0);
+	return mail_cache_mmap_update(cache, 0, 0);
 }
 
 static uint32_t mail_cache_append_space(struct mail_cache_transaction_ctx *ctx,
@@ -219,46 +218,54 @@
 	uint32_t write_offset, update_offset;
 	const void *buf;
 	size_t size, buf_size;
+	int ret;
 
 	buf = buffer_get_data(ctx->cache_data, &buf_size);
 
 	size = sizeof(*cache_rec) + buf_size;
 	ctx->cache_rec.size = uint32_to_nbo(size);
 
-	write_offset = mail_cache_append_space(ctx, size);
-	if (write_offset == 0)
-		return -1;
-
 	// FIXME: check cache_offset in transaction
-	if (mail_index_lookup_latest(ctx->view->view, ctx->prev_seq, &rec) < 0)
+	ret = mail_index_lookup(ctx->view->view, ctx->prev_seq, &rec);
+	if (ret < 0)
 		return -1;
 
-	cache_rec = mail_cache_get_record(cache, rec->cache_offset);
-	if (cache_rec == NULL) {
-		/* first cache record - update offset in index file */
-		mail_index_update_cache(ctx->trans, ctx->prev_seq,
-					write_offset);
+	if (ret == 0) {
+		/* it's been expunged already, do nothing */
 	} else {
-		/* find the last cache record */
-		while ((next = mail_cache_get_next_record(cache,
-							  cache_rec)) != NULL)
-			cache_rec = next;
+		write_offset = mail_cache_append_space(ctx, size);
+		if (write_offset == 0)
+			return -1;
 
-		/* mark next_offset to be updated later */
-		update_offset = (char *) &cache_rec->next_offset -
-			(char *) cache->mmap_base;
-		mark_update(&ctx->cache_marks, update_offset,
-			    mail_cache_uint32_to_offset(write_offset));
+		cache_rec = mail_cache_get_record(cache, rec->cache_offset,
+						  TRUE);
+		if (cache_rec == NULL) {
+			/* first cache record - update offset in index file */
+			mail_index_update_cache(ctx->trans, ctx->prev_seq,
+						write_offset);
+		} else {
+			/* find the last cache record */
+			while ((next = mail_cache_get_next_record(cache,
+								  cache_rec)) != NULL)
+				cache_rec = next;
+
+			/* mark next_offset to be updated later */
+			update_offset = (char *) &cache_rec->next_offset -
+				(char *) cache->mmap_base;
+			mark_update(&ctx->cache_marks, update_offset,
+				    mail_cache_uint32_to_offset(write_offset));
+		}
+
+		memcpy((char *) cache->mmap_base + write_offset,
+		       &ctx->cache_rec, sizeof(ctx->cache_rec));
+		memcpy((char *) cache->mmap_base + write_offset +
+		       sizeof(ctx->cache_rec), buf, buf_size);
 	}
+
+	/* reset the write context */
 	ctx->prev_seq = 0;
 	ctx->prev_fields = 0;
 
-	memcpy((char *) cache->mmap_base + write_offset,
-	       &ctx->cache_rec, sizeof(ctx->cache_rec));
-	memcpy((char *) cache->mmap_base + write_offset +
-	       sizeof(ctx->cache_rec), buf, buf_size);
-
-	/* reset the write context */
 	memset(&ctx->cache_rec, 0, sizeof(ctx->cache_rec));
 	buffer_set_used_size(ctx->cache_data, 0);
 	return 0;
@@ -558,5 +565,9 @@
 
 	return 0;
 }
-#else
-#endif
+
+int mail_cache_update_record_flags(struct mail_cache_view *view, uint32_t seq,
+				   enum mail_cache_record_flag flags)
+{
+	return -1;
+}
--- a/src/lib-index/mail-cache.c	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-index/mail-cache.c	Sun Jun 20 11:05:41 2004 +0300
@@ -4,6 +4,7 @@
 #include "buffer.h"
 #include "byteorder.h"
 #include "file-lock.h"
+#include "file-set-size.h"
 #include "mmap-util.h"
 #include "write-full.h"
 #include "mail-cache-private.h"
@@ -38,7 +39,6 @@
 	MAIL_CACHE_HEADERS4
 };
 
-#if 0
 uint32_t mail_cache_uint32_to_offset(uint32_t offset)
 {
 	unsigned char buf[4];
@@ -187,13 +187,15 @@
 {
 	struct stat st;
 
+#if 0 // FIXME
 	/* if sequence has changed, the file has to be reopened.
 	   note that if main index isn't locked, it may change again */
 	if (cache->hdr->file_seq != cache->index->hdr->cache_file_seq &&
 	    cache->mmap_base != NULL) {
-		if (!mail_cache_file_reopen(cache))
+		if (mail_cache_file_reopen(cache) < 0)
 			return -1;
 	}
+#endif
 
 	if (offset < cache->mmap_length &&
 	    size <= cache->mmap_length - offset &&
@@ -244,35 +246,38 @@
 	return 0;
 }
 
-static int mmap_update(struct mail_cache *cache, size_t offset, size_t size)
+int mail_cache_mmap_update(struct mail_cache *cache, size_t offset, size_t size)
 {
 	int synced, ret;
 
 	for (synced = FALSE;; synced = TRUE) {
 		ret = mmap_update_nocheck(cache, offset, size);
 		if (ret > 0)
-			return TRUE;
+			return 0;
 		if (ret < 0)
-			return FALSE;
+			return -1;
 
-		if (!mmap_verify_header(cache))
-			return FALSE;
+		if (mmap_verify_header(cache) <= 0)
+			return -1;
 
+#if 0 // FIXME
 		/* see if cache file was rebuilt - do it only once to avoid
 		   infinite looping */
-		if (cache->hdr->sync_id == cache->index->cache_sync_id ||
+		if (cache->hdr->file_seq == cache->index->hdr->cache_file_seq ||
 		    synced)
 			break;
 
-		if (!mail_cache_file_reopen(cache))
-			return FALSE;
+		if (mail_cache_file_reopen(cache) < 0)
+			return -1;
+#endif
 	}
-	return TRUE;
+	return 0;
 }
 
 static int mail_cache_open_and_verify(struct mail_cache *cache, int silent)
 {
 	struct stat st;
+	int ret;
 
 	mail_cache_file_close(cache);
 
@@ -299,9 +304,9 @@
 
 	/* verify that this really is the cache for wanted index */
 	cache->silent = silent;
-	if (!mmap_verify_header(cache)) {
+	if ((ret = mmap_verify_header(cache)) <= 0) {
 		cache->silent = FALSE;
-		return 0;
+		return ret;
 	}
 
 	cache->silent = FALSE;
@@ -318,66 +323,64 @@
 
 	ret = mail_cache_open_and_verify(cache, FALSE);
 	if (ret != 0)
-		return ret > 0;
-
-	/* we'll have to clear cache_offsets which requires exclusive lock */
-	if (!mail_index_set_lock(cache->index, MAIL_LOCK_EXCLUSIVE))
-		return FALSE;
+		return ret < 0 ? -1 : 0;
 
 	/* maybe a rebuild.. */
-	fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
+	fd = file_dotlock_open(cache->filepath, NULL, NULL,
+			       MAIL_CACHE_LOCK_TIMEOUT,
 			       MAIL_CACHE_LOCK_CHANGE_TIMEOUT,
 			       MAIL_CACHE_LOCK_IMMEDIATE_TIMEOUT, NULL, NULL);
 	if (fd == -1) {
 		mail_cache_set_syscall_error(cache, "file_dotlock_open()");
-		return FALSE;
+		return -1;
 	}
 
 	/* see if someone else just created the cache file */
 	ret = mail_cache_open_and_verify(cache, TRUE);
 	if (ret != 0) {
-		(void)file_dotlock_delete(cache->filepath, fd);
-		return ret > 0;
+		(void)file_dotlock_delete(cache->filepath, NULL, fd);
+		return ret < 0 ? -1 : 0;
 	}
 
 	/* rebuild then */
 	if (write_full(fd, hdr, sizeof(*hdr)) < 0) {
 		mail_cache_set_syscall_error(cache, "write_full()");
-		(void)file_dotlock_delete(cache->filepath, fd);
-		return FALSE;
+		(void)file_dotlock_delete(cache->filepath, NULL, fd);
+		return -1;
 	}
 	if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
 		mail_cache_set_syscall_error(cache, "file_set_size()");
-		(void)file_dotlock_delete(cache->filepath, fd);
-		return FALSE;
+		(void)file_dotlock_delete(cache->filepath, NULL, fd);
+		return -1;
 	}
 
-	if (cache->index->hdr.cache_file_seq != 0) {
-		// FIXME: recreate index file with cache_offsets cleared
+	if (cache->index->hdr->cache_file_seq != 0) {
+		// FIXME: clear cache_offsets in index file
 	}
 
 	mail_cache_file_close(cache);
 	cache->fd = dup(fd);
 
-	if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
+	if (file_dotlock_replace(cache->filepath, NULL, fd, FALSE) < 0) {
 		mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
-		return FALSE;
+		return -1;
 	}
 
-	if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
-		return FALSE;
+	if (mail_cache_mmap_update(cache, 0,
+				   sizeof(struct mail_cache_header)) < 0)
+		return -1;
 
-	return TRUE;
+	return 0;
 }
 
-int mail_cache_open_or_create(struct mail_index *index)
+struct mail_cache *mail_cache_open_or_create(struct mail_index *index)
 {
         struct mail_cache_header hdr;
 	struct mail_cache *cache;
 
 	memset(&hdr, 0, sizeof(hdr));
 	hdr.indexid = index->indexid;
-	hdr.sync_id = index->hdr->cache_file_seq; // FIXME
+	hdr.file_seq = index->hdr->cache_file_seq + 1;
 	hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
 
 	cache = i_new(struct mail_cache, 1);
@@ -385,25 +388,21 @@
 	cache->fd = -1;
         cache->split_header_pool = pool_alloconly_create("Headers", 512);
 
-	index->cache = cache;
-
 	/* we'll do anon-mmaping only if initially requested. if we fail
 	   because of out of disk space, we'll just let the main index code
 	   know it and fail. */
-	if (!mail_cache_open_or_create_file(cache, &hdr)) {
+	if (mail_cache_open_or_create_file(cache, &hdr) < 0) {
 		mail_cache_free(cache);
-		return FALSE;
+		return NULL;
 	}
 
-	return TRUE;
+	return cache;
 }
 
 void mail_cache_free(struct mail_cache *cache)
 {
 	i_assert(cache->trans_ctx == NULL);
 
-	cache->index->cache = NULL;
-
 	mail_cache_file_close(cache);
 
 	pool_unref(cache->split_header_pool);
@@ -422,18 +421,20 @@
 int mail_cache_reset(struct mail_cache *cache)
 {
 	struct mail_cache_header hdr;
-	int ret, fd;
+	int fd;
 
-	i_assert(cache->index->lock_type == MAIL_LOCK_EXCLUSIVE);
+	i_assert(cache->index->lock_type == F_WRLCK);
 
 	memset(&hdr, 0, sizeof(hdr));
 	hdr.indexid = cache->index->indexid;
-	hdr.sync_id = cache->sync_id = cache->index->cache_sync_id =
-		++cache->index->hdr->cache_sync_id;
+	hdr.file_seq = cache->index->hdr->cache_file_seq + 1;
 	hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
 	cache->used_file_size = sizeof(hdr);
 
-	fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
+	// FIXME: update cache_offsets in index
+
+	fd = file_dotlock_open(cache->filepath, NULL, NULL,
+			       MAIL_CACHE_LOCK_TIMEOUT,
 			       MAIL_CACHE_LOCK_CHANGE_TIMEOUT,
 			       MAIL_CACHE_LOCK_IMMEDIATE_TIMEOUT, NULL, NULL);
 	if (fd == -1) {
@@ -443,25 +444,26 @@
 
 	if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
 		mail_cache_set_syscall_error(cache, "write_full()");
-		(void)file_dotlock_delete(cache->filepath, fd);
+		(void)file_dotlock_delete(cache->filepath, NULL, fd);
 		return -1;
 	}
 	if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
 		mail_cache_set_syscall_error(cache, "file_set_size()");
-		(void)file_dotlock_delete(cache->filepath, fd);
+		(void)file_dotlock_delete(cache->filepath, NULL, fd);
 		return -1;
 	}
 
 	mail_cache_file_close(cache);
 	cache->fd = dup(fd);
 
-	if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
+	if (file_dotlock_replace(cache->filepath, NULL, fd, FALSE) < 0) {
 		mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
 		return -1;
 	}
 
 	cache->mmap_refresh = TRUE;
-	if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
+	if (mail_cache_mmap_update(cache, 0,
+				   sizeof(struct mail_cache_header)) < 0)
 		return -1;
 
 	return 0;
@@ -472,7 +474,7 @@
 	int ret;
 
 	if (cache->locks++ != 0)
-		return TRUE;
+		return 1;
 
 	if (nonblock) {
 		ret = file_try_lock(cache->fd, F_WRLCK);
@@ -485,20 +487,22 @@
 	}
 
 	if (ret > 0) {
-		if (!mmap_update(cache, 0, 0)) {
+		if (mail_cache_mmap_update(cache, 0, 0) < 0) {
 			(void)mail_cache_unlock(cache);
 			return -1;
 		}
-		if (cache->sync_id != cache->index->cache_sync_id) {
+#if 0 // FIXME
+		if (cache->hdr->file_seq != cache->index->hdr->cache_file_seq) {
 			/* 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->hdr->sync_id =
-				cache->index->cache_sync_id;
+			cache->hdr->file_seq =
+				cache->index->hdr->cache_file_seq;
 		}
+#endif
 	}
 	return ret;
 }
@@ -506,14 +510,14 @@
 int mail_cache_unlock(struct mail_cache *cache)
 {
 	if (--cache->locks > 0)
-		return TRUE;
+		return 0;
 
 	if (file_wait_lock(cache->fd, F_UNLCK) <= 0) {
 		mail_cache_set_syscall_error(cache, "file_wait_lock(F_UNLCK)");
-		return FALSE;
+		return -1;
 	}
 
-	return TRUE;
+	return 0;
 }
 
 int mail_cache_is_locked(struct mail_cache *cache)
@@ -536,128 +540,9 @@
 {
 	i_free(view);
 }
-#else
 
-int mail_cache_open_or_create(struct mail_index *index)
-{
-	return 0;
-}
-
-void mail_cache_free(struct mail_cache *cache)
+void mail_cache_mark_missing(struct mail_cache_view *view,
+			     enum mail_cache_field fields)
 {
-}
-
-void mail_cache_set_defaults(struct mail_cache *cache,
-			     enum mail_cache_field default_cache_fields,
-			     enum mail_cache_field never_cache_fields) {}
-
-/* Compress cache file. */
-int mail_cache_compress(struct mail_cache *cache) {return 0;}
-
-/* Reset the cache file, clearing all data. */
-int mail_cache_reset(struct mail_cache *cache) {return 0;}
-
-/* Explicitly lock the cache file. Returns 1 if ok, 0 if nonblock is TRUE and
-   we couldn't immediately get a lock, or -1 if error. */
-int mail_cache_lock(struct mail_cache *cache, int nonblock) {return 0;}
-int mail_cache_unlock(struct mail_cache *cache) {return 0;}
-
-/* Returns TRUE if cache file is locked. */
-int mail_cache_is_locked(struct mail_cache *cache) {return TRUE;}
-
-struct mail_cache_view *
-mail_cache_view_open(struct mail_cache *cache, struct mail_index_view *iview)
-{return i_new(struct mail_cache_view, 1);}
-void mail_cache_view_close(struct mail_cache_view *view) {i_free(view);}
-
-/* Begin transaction. Cache transaction may be committed or rollbacked multiple
-   times. It will finish when index transaction is committed or rollbacked.
-   The transaction might also be partially committed automatically, so this
-   is kind of fake transaction, it's only purpose being optimizing writes.
-   Returns same as mail_cache_lock(). */
-int mail_cache_transaction_begin(struct mail_cache_view *view, int nonblock,
-				 struct mail_index_transaction *t,
-				 struct mail_cache_transaction_ctx **ctx_r)
-{
-	*ctx_r = NULL;
-	return 1;
+	// FIXME
 }
-int mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx)
-{return 0;}
-void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx) {}
-
-/* Should be called only by mail_transaction_commit/rollback: */
-int mail_cache_transaction_end(struct mail_cache_transaction_ctx *ctx)
-{return 0;}
-
-/* Return NULL-terminated list of headers for given index, or NULL if
-   header index isn't used. */
-const char *const *mail_cache_get_header_fields(struct mail_cache_view *view,
-						unsigned int idx)
-{return NULL;}
-/* Set list of headers for given index. */
-int mail_cache_set_header_fields(struct mail_cache_transaction_ctx *ctx,
-				 unsigned int idx, const char *const headers[])
-{return 0;}
-
-/* Add new field to given record. Updates are not allowed. Fixed size fields
-   must be exactly the expected size and they're converted to network byte
-   order in disk. */
-int mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
-		   enum mail_cache_field field,
-		   const void *data, size_t data_size)
-{return 0;}
-
-/* Mark the given record deleted. */
-int mail_cache_delete(struct mail_cache_transaction_ctx *ctx, uint32_t seq)
-{return 0;}
-
-/* Return all fields that are currently cached for record. */
-enum mail_cache_field
-mail_cache_get_fields(struct mail_cache_view *view, uint32_t seq) {return 0;}
-
-/* Set data_r and size_r to point to wanted field in cache file.
-   Returns TRUE if field was found. If field contains multiple fields,
-   first one found is returned. This is mostly useful for finding headers. */
-int mail_cache_lookup_field(struct mail_cache_view *view, uint32_t seq,
-			    enum mail_cache_field field,
-			    const void **data_r, size_t *size_r) {return 0;}
-
-/* Return string field. */
-const char *
-mail_cache_lookup_string_field(struct mail_cache_view *view, uint32_t seq,
-			       enum mail_cache_field field) {return 0;}
-
-/* Copy fixed size field to given buffer. buffer_size must be exactly the
-   expected size. The result will be converted to host byte order.
-   Returns TRUE if field was found. */
-int mail_cache_copy_fixed_field(struct mail_cache_view *view, uint32_t seq,
-				enum mail_cache_field field,
-				void *buffer, size_t buffer_size) {return 0;}
-
-/* Mark given fields as missing, ie. they should be cached when possible. */
-void mail_cache_mark_missing(struct mail_cache_view *view,
-			     enum mail_cache_field fields) {}
-
-/* Return index flags. */
-enum mail_cache_record_flag
-mail_cache_get_record_flags(struct mail_cache_view *view, uint32_t seq)
-{return 0;}
-
-/* Update index flags. The cache file must be locked and the flags must be
-   already inserted to the record. */
-int mail_cache_update_record_flags(struct mail_cache_view *view, uint32_t seq,
-				   enum mail_cache_record_flag flags)
-{return 0;}
-
-/* Update location offset. External locking is assumed to take care of locking
-   readers out to prevent race conditions. */
-int mail_cache_update_location_offset(struct mail_cache_view *view,
-				      uint32_t seq, uoff_t offset)
-{return 0;}
-
-/* "Error in index cache file %s: ...". */
-void mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
-{}
-
-#endif
--- a/src/lib-index/mail-cache.h	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-index/mail-cache.h	Sun Jun 20 11:05:41 2004 +0300
@@ -28,12 +28,9 @@
 enum mail_cache_field {
 	/* fixed size fields */
 	MAIL_CACHE_INDEX_FLAGS		= 0x00000001,
-	MAIL_CACHE_LOCATION_OFFSET	= 0x00000002,
-	MAIL_CACHE_MD5			= 0x00000004,
 	MAIL_CACHE_SENT_DATE		= 0x00000008,
 	MAIL_CACHE_RECEIVED_DATE	= 0x00000010,
 	MAIL_CACHE_VIRTUAL_FULL_SIZE	= 0x00000020,
-	MAIL_CACHE_PHYSICAL_BODY_SIZE	= 0x00000040,
 
 	/* variable sized field */
 	MAIL_CACHE_HEADERS1		= 0x40000000,
@@ -47,12 +44,9 @@
 	MAIL_CACHE_MESSAGEPART		= 0x00400000,
 
 	MAIL_CACHE_FIXED_MASK		= MAIL_CACHE_INDEX_FLAGS |
-					  MAIL_CACHE_LOCATION_OFFSET |
-					  MAIL_CACHE_MD5 |
 					  MAIL_CACHE_SENT_DATE |
 					  MAIL_CACHE_RECEIVED_DATE |
-					  MAIL_CACHE_VIRTUAL_FULL_SIZE |
-					  MAIL_CACHE_PHYSICAL_BODY_SIZE,
+					  MAIL_CACHE_VIRTUAL_FULL_SIZE,
 	MAIL_CACHE_HEADERS_MASK		= MAIL_CACHE_HEADERS1 |
 					  MAIL_CACHE_HEADERS2 |
 					  MAIL_CACHE_HEADERS3 |
@@ -74,7 +68,7 @@
 
 extern enum mail_cache_field mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT];
 
-int mail_cache_open_or_create(struct mail_index *index);
+struct mail_cache *mail_cache_open_or_create(struct mail_index *index);
 void mail_cache_free(struct mail_cache *cache);
 
 void mail_cache_set_defaults(struct mail_cache *cache,
@@ -167,11 +161,6 @@
 int mail_cache_update_record_flags(struct mail_cache_view *view, uint32_t seq,
 				   enum mail_cache_record_flag flags);
 
-/* Update location offset. External locking is assumed to take care of locking
-   readers out to prevent race conditions. */
-int mail_cache_update_location_offset(struct mail_cache_view *view,
-				      uint32_t seq, uoff_t offset);
-
 /* "Error in index cache file %s: ...". */
 void mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
 	__attr_format__(2, 3);
--- a/src/lib-index/mail-index.c	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-index/mail-index.c	Sun Jun 20 11:05:41 2004 +0300
@@ -8,6 +8,7 @@
 #include "write-full.h"
 #include "mail-index-private.h"
 #include "mail-transaction-log.h"
+#include "mail-cache.h"
 
 #include <stdio.h>
 #include <stddef.h>
@@ -553,7 +554,7 @@
 	ret = mail_index_try_open(index, NULL);
 	if (ret != 0) {
 		mail_transaction_log_sync_unlock(index->log);
-		return ret;
+		return ret < 0 ? -1 : 0;
 	}
 
 	/* create it fully in index.tmp first */
@@ -618,6 +619,7 @@
 	hdr->next_uid = 1;
 }
 
+/* returns -1 = error, 0 = won't create, 1 = ok */
 static int mail_index_open_files(struct mail_index *index,
 				 enum mail_index_open_flags flags)
 {
@@ -643,9 +645,27 @@
 	if (index->log == NULL)
 		return -1;
 
-	if (lock_id != 0)
-		mail_index_unlock(index, lock_id);
-	return index->fd != -1 ? 1 : mail_index_create(index, &hdr);
+	if (index->fd == -1) {
+		if (lock_id != 0) {
+			mail_index_unlock(index, lock_id);
+			lock_id = 0;
+		}
+		if (mail_index_create(index, &hdr) < 0)
+			return -1;
+	}
+
+	if (lock_id == 0) {
+		if (mail_index_lock_shared(index, FALSE, &lock_id) < 0)
+			return -1;
+
+	}
+
+	index->cache = mail_cache_open_or_create(index);
+	if (index->cache == NULL)
+		return -1;
+
+	mail_index_unlock(index, lock_id);
+	return 1;
 }
 
 int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags)
@@ -711,6 +731,11 @@
 		index->map = NULL;
 	}
 
+	if (index->cache != NULL) {
+		mail_cache_free(index->cache);
+		index->cache = NULL;
+	}
+
 	if (index->fd != -1) {
 		if (close(index->fd) < 0)
 			mail_index_set_syscall_error(index, "close()");
--- a/src/lib-storage/index/index-mail.c	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-storage/index/index-mail.c	Sun Jun 20 11:05:41 2004 +0300
@@ -157,8 +157,8 @@
         if (!index_mail_cache_can_add(mail, field))
 		return;
 
-	if (!mail_cache_add(mail->trans->cache_trans, mail->data.seq,
-			    field, data, size))
+	if (mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+			   field, data, size) < 0)
 		mail_cache_transaction_rollback(mail->trans->cache_trans);
 
 	mail->data.cached_fields |= field;
--- a/src/lib-storage/index/index-storage.c	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-storage/index/index-storage.c	Sun Jun 20 11:05:41 2004 +0300
@@ -350,6 +350,8 @@
 		}
 
 		ibox->view = mail_index_view_open(index);
+		ibox->cache_view =
+			mail_cache_view_open(ibox->cache, ibox->view);
 		return ibox;
 	} while (0);
 
@@ -362,6 +364,8 @@
 {
 	struct index_mailbox *ibox = (struct index_mailbox *) box;
 
+	if (ibox->cache_view != NULL)
+		mail_cache_view_close(ibox->cache_view);
 	if (ibox->view != NULL)
 		mail_index_view_close(ibox->view);
 
--- a/src/lib-storage/index/index-transaction.c	Sun Jun 20 09:33:08 2004 +0300
+++ b/src/lib-storage/index/index-transaction.c	Sun Jun 20 11:05:41 2004 +0300
@@ -20,8 +20,11 @@
 	uoff_t offset;
 	int ret;
 
-	if (t->cache_trans != NULL) 
+	if (t->cache_trans != NULL)  {
 		(void)mail_cache_transaction_commit(t->cache_trans);
+		(void)mail_cache_transaction_end(t->cache_trans);
+		t->cache_trans = NULL;
+	}
 
 	ret = mail_index_transaction_commit(t->trans, &seq, &offset);
 	if (ret < 0)