changeset 6165:6418139447fa HEAD

Created a new struct mail_index_record_map which can be shared by multiple mail_index_maps. This avoids having to copy records when appending new messages.
author Timo Sirainen <tss@iki.fi>
date Fri, 03 Aug 2007 03:22:04 +0300
parents 22398d619cac
children 004534216202
files src/lib-index/mail-cache.c src/lib-index/mail-index-fsck.c src/lib-index/mail-index-map.c src/lib-index/mail-index-private.h src/lib-index/mail-index-sync-ext.c src/lib-index/mail-index-sync-update.c src/lib-index/mail-index-transaction.c src/lib-index/mail-index-view-sync.c src/lib-index/mail-index-view.c src/lib-index/mail-index-write.c src/lib-index/mail-index.c
diffstat 11 files changed, 278 insertions(+), 166 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-cache.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-cache.c	Fri Aug 03 03:22:04 2007 +0300
@@ -527,8 +527,8 @@
 	uoff_t max_del_space;
 
         cont_percentage = hdr->continued_record_count * 100 /
-		(cache->index->map->records_count == 0 ? 1 :
-		 cache->index->map->records_count);
+		(cache->index->map->rec_map->records_count == 0 ? 1 :
+		 cache->index->map->rec_map->records_count);
 	if (cont_percentage >= MAIL_CACHE_COMPRESS_CONTINUED_PERCENTAGE &&
 	    hdr->used_file_size >= MAIL_CACHE_COMPRESS_MIN_SIZE) {
 		/* too many continued rows, compress */
--- a/src/lib-index/mail-index-fsck.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-fsck.c	Fri Aug 03 03:22:04 2007 +0300
@@ -78,8 +78,8 @@
 	hdr.first_unseen_uid_lowwater = 0;
 	hdr.first_deleted_uid_lowwater = 0;
 
-	rec = map->records; last_uid = 0;
-	for (i = 0; i < map->records_count; i++) {
+	rec = map->rec_map->records; last_uid = 0;
+	for (i = 0; i < map->rec_map->records_count; i++) {
 		if (rec->uid <= last_uid) {
 			*error_r = "Record UIDs are not ordered";
 			return 0;
--- a/src/lib-index/mail-index-map.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-map.c	Fri Aug 03 03:22:04 2007 +0300
@@ -310,12 +310,12 @@
 	    hdr->first_deleted_uid_lowwater > hdr->next_uid)
 		return 0;
 
-	if (map->records_count > 0) {
+	if (hdr->messages_count > 0) {
 		/* last message's UID must be smaller than next_uid.
 		   also make sure it's not zero. */
 		const struct mail_index_record *rec;
 
-		rec = MAIL_INDEX_MAP_IDX(map, map->records_count-1);
+		rec = MAIL_INDEX_MAP_IDX(map, hdr->messages_count-1);
 		if (rec->uid == 0 || rec->uid >= hdr->next_uid)
 			return 0;
 	}
@@ -323,28 +323,6 @@
 	return 1;
 }
 
-static void mail_index_map_clear(struct mail_index_map *map)
-{
-	if (map->buffer != NULL) {
-		i_assert(map->mmap_base == NULL);
-		buffer_free(map->buffer);
-		map->buffer = NULL;
-	} else if (map->mmap_base != NULL) {
-		i_assert(map->buffer == NULL);
-		if (munmap(map->mmap_base, map->mmap_size) < 0)
-			mail_index_set_syscall_error(map->index, "munmap()");
-		map->mmap_base = NULL;
-	}
-
-	if (map->refcount > 0) {
-		memset(&map->hdr, 0, sizeof(map->hdr));
-		map->mmap_size = 0;
-		map->mmap_used_size = 0;
-		map->records = NULL;
-		map->records_count = 0;
-	}
-}
-
 static void mail_index_map_copy_hdr(struct mail_index_map *map,
 				    const struct mail_index_header *hdr)
 {
@@ -361,14 +339,14 @@
 static int mail_index_mmap(struct mail_index_map *map, uoff_t file_size)
 {
 	struct mail_index *index = map->index;
+	struct mail_index_record_map *rec_map = map->rec_map;
 	const struct mail_index_header *hdr;
 	unsigned int records_count;
 
-	if (map->buffer != NULL) {
-		/* we had temporarily used a buffer, eg. for updating index */
-		buffer_free(map->buffer);
-		map->buffer = NULL;
-	}
+	i_assert(rec_map->mmap_base == NULL);
+
+	buffer_free(rec_map->buffer);
+	rec_map->buffer = NULL;
 
 	if (file_size > SSIZE_T_MAX) {
 		/* too large file to map into memory */
@@ -377,40 +355,40 @@
 		return -1;
 	}
 
-	map->mmap_base = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
-			      MAP_PRIVATE, index->fd, 0);
-	if (map->mmap_base == MAP_FAILED) {
-		map->mmap_base = NULL;
+	rec_map->mmap_base = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
+				  MAP_PRIVATE, index->fd, 0);
+	if (rec_map->mmap_base == MAP_FAILED) {
+		rec_map->mmap_base = NULL;
 		mail_index_set_syscall_error(index, "mmap()");
 		return -1;
 	}
-	map->mmap_size = file_size;
+	rec_map->mmap_size = file_size;
 
-	hdr = map->mmap_base;
-	if (map->mmap_size >
+	hdr = rec_map->mmap_base;
+	if (rec_map->mmap_size >
 	    offsetof(struct mail_index_header, major_version) &&
 	    hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
 		/* major version change - handle silently */
 		return 0;
 	}
 
-	if (map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
+	if (rec_map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
 		mail_index_set_error(index, "Corrupted index file %s: "
 				     "File too small (%"PRIuSIZE_T")",
-				     index->filepath, map->mmap_size);
+				     index->filepath, rec_map->mmap_size);
 		return 0;
 	}
 
-	if (!mail_index_check_header_compat(index, hdr, map->mmap_size)) {
+	if (!mail_index_check_header_compat(index, hdr, rec_map->mmap_size)) {
 		/* Can't use this file */
 		return 0;
 	}
 
-	map->mmap_used_size = hdr->header_size +
+	rec_map->mmap_used_size = hdr->header_size +
 		hdr->messages_count * hdr->record_size;
 
-	if (map->mmap_used_size > map->mmap_size) {
-		records_count = (map->mmap_size - hdr->header_size) /
+	if (rec_map->mmap_used_size > rec_map->mmap_size) {
+		records_count = (rec_map->mmap_size - hdr->header_size) /
 			hdr->record_size;
 		mail_index_set_error(index, "Corrupted index file %s: "
 				     "messages_count too large (%u > %u)",
@@ -421,9 +399,9 @@
 
 	mail_index_map_copy_hdr(map, hdr);
 
-	map->hdr_base = map->mmap_base;
-	map->records = PTR_OFFSET(map->mmap_base, map->hdr.header_size);
-	map->records_count = map->hdr.messages_count;
+	map->hdr_base = rec_map->mmap_base;
+	rec_map->records = PTR_OFFSET(rec_map->mmap_base, map->hdr.header_size);
+	rec_map->records_count = map->hdr.messages_count;
 	return 1;
 }
 
@@ -464,7 +442,7 @@
 	size_t pos, records_size, initial_buf_pos = 0;
 	unsigned int records_count, extra;
 
-	i_assert(map->mmap_base == NULL);
+	i_assert(map->rec_map->mmap_base == NULL);
 
 	*retry_r = FALSE;
 	ret = mail_index_read_header(index, read_buf, sizeof(read_buf), &pos);
@@ -517,23 +495,24 @@
 			return 0;
 		}
 
-		if (map->buffer == NULL) {
-			map->buffer = buffer_create_dynamic(default_pool,
-							    records_size);
+		if (map->rec_map->buffer == NULL) {
+			map->rec_map->buffer =
+				buffer_create_dynamic(default_pool,
+						      records_size);
 		}
 
 		/* @UNSAFE */
-		buffer_set_used_size(map->buffer, 0);
+		buffer_set_used_size(map->rec_map->buffer, 0);
 		if (initial_buf_pos <= hdr->header_size)
 			extra = 0;
 		else {
 			extra = initial_buf_pos - hdr->header_size;
-			buffer_append(map->buffer,
+			buffer_append(map->rec_map->buffer,
 				      CONST_PTR_OFFSET(buf, hdr->header_size),
 				      extra);
 		}
 		if (records_size > extra) {
-			data = buffer_append_space_unsafe(map->buffer,
+			data = buffer_append_space_unsafe(map->rec_map->buffer,
 							  records_size - extra);
 			ret = pread_full(index->fd, data, records_size - extra,
 					 hdr->header_size + extra);
@@ -556,8 +535,9 @@
 		return 0;
 	}
 
-	map->records = buffer_get_modifiable_data(map->buffer, NULL);
-	map->records_count = hdr->messages_count;
+	map->rec_map->records =
+		buffer_get_modifiable_data(map->rec_map->buffer, NULL);
+	map->rec_map->records_count = hdr->messages_count;
 
 	mail_index_map_copy_hdr(map, hdr);
 	map->hdr_base = map->hdr_copy_buf->data;
@@ -700,7 +680,9 @@
 
 	new_map = mail_index_map_alloc(index);
 	if (use_mmap) {
-		new_map->lock_id = lock_id;
+		new_map->rec_map->lock_id = lock_id;
+		new_map->rec_map->lock_count++;
+		new_map->locked = TRUE;
 		ret = mail_index_mmap(new_map, file_size);
 	} else {
 		ret = mail_index_read_map(new_map, file_size);
@@ -718,6 +700,7 @@
 		mail_index_unmap(&new_map);
 		return ret;
 	}
+	i_assert(new_map->rec_map->records != NULL);
 
 	index->last_read_log_file_seq = new_map->hdr.log_file_seq;
 	index->last_read_log_file_head_offset =
@@ -773,6 +756,40 @@
 	return ret;
 }
 
+static void mail_index_record_map_free(struct mail_index_map *map,
+				       struct mail_index_record_map *rec_map)
+{
+	if (rec_map->buffer != NULL) {
+		i_assert(rec_map->mmap_base == NULL);
+		buffer_free(rec_map->buffer);
+		rec_map->buffer = NULL;
+	} else if (rec_map->mmap_base != NULL) {
+		i_assert(rec_map->buffer == NULL);
+		if (munmap(rec_map->mmap_base, rec_map->mmap_size) < 0)
+			mail_index_set_syscall_error(map->index, "munmap()");
+		rec_map->mmap_base = NULL;
+	}
+	array_free(&rec_map->maps);
+	i_free(rec_map);
+}
+
+static void mail_index_record_map_unlink(struct mail_index_map *map)
+{
+	struct mail_index_map *const *maps;
+	unsigned int i, count;
+
+	maps = array_get(&map->rec_map->maps, &count);
+	for (i = 0; i < count; i++) {
+		if (maps[i] == map) {
+			array_delete(&map->rec_map->maps, i, 1);
+			if (i == 0 && count == 1)
+				mail_index_record_map_free(map, map->rec_map);
+			return;
+		}
+	}
+	i_unreached();
+}
+
 void mail_index_unmap(struct mail_index_map **_map)
 {
 	struct mail_index_map *map = *_map;
@@ -782,8 +799,8 @@
 		return;
 
 	i_assert(map->refcount == 0);
-	mail_index_map_clear(map);
 	mail_index_map_unlock(map);
+	mail_index_record_map_unlink(map);
 
 	if (map->extension_pool != NULL)
 		pool_unref(map->extension_pool);
@@ -795,35 +812,61 @@
 
 int mail_index_map_lock(struct mail_index_map *map)
 {
-	if (map->lock_id != 0 || MAIL_INDEX_MAP_IS_IN_MEMORY(map))
+	if (map->locked)
 		return 0;
 
-	if (mail_index_lock_shared(map->index, &map->lock_id) < 0)
+	if (map->rec_map->lock_id != 0 || MAIL_INDEX_MAP_IS_IN_MEMORY(map)) {
+		map->rec_map->lock_count++;
+		map->locked = TRUE;
+		return 0;
+	}
+
+	if (mail_index_lock_shared(map->index, &map->rec_map->lock_id) < 0)
 		return -1;
 
-	mail_index_map_copy_hdr(map, map->mmap_base);
+	mail_index_map_copy_hdr(map, map->rec_map->mmap_base);
+	map->rec_map->lock_count++;
+	map->locked = TRUE;
 	return 0;
 }
 
 void mail_index_map_unlock(struct mail_index_map *map)
 {
-	mail_index_unlock(map->index, &map->lock_id);
+	if (!map->locked)
+		return;
+
+	if (--map->rec_map->lock_count == 0)
+		mail_index_unlock(map->index, &map->rec_map->lock_id);
 }
 
-static void mail_index_map_copy(struct mail_index_map *dest,
-				const struct mail_index_map *src)
+static void mail_index_map_copy_records(struct mail_index_record_map *dest,
+					const struct mail_index_record_map *src,
+					unsigned int record_size)
 {
 	size_t size;
 
-	/* copy records */
-	size = src->records_count * src->hdr.record_size;
-	dest->buffer = buffer_create_dynamic(default_pool, size);
+	if (src == NULL) {
+		dest->buffer = buffer_create_dynamic(default_pool, 1024);
+		return;
+	}
+
+	size = src->records_count * record_size;
+	dest->buffer = buffer_create_dynamic(default_pool, I_MIN(size, 1024));
 	buffer_append(dest->buffer, src->records, size);
 
 	dest->records = buffer_get_modifiable_data(dest->buffer, NULL);
 	dest->records_count = src->records_count;
 
-	/* copy header. use src->hdr copy directly, because if we got here
+	/* if the map is ever written back to disk, we need to keep track of
+	   what has changed. */
+	dest->write_seq_first = src->write_seq_first;
+	dest->write_seq_last = src->write_seq_last;
+}
+
+static void mail_index_map_copy_header(struct mail_index_map *dest,
+				       const struct mail_index_map *src)
+{
+	/* use src->hdr copy directly, because if we got here
 	   from syncing it has the latest changes. */
 	dest->hdr = src->hdr;
 	if (dest->hdr_copy_buf != NULL)
@@ -841,6 +884,17 @@
 	dest->hdr_base = buffer_get_modifiable_data(dest->hdr_copy_buf, NULL);
 }
 
+static struct mail_index_record_map *
+mail_index_record_map_alloc(struct mail_index_map *map)
+{
+	struct mail_index_record_map *rec_map;
+
+	rec_map = i_new(struct mail_index_record_map, 1);
+	i_array_init(&rec_map->maps, 4);
+	array_append(&rec_map->maps, &map, 1);
+	return rec_map;
+}
+
 struct mail_index_map *mail_index_map_clone(const struct mail_index_map *map)
 {
 	struct mail_index_map *mem_map;
@@ -850,19 +904,15 @@
 	mem_map = i_new(struct mail_index_map, 1);
 	mem_map->index = map->index;
 	mem_map->refcount = 1;
-
-	mail_index_map_copy(mem_map, map);
+	mem_map->rec_map = mail_index_record_map_alloc(mem_map);
 
-	/* if the map is ever written back to disk, we need to keep track of
-	   what has changed. */
-	if (map->write_atomic)
-		mem_map->write_atomic = TRUE;
-	else {
-		mem_map->write_seq_first = map->write_seq_first;
-		mem_map->write_seq_last = map->write_seq_last;
-		mem_map->write_base_header = map->write_base_header;
-		mem_map->write_ext_header = map->write_ext_header;
-	}
+	mail_index_map_copy_records(mem_map->rec_map, map->rec_map,
+				    map->hdr.record_size);
+	mail_index_map_copy_header(mem_map, map);
+
+	mem_map->write_atomic = map->write_atomic;
+	mem_map->write_base_header = map->write_base_header;
+	mem_map->write_ext_header = map->write_ext_header;
 
 	/* copy extensions */
 	if (array_is_created(&map->ext_id_map)) {
@@ -888,17 +938,29 @@
 
 void mail_index_map_move_to_memory(struct mail_index_map *map)
 {
-	if (map->mmap_base == NULL)
+	struct mail_index_record_map *new_map;
+
+	if (map->rec_map->mmap_base == NULL)
 		return;
 
-	i_assert(map->lock_id != 0);
+	i_assert(map->rec_map->lock_id != 0);
+
+	new_map = array_count(&map->rec_map->maps) == 1 ? map->rec_map :
+		mail_index_record_map_alloc(map);
 
-	mail_index_map_copy(map, map);
+	mail_index_map_copy_records(new_map, map->rec_map,
+				    map->hdr.record_size);
+	mail_index_map_copy_header(map, map);
+
 	mail_index_map_unlock(map);
-
-	if (munmap(map->mmap_base, map->mmap_size) < 0)
-		i_error("munmap(index map) failed: %m");
-	map->mmap_base = NULL;
+	if (new_map != map->rec_map) {
+		mail_index_record_map_unlink(map);
+		map->rec_map = new_map;
+	} else if (new_map->mmap_base != NULL) {
+		if (munmap(new_map->mmap_base, new_map->mmap_size) < 0)
+			mail_index_set_syscall_error(map->index, "munmap()");
+		new_map->mmap_base = NULL;
+	}
 }
 
 int mail_index_map_get_ext_idx(struct mail_index_map *map,
--- a/src/lib-index/mail-index-private.h	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-private.h	Fri Aug 03 03:22:04 2007 +0300
@@ -25,11 +25,11 @@
 	((index)->dir == NULL)
 
 #define MAIL_INDEX_MAP_IS_IN_MEMORY(map) \
-	((map)->buffer != NULL)
+	((map)->rec_map->mmap_base == NULL)
 
 #define MAIL_INDEX_MAP_IDX(map, idx) \
 	((struct mail_index_record *) \
-		PTR_OFFSET((map)->records, (idx) * (map)->hdr.record_size))
+	 PTR_OFFSET((map)->rec_map->records, (idx) * (map)->hdr.record_size))
 
 typedef int mail_index_expunge_handler_t(struct mail_index_sync_map_ctx *ctx,
 					 uint32_t seq, const void *data,
@@ -102,33 +102,43 @@
 	unsigned int expunge_handler_call_always:1;
 };
 
+struct mail_index_record_map {
+	ARRAY_DEFINE(maps, struct mail_index_map *);
+
+	void *mmap_base;
+	size_t mmap_size, mmap_used_size;
+
+	unsigned int lock_count;
+	unsigned int lock_id;
+
+	buffer_t *buffer;
+
+	void *records; /* struct mail_index_record[] */
+	unsigned int records_count;
+
+	/* If this mapping is written to disk and write_atomic=FALSE,
+	   write_seq_* specify the message sequence range that needs to be
+	   written. */
+	uint32_t write_seq_first, write_seq_last;
+};
+
 struct mail_index_map {
 	struct mail_index *index;
 	int refcount;
 
 	struct mail_index_header hdr;
 	const void *hdr_base;
-	void *records; /* struct mail_index_record[] */
-	unsigned int records_count;
+	buffer_t *hdr_copy_buf;
 
 	pool_t extension_pool;
 	ARRAY_DEFINE(extensions, struct mail_index_ext);
 	ARRAY_DEFINE(ext_id_map, uint32_t); /* index -> file */
 
-	void *mmap_base;
-	size_t mmap_size, mmap_used_size;
-	unsigned int lock_id;
-
-	buffer_t *buffer;
-	buffer_t *hdr_copy_buf;
-
 	ARRAY_DEFINE(keyword_idx_map, unsigned int); /* file -> index */
 
-	/* If this mapping is written to disk and write_atomic=FALSE,
-	   write_seq_* specify the message sequence range that needs to be
-	   written. */
-	uint32_t write_seq_first, write_seq_last;
+	struct mail_index_record_map *rec_map;
 
+	unsigned int locked:1;
 	unsigned int keywords_read:1;
 	unsigned int write_base_header:1;
 	unsigned int write_ext_header:1;
--- a/src/lib-index/mail-index-sync-ext.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-sync-ext.c	Fri Aug 03 03:22:04 2007 +0300
@@ -223,11 +223,11 @@
 	new_record_size = offset;
 
 	/* copy the records to new buffer */
-	new_buffer_size = map->records_count * new_record_size;
+	new_buffer_size = map->rec_map->records_count * new_record_size;
 	new_buffer = buffer_create_dynamic(default_pool, new_buffer_size);
-	src = map->records;
+	src = map->rec_map->records;
 	offset = 0;
-	for (rec_idx = 0; rec_idx < map->records_count; rec_idx++) {
+	for (rec_idx = 0; rec_idx < map->rec_map->records_count; rec_idx++) {
 		/* write the base record */
 		buffer_write(new_buffer, offset, src,
 			     sizeof(struct mail_index_record));
@@ -249,9 +249,10 @@
 		buffer_append_zero(new_buffer, space);
 	}
 
-	buffer_free(map->buffer);
-	map->buffer = new_buffer;
-	map->records = buffer_get_modifiable_data(map->buffer, NULL);
+	buffer_free(map->rec_map->buffer);
+	map->rec_map->buffer = new_buffer;
+	map->rec_map->records =
+		buffer_get_modifiable_data(map->rec_map->buffer, NULL);
 	map->hdr.record_size = new_record_size;
 
 	/* update record offsets in headers */
@@ -468,13 +469,13 @@
 				       ext->hdr_size), 0, ext->hdr_size);
 	map->hdr_base = map->hdr_copy_buf->data;
 
-	for (i = 0; i < view->map->records_count; i++) {
+	for (i = 0; i < view->map->rec_map->records_count; i++) {
 		rec = MAIL_INDEX_MAP_IDX(view->map, i);
 		memset(PTR_OFFSET(rec, ext->record_offset), 0,
 		       ext->record_size);
 	}
-	map->write_seq_first = 1;
-	map->write_seq_last = view->map->records_count;
+	map->rec_map->write_seq_first = 1;
+	map->rec_map->write_seq_last = view->map->rec_map->records_count;
 
 	ext_hdr = get_ext_header(map, ext);
 	ext_hdr->reset_id = u->new_reset_id;
@@ -526,7 +527,6 @@
 
 	if (seq == 0)
 		return 1;
-	mail_index_sync_move_to_private(ctx);
 
 	ext = array_idx(&view->map->extensions, ctx->cur_ext_id);
 
--- a/src/lib-index/mail-index-sync-update.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-sync-update.c	Fri Aug 03 03:22:04 2007 +0300
@@ -63,7 +63,8 @@
 {
 	struct mail_index_map *map = ctx->view->map;
 
-	i_assert(MAIL_INDEX_MAP_IS_IN_MEMORY(map) || map->lock_id != 0);
+	i_assert(MAIL_INDEX_MAP_IS_IN_MEMORY(map) ||
+		 map->rec_map->lock_id != 0);
 
 	if (map->refcount > 1) {
 		map = mail_index_map_clone(map);
@@ -135,26 +136,56 @@
 }
 
 static void
+mail_index_sync_header_update_counts_all(struct mail_index_sync_map_ctx *ctx,
+					 uint8_t old_flags, uint8_t new_flags)
+{
+	struct mail_index_map *const *maps;
+	const char *error;
+	unsigned int i, count;
+
+	maps = array_get(&ctx->view->map->rec_map->maps, &count);
+	for (i = 0; i < count; i++) {
+		if (mail_index_header_update_counts(&maps[i]->hdr,
+						    old_flags, new_flags,
+						    &error) < 0)
+			mail_index_sync_set_corrupted(ctx, "%s", error);
+	}
+}
+
+static void
 mail_index_sync_header_update_counts(struct mail_index_sync_map_ctx *ctx,
-				     uint8_t old_flags, uint8_t new_flags)
+				     uint8_t old_flags, uint8_t new_flags,
+				     bool all)
 {
 	const char *error;
 
-	if (mail_index_header_update_counts(&ctx->view->map->hdr,
-					    old_flags, new_flags, &error) < 0)
-		mail_index_sync_set_corrupted(ctx, "%s", error);
+	if (all) {
+		mail_index_sync_header_update_counts_all(ctx, old_flags,
+							 new_flags);
+	} else {
+		if (mail_index_header_update_counts(&ctx->view->map->hdr,
+						    old_flags, new_flags,
+						    &error) < 0)
+			mail_index_sync_set_corrupted(ctx, "%s", error);
+	}
 }
 
 static void
-mail_index_header_update_lowwaters(struct mail_index_header *hdr,
+mail_index_header_update_lowwaters(struct mail_index_sync_map_ctx *ctx,
 				   const struct mail_index_record *rec)
 {
-	if ((rec->flags & MAIL_SEEN) == 0 &&
-	    rec->uid < hdr->first_unseen_uid_lowwater)
-		hdr->first_unseen_uid_lowwater = rec->uid;
-	if ((rec->flags & MAIL_DELETED) != 0 &&
-	    rec->uid < hdr->first_deleted_uid_lowwater)
-		hdr->first_deleted_uid_lowwater = rec->uid;
+	struct mail_index_map *const *maps;
+	unsigned int i, count;
+
+	maps = array_get(&ctx->view->map->rec_map->maps, &count);
+	for (i = 0; i < count; i++) {
+		if ((rec->flags & MAIL_SEEN) == 0 &&
+		    rec->uid < maps[i]->hdr.first_unseen_uid_lowwater)
+			maps[i]->hdr.first_unseen_uid_lowwater = rec->uid;
+		if ((rec->flags & MAIL_DELETED) != 0 &&
+		    rec->uid < maps[i]->hdr.first_deleted_uid_lowwater)
+			maps[i]->hdr.first_deleted_uid_lowwater = rec->uid;
+	}
 }
 
 static int
@@ -210,7 +241,8 @@
 		for (seq = seq1; seq <= seq2; seq++) {
 			rec = MAIL_INDEX_MAP_IDX(map, seq-1);
 			mail_index_sync_header_update_counts(ctx,
-							     rec->flags, 0);
+							     rec->flags, 0,
+							     FALSE);
 		}
 
 		if (sync_expunge_call_handlers(ctx, seq1, seq2) < 0)
@@ -219,10 +251,11 @@
 		/* @UNSAFE */
 		memmove(MAIL_INDEX_MAP_IDX(map, seq1-1),
 			MAIL_INDEX_MAP_IDX(map, seq2),
-			(map->records_count - seq2) * map->hdr.record_size);
+			(map->rec_map->records_count - seq2) *
+			map->hdr.record_size);
 
 		seq_count = seq2 - seq1 + 1;
-		map->records_count -= seq_count;
+		map->rec_map->records_count -= seq_count;
 		map->hdr.messages_count -= seq_count;
 	}
 	return 1;
@@ -233,11 +266,11 @@
 {
 	struct mail_index_map *map = ctx->view->map;
 
-	if (map->write_seq_first == 0 ||
-	    map->write_seq_first > seq1)
-		map->write_seq_first = seq1;
-	if (map->write_seq_last < seq2)
-		map->write_seq_last = seq2;
+	if (map->rec_map->write_seq_first == 0 ||
+	    map->rec_map->write_seq_first > seq1)
+		map->rec_map->write_seq_first = seq1;
+	if (map->rec_map->write_seq_last < seq2)
+		map->rec_map->write_seq_last = seq2;
 }
 
 static int sync_append(const struct mail_index_record *rec,
@@ -263,10 +296,11 @@
 
 	/* don't rely on buffer->used being at the correct position.
 	   at least expunges can move it */
-	append_pos = map->records_count * map->hdr.record_size;
-	dest = buffer_get_space_unsafe(map->buffer, append_pos,
+	append_pos = map->rec_map->records_count * map->hdr.record_size;
+	dest = buffer_get_space_unsafe(map->rec_map->buffer, append_pos,
 				       map->hdr.record_size);
-	map->records = buffer_get_modifiable_data(map->buffer, NULL);
+	map->rec_map->records =
+		buffer_get_modifiable_data(map->rec_map->buffer, NULL);
 
 	memcpy(dest, rec, sizeof(*rec));
 	memset(PTR_OFFSET(dest, sizeof(*rec)), 0,
@@ -274,7 +308,7 @@
 
 	map->hdr.messages_count++;
 	map->hdr.next_uid = rec->uid+1;
-	map->records_count++;
+	map->rec_map->records_count++;
 
 	mail_index_sync_write_seq_update(ctx, map->hdr.messages_count,
 					 map->hdr.messages_count);
@@ -282,8 +316,8 @@
 	if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
 		map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
 
-	mail_index_header_update_lowwaters(&map->hdr, rec);
-	mail_index_sync_header_update_counts(ctx, 0, rec->flags);
+	mail_index_header_update_lowwaters(ctx, rec);
+	mail_index_sync_header_update_counts(ctx, 0, rec->flags, FALSE);
 	return 1;
 }
 
@@ -291,7 +325,6 @@
 			    struct mail_index_sync_map_ctx *ctx)
 {
 	struct mail_index_view *view = ctx->view;
-	struct mail_index_header *hdr;
 	struct mail_index_record *rec;
 	uint8_t flag_mask, old_flags;
 	uint32_t idx, seq1, seq2;
@@ -305,9 +338,8 @@
 
 	mail_index_sync_write_seq_update(ctx, seq1, seq2);
 
-	hdr = &view->map->hdr;
 	if ((u->add_flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
-		hdr->flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
+		view->map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
 
         flag_mask = ~u->remove_flags;
 
@@ -325,9 +357,9 @@
 			old_flags = rec->flags;
 			rec->flags = (rec->flags & flag_mask) | u->add_flags;
 
-			mail_index_header_update_lowwaters(hdr, rec);
+			mail_index_header_update_lowwaters(ctx, rec);
 			mail_index_sync_header_update_counts(ctx, old_flags,
-							     rec->flags);
+							     rec->flags, TRUE);
 		}
 	}
 	return 1;
@@ -554,7 +586,7 @@
 	}
 	t_pop();
 
-	i_assert(ctx->view->map->records_count ==
+	i_assert(ctx->view->map->rec_map->records_count ==
 		 ctx->view->map->hdr.messages_count);
 	return ret;
 }
@@ -590,7 +622,7 @@
 		return;
 
 	/* do we have dirty flags anymore? */
-	for (i = 0; i < map->records_count; i++) {
+	for (i = 0; i < map->rec_map->records_count; i++) {
 		rec = MAIL_INDEX_MAP_IDX(map, i);
 		if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
 			map->hdr.flags |= MAIL_INDEX_HDR_FLAG_HAVE_DIRTY;
@@ -659,7 +691,8 @@
 			index_size = MAIL_INDEX_SYNC_MIN_READ_INDEX_SIZE;
 		} else {
 			index_size = map->hdr.header_size +
-				map->records_count * map->hdr.record_size;
+				map->rec_map->records_count *
+				map->hdr.record_size;
 		}
 
 		/* this isn't necessary correct currently, but it should be
@@ -765,7 +798,7 @@
 
 	buffer_write(map->hdr_copy_buf, 0, &map->hdr, sizeof(map->hdr));
 	if (!MAIL_INDEX_MAP_IS_IN_MEMORY(map)) {
-		memcpy(map->mmap_base, map->hdr_copy_buf->data,
+		memcpy(map->rec_map->mmap_base, map->hdr_copy_buf->data,
 		       map->hdr_copy_buf->used);
 	}
 
--- a/src/lib-index/mail-index-transaction.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-transaction.c	Fri Aug 03 03:22:04 2007 +0300
@@ -223,7 +223,7 @@
 			if (*seq >= t->first_new_seq)
 				rec = mail_index_transaction_lookup(t, *seq);
 			else {
-				i_assert(*seq <= view->map->records_count);
+				i_assert(*seq <= view->map->hdr.messages_count);
 				rec = MAIL_INDEX_MAP_IDX(view->map, *seq - 1);
 			}
 
@@ -233,9 +233,9 @@
 				/* FIXME: replace with simple assert once we
 				   figure out why this happens.. */
 				i_panic("seq = %u, rec->uid = %u, "
-					"first_new_seq = %u, records = %u",
+					"first_new_seq = %u, messages = %u",
 					*seq, rec->uid, t->first_new_seq,
-					view->map->records_count);
+					view->map->hdr.messages_count);
 			}
 			*seq = rec->uid;
 		}
--- a/src/lib-index/mail-index-view-sync.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-view-sync.c	Fri Aug 03 03:22:04 2007 +0300
@@ -324,7 +324,8 @@
 		} else {
 			map = view->map;
 		}
-		i_assert(map->records_count == map->hdr.messages_count);
+		i_assert(map->rec_map->records_count ==
+			 map->hdr.messages_count);
 	}
 
 #ifdef DEBUG
--- a/src/lib-index/mail-index-view.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-view.c	Fri Aug 03 03:22:04 2007 +0300
@@ -244,13 +244,14 @@
 	const struct mail_index_record *rec_base, *rec;
 	uint32_t idx, right_idx, record_size;
 
-	i_assert(view->map->hdr.messages_count <= view->map->records_count);
+	i_assert(view->map->hdr.messages_count <=
+		 view->map->rec_map->records_count);
 
 	if (uid == 1) {
 		/* optimization: the message can be only the first one */
 		return 1;
 	}
-	rec_base = view->map->records;
+	rec_base = view->map->rec_map->records;
 	record_size = view->map->hdr.record_size;
 
 	idx = left_idx;
@@ -355,7 +356,8 @@
 			return 0;
 	}
 
-	i_assert(view->map->hdr.messages_count <= view->map->records_count);
+	i_assert(view->map->hdr.messages_count <=
+		 view->map->rec_map->records_count);
 
 	/* we can delay locking until we're looking at the flags */
 	if (mail_index_map_lock(view->map) < 0)
--- a/src/lib-index/mail-index-write.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index-write.c	Fri Aug 03 03:22:04 2007 +0300
@@ -35,7 +35,8 @@
 				 map->hdr.header_size - base_size);
 	}
 	if (ret == 0) {
-		ret = write_full(fd, map->records, map->records_count *
+		ret = write_full(fd, map->rec_map->records,
+				 map->rec_map->records_count *
 				 map->hdr.record_size);
 	}
 	if (ret < 0)
@@ -69,6 +70,7 @@
 static int mail_index_write_map_over(struct mail_index *index)
 {
 	struct mail_index_map *map = index->map;
+	struct mail_index_record_map *rec_map = map->rec_map;
 	unsigned int base_size;
 
 	if (MAIL_INDEX_IS_IN_MEMORY(index))
@@ -85,14 +87,15 @@
 	}
 
 	/* write records. */
-	if (map->write_seq_first != 0) {
+	if (rec_map->write_seq_first != 0) {
 		size_t rec_offset =
-			(map->write_seq_first-1) * map->hdr.record_size;
+			(rec_map->write_seq_first-1) * map->hdr.record_size;
 		size_t recs_size = map->hdr.record_size *
-			(map->write_seq_last - map->write_seq_first + 1);
+			(rec_map->write_seq_last -
+			 rec_map->write_seq_first + 1);
 
 		if (pwrite_full(index->fd,
-				CONST_PTR_OFFSET(map->records, rec_offset),
+				CONST_PTR_OFFSET(rec_map->records, rec_offset),
 				recs_size,
 				map->hdr.header_size + rec_offset) < 0)
 			return -1;
@@ -130,7 +133,7 @@
 
 #define mail_index_map_has_changed(map) \
 	((map)->write_base_header || (map)->write_ext_header || \
-	 (map)->write_seq_first != 0)
+	 (map)->rec_map->write_seq_first != 0)
 
 void mail_index_write(struct mail_index *index, bool want_rotate)
 {
@@ -154,8 +157,9 @@
 	}
 
 	if (index->last_read_stat.st_size < MAIL_INDEX_MIN_UPDATE_SIZE ||
-	    (map->write_seq_last - map->write_seq_first + 1) +
-	    MAIL_INDEX_MAX_OVERWRITE_NEG_SEQ_COUNT >= map->records_count) {
+	    (map->rec_map->write_seq_last - map->rec_map->write_seq_first + 1) +
+	    MAIL_INDEX_MAX_OVERWRITE_NEG_SEQ_COUNT >=
+	    map->rec_map->records_count) {
 		/* the file is so small that we don't even bother trying to
 		   update it / changes are so large we might as well recreate */
 		map->write_atomic = TRUE;
@@ -216,8 +220,8 @@
 	index->last_read_log_file_head_offset = hdr->log_file_head_offset;
 	index->last_read_log_file_tail_offset = hdr->log_file_tail_offset;
 
+	map->rec_map->write_seq_first = map->rec_map->write_seq_last = 0;
 	map->write_atomic = FALSE;
-	map->write_seq_first = map->write_seq_last = 0;
 	map->write_base_header = FALSE;
 	map->write_ext_header = FALSE;
 
--- a/src/lib-index/mail-index.c	Thu Aug 02 19:06:34 2007 +0300
+++ b/src/lib-index/mail-index.c	Fri Aug 03 03:22:04 2007 +0300
@@ -358,7 +358,7 @@
 	if (MAIL_INDEX_IS_IN_MEMORY(index))
 		return 0;
 
-	i_assert(index->map == NULL || index->map->lock_id == 0);
+	i_assert(index->map == NULL || index->map->rec_map->lock_id == 0);
 	ret = mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD);
 	if (ret == 0) {
 		/* it's corrupted - recreate it */