changeset 6600:416d9ee66047 HEAD

Support for reading maildir files. This makes it possible to do a fast maildir to dbox conversion by only renaming a few files.
author Timo Sirainen <tss@iki.fi>
date Sun, 21 Oct 2007 18:58:00 +0300
parents a6f51026b969
children 835daa872b0a
files src/lib-storage/index/dbox/Makefile.am src/lib-storage/index/dbox/dbox-file-maildir.c src/lib-storage/index/dbox/dbox-file-maildir.h src/lib-storage/index/dbox/dbox-file.c src/lib-storage/index/dbox/dbox-file.h src/lib-storage/index/dbox/dbox-index.c src/lib-storage/index/dbox/dbox-index.h src/lib-storage/index/dbox/dbox-mail.c src/lib-storage/index/dbox/dbox-storage.h src/lib-storage/index/dbox/dbox-sync-rebuild.c
diffstat 10 files changed, 330 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/dbox/Makefile.am	Sun Oct 21 18:54:32 2007 +0300
+++ b/src/lib-storage/index/dbox/Makefile.am	Sun Oct 21 18:58:00 2007 +0300
@@ -10,6 +10,7 @@
 
 libstorage_dbox_a_SOURCES = \
 	dbox-file.c \
+	dbox-file-maildir.c \
 	dbox-index.c \
 	dbox-mail.c \
 	dbox-save.c \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/dbox/dbox-file-maildir.c	Sun Oct 21 18:58:00 2007 +0300
@@ -0,0 +1,85 @@
+/* Copyright (c) 2007-2007 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "dbox-storage.h"
+#include "../maildir/maildir-storage.h"
+#include "../maildir/maildir-filename.h"
+#include "dbox-file.h"
+#include "dbox-file-maildir.h"
+
+static const char *
+dbox_file_maildir_get_flags(struct dbox_file *file, enum dbox_metadata_key key)
+{
+	ARRAY_TYPE(keyword_indexes) keyword_indexes;
+	struct mail_keywords *keywords;
+	enum mail_flags flags;
+	string_t *str;
+	const char *fname;
+
+	if (file->mbox->maildir_sync_keywords == NULL)
+		return NULL;
+
+	fname = strrchr(file->path, '/');
+	i_assert(fname != NULL);
+	fname++;
+
+	t_array_init(&keyword_indexes, 32);
+	maildir_filename_get_flags(file->mbox->maildir_sync_keywords,
+				   fname, &flags, &keyword_indexes);
+	str = t_str_new(64);
+	if (key == DBOX_METADATA_FLAGS)
+		dbox_mail_metadata_flags_append(str, flags);
+	else {
+		keywords = mail_index_keywords_create_from_indexes(
+			file->mbox->ibox.index, &keyword_indexes);
+		dbox_mail_metadata_keywords_append(file->mbox, str, keywords);
+		mail_index_keywords_free(&keywords);
+	}
+	return str_c(str);
+}
+
+const char *dbox_file_maildir_metadata_get(struct dbox_file *file,
+					   enum dbox_metadata_key key)
+{
+	const char *fname;
+	struct stat st;
+	uoff_t size;
+
+	switch (key) {
+	case DBOX_METADATA_FLAGS:
+	case DBOX_METADATA_KEYWORDS:
+		return dbox_file_maildir_get_flags(file, key);
+	case DBOX_METADATA_RECEIVED_TIME:
+	case DBOX_METADATA_SAVE_TIME:
+		if (file->fd != -1) {
+			if (fstat(file->fd, &st) < 0) {
+				dbox_file_set_syscall_error(file, "fstat");
+				return NULL;
+			}
+		} else {
+			if (stat(file->path, &st) < 0) {
+				if (errno == ENOENT)
+					return NULL;
+				dbox_file_set_syscall_error(file, "stat");
+				return NULL;
+			}
+		}
+		if (key == DBOX_METADATA_RECEIVED_TIME)
+			return dec2str(st.st_mtime);
+		else
+			return dec2str(st.st_ctime);
+	case DBOX_METADATA_VIRTUAL_SIZE:
+		fname = strrchr(file->path, '/');
+		i_assert(fname != NULL);
+		maildir_filename_get_size(fname + 1, MAILDIR_EXTRA_VIRTUAL_SIZE,
+					  &size);
+		return dec2str(size);
+	case DBOX_METADATA_EXPUNGED:
+	case DBOX_METADATA_EXT_REF:
+	case DBOX_METADATA_SPACE:
+		break;
+	}
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/dbox/dbox-file-maildir.h	Sun Oct 21 18:58:00 2007 +0300
@@ -0,0 +1,7 @@
+#ifndef DBOX_FILE_MAILDIR_H
+#define DBOX_FILE_MAILDIR_H
+
+const char *dbox_file_maildir_metadata_get(struct dbox_file *file,
+					   enum dbox_metadata_key key);
+
+#endif
--- a/src/lib-storage/index/dbox/dbox-file.c	Sun Oct 21 18:54:32 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-file.c	Sun Oct 21 18:58:00 2007 +0300
@@ -12,6 +12,7 @@
 #include "dbox-storage.h"
 #include "dbox-index.h"
 #include "dbox-file.h"
+#include "dbox-file-maildir.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -128,16 +129,29 @@
 }
 
 static char *
-dbox_file_id_get_path(struct dbox_mailbox *mbox, unsigned int file_id)
+dbox_file_id_get_path(struct dbox_mailbox *mbox, unsigned int file_id,
+		      bool *maildir_file_r)
 {
+	struct dbox_index_record *rec;
+	const char *p;
+
+	*maildir_file_r = FALSE;
 	if ((file_id & DBOX_FILE_ID_FLAG_UID) != 0) {
 		file_id &= ~DBOX_FILE_ID_FLAG_UID;
 		return i_strdup_printf("%s/"DBOX_MAIL_FILE_UID_FORMAT,
 				       mbox->path, file_id);
-	} else {
-		return i_strdup_printf("%s/"DBOX_MAIL_FILE_MULTI_FORMAT,
-				       mbox->path, file_id);
 	}
+
+	rec = dbox_index_record_lookup(mbox->dbox_index, file_id);
+	if (rec != NULL && rec->status == DBOX_INDEX_FILE_STATUS_MAILDIR) {
+		/* data contains <uid> <filename> */
+		*maildir_file_r = TRUE;
+		p = strchr(rec->data, ' ');
+		return i_strdup_printf("%s/%s", mbox->path, p + 1);
+	}
+
+	return i_strdup_printf("%s/"DBOX_MAIL_FILE_MULTI_FORMAT,
+			       mbox->path, file_id);
 }
 
 struct dbox_file *
@@ -145,6 +159,7 @@
 {
 	struct dbox_file *file;
 	unsigned int count;
+	bool maildir;
 
 	file = file_id == 0 ? NULL :
 		dbox_find_and_move_open_file(mbox, file_id);
@@ -162,7 +177,8 @@
 	file->mbox = mbox;
 	if (file_id != 0) {
 		file->file_id = file_id;
-		file->path = dbox_file_id_get_path(mbox, file_id);
+		file->path = dbox_file_id_get_path(mbox, file_id, &maildir);
+		file->maildir_file = maildir;
 	} else {
 		file->path = dbox_generate_tmp_filename(mbox->path);
 	}
@@ -173,24 +189,38 @@
 	return file;
 }
 
+struct dbox_file *
+dbox_file_init_new_maildir(struct dbox_mailbox *mbox, const char *fname)
+{
+	struct dbox_file *file;
+
+	file = dbox_file_init(mbox, 0);
+	file->maildir_file = TRUE;
+	file->path = i_strdup_printf("%s/%s", mbox->path, fname);
+	return file;
+}
+
 int dbox_file_assign_id(struct dbox_file *file, unsigned int file_id)
 {
 	char *new_path;
+	bool maildir;
 
 	i_assert(file->file_id == 0);
 	i_assert(file_id != 0);
 
-	new_path = dbox_file_id_get_path(file->mbox, file_id);
-	if (rename(file->path, new_path) < 0) {
-		mail_storage_set_critical(file->mbox->ibox.box.storage,
-			"rename(%s, %s) failed: %m", file->path, new_path);
-		i_free(new_path);
-		return -1;
+	if (!file->maildir_file) {
+		new_path = dbox_file_id_get_path(file->mbox, file_id, &maildir);
+		if (rename(file->path, new_path) < 0) {
+			mail_storage_set_critical(file->mbox->ibox.box.storage,
+						  "rename(%s, %s) failed: %m",
+						  file->path, new_path);
+			i_free(new_path);
+			return -1;
+		}
+		i_free(file->path);
+		file->path = new_path;
 	}
 
-	i_free(file->path);
-	file->path = new_path;
-
 	file->file_id = file_id;
 	array_append(&file->mbox->open_files, &file, 1);
 	return 0;
@@ -252,7 +282,7 @@
 bool dbox_file_can_append(struct dbox_file *file, uoff_t mail_size)
 {
 	if (file->nonappendable)
-		return 0;
+		return FALSE;
 
 	if (file->append_offset == 0) {
 		/* messages have been expunged */
@@ -352,7 +382,8 @@
 	}
 
 	file->input = i_stream_create_fd(file->fd, MAIL_READ_BLOCK_SIZE, FALSE);
-	return !read_header ? 1 : dbox_file_read_header(file);
+	return !read_header || file->maildir_file ? 1 :
+		dbox_file_read_header(file);
 }
 
 static int dbox_file_create(struct dbox_file *file)
@@ -406,6 +437,30 @@
 		return dbox_file_open(file, read_header, deleted_r);
 }
 
+static int
+dbox_file_get_maildir_data(struct dbox_file *file, uint32_t *uid_r,
+			   uoff_t *physical_size_r)
+{
+	struct dbox_index_record *rec;
+	struct stat st;
+
+	if (fstat(file->fd, &st) < 0) {
+		dbox_file_set_syscall_error(file, "fstat");
+		return -1;
+	}
+
+	rec = dbox_index_record_lookup(file->mbox->dbox_index, file->file_id);
+	if (rec == NULL) {
+		/* should happen only when we're rebuilding the index */
+		*uid_r = 0;
+	} else {
+		i_assert(rec->status == DBOX_INDEX_FILE_STATUS_MAILDIR);
+		*uid_r = strtoul(rec->data, NULL, 10);
+	}
+	*physical_size_r = st.st_size;
+	return 1;
+}
+
 static int dbox_file_read_mail_header(struct dbox_file *file, uint32_t *uid_r,
 				      uoff_t *physical_size_r)
 {
@@ -414,6 +469,9 @@
 	size_t size;
 	int ret;
 
+	if (file->maildir_file)
+		return dbox_file_get_maildir_data(file, uid_r, physical_size_r);
+
 	ret = i_stream_read_data(file->input, &data, &size,
 				 file->msg_header_size - 1);
 	if (ret <= 0) {
@@ -627,6 +685,9 @@
 				     uoff_t physical_size)
 {
 	if (offset == 0) {
+		if (file->maildir_file)
+			return 0;
+
 		i_assert(file->file_header_size != 0);
 		offset = file->file_header_size;
 	}
@@ -683,6 +744,12 @@
 	}
 	file->metadata_read_offset = 0;
 
+	if (file->maildir_file) {
+		/* no metadata in maildir files, but we do later some kludging
+		   to return metadata when needed. */
+		return 0;
+	}
+
 	if (file->input == NULL) {
 		if ((ret = dbox_file_open(file, TRUE, &deleted)) <= 0)
 			return ret;
@@ -749,6 +816,9 @@
 	const char *const *metadata;
 	unsigned int i, count;
 
+	if (file->maildir_file)
+		return dbox_file_maildir_metadata_get(file, key);
+
 	metadata = array_get(&file->metadata, &count);
 	for (i = 0; i < count; i++) {
 		if (*metadata[i] == (char)key)
--- a/src/lib-storage/index/dbox/dbox-file.h	Sun Oct 21 18:54:32 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-file.h	Sun Oct 21 18:58:00 2007 +0300
@@ -102,7 +102,7 @@
 	unsigned int append_offset_header_pos;
 
 	unsigned int append_count;
-	uint32_t last_append_uid;
+	uint32_t last_append_uid, maildir_append_seq;
 
 	uoff_t append_offset;
 	time_t create_time;
@@ -122,6 +122,7 @@
 	/* Includes the trailing LF that shouldn't be used */
 	unsigned int metadata_len;
 
+	unsigned int maildir_file:1;
 	unsigned int nonappendable:1;
 	unsigned int deleted:1;
 };
@@ -131,6 +132,8 @@
 
 struct dbox_file *
 dbox_file_init(struct dbox_mailbox *mbox, unsigned int file_id);
+struct dbox_file *
+dbox_file_init_new_maildir(struct dbox_mailbox *mbox, const char *fname);
 void dbox_file_unref(struct dbox_file **file);
 
 /* Free all currently opened files. */
--- a/src/lib-storage/index/dbox/dbox-index.c	Sun Oct 21 18:54:32 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-index.c	Sun Oct 21 18:58:00 2007 +0300
@@ -28,6 +28,7 @@
 	uint32_t uid_validity, next_uid;
 	unsigned int next_file_id;
 
+	pool_t record_data_pool;
 	ARRAY_DEFINE(records, struct dbox_index_record);
 };
 
@@ -55,6 +56,8 @@
 	index->next_uid = 1;
 	index->next_file_id = 1;
 	i_array_init(&index->records, 128);
+	index->record_data_pool =
+		pool_alloconly_create("dbox index record data", 256);
 	return index;
 }
 
@@ -77,10 +80,25 @@
 
 	dbox_index_close(index);
 	array_free(&index->records);
+	pool_unref(&index->record_data_pool);
 	i_free(index->path);
 	i_free(index);
 }
 
+static int dbox_index_parse_maildir(struct dbox_index *index, const char *line,
+				    struct dbox_index_record *rec)
+{
+	char *p;
+	unsigned long uid;
+
+	uid = strtoul(line, &p, 10);
+	if (*p++ != ' ' || *p == '\0' || uid == 0 || uid >= (uint32_t)-1)
+		return -1;
+
+	rec->data = p_strdup(index->record_data_pool, p);
+	return 0;
+}
+
 static int dbox_index_parse_line(struct dbox_index *index, const char *line,
 				 unsigned int offset)
 {
@@ -109,6 +127,10 @@
 	rec.dirty = line[2] != '0';
 
 	line += 3;
+	if (rec.status == DBOX_INDEX_FILE_STATUS_MAILDIR) {
+		if (dbox_index_parse_maildir(index, line, &rec) < 0)
+			return -1;
+	}
 	array_append(&index->records, &rec, 1);
 	return 0;
 }
@@ -201,6 +223,7 @@
 		return -1;
 	}
 
+	p_clear(index->record_data_pool);
 	array_clear(&index->records);
 	input = index->input = i_stream_create_fd(index->fd, 1024, FALSE);
 
@@ -479,7 +502,8 @@
 	case DBOX_INDEX_FILE_STATUS_SINGLE_MESSAGE:
 		break;
 	case DBOX_INDEX_FILE_STATUS_MAILDIR:
-		/* FIXME */
+		str_append_c(str, ' ');
+		str_append(str, rec->data);
 		break;
 	}
 	str_append_c(str, '\n');
@@ -697,6 +721,13 @@
 	return 0;
 }
 
+void dbox_index_append_file(struct dbox_index_append_context *ctx,
+			    struct dbox_file *file)
+{
+	file->refcount++;
+	array_append(&ctx->files, &file, 1);
+}
+
 static int dbox_index_append_commit_new(struct dbox_index_append_context *ctx,
 					struct dbox_file *file, string_t *str)
 {
@@ -707,7 +738,8 @@
 
 	i_assert(file->append_count > 0);
 
-	if (file->append_count == 1 && !dbox_file_can_append(file, 0)) {
+	if (file->append_count == 1 && !file->maildir_file &&
+	    !dbox_file_can_append(file, 0)) {
 		/* single UID message file */
 		i_assert(file->last_append_uid != 0);
 		file_id = file->last_append_uid | DBOX_FILE_ID_FLAG_UID;
@@ -740,9 +772,17 @@
 	memset(&rec, 0, sizeof(rec));
 	rec.file_id = file_id;
 	rec.file_offset = ctx->output_offset + str_len(str);
-	rec.status = dbox_file_can_append(file, 0) ?
-		DBOX_INDEX_FILE_STATUS_APPENDABLE :
-		DBOX_INDEX_FILE_STATUS_NONAPPENDABLE;
+	if (file->maildir_file) {
+		rec.status = DBOX_INDEX_FILE_STATUS_MAILDIR;
+		rec.data = p_strdup_printf(ctx->index->record_data_pool,
+					   "%u %s", file->last_append_uid,
+					   strrchr(file->path, '/') + 1);
+
+	} else {
+		rec.status = dbox_file_can_append(file, 0) ?
+			DBOX_INDEX_FILE_STATUS_APPENDABLE :
+			DBOX_INDEX_FILE_STATUS_NONAPPENDABLE;
+	}
 
 	array_append(&ctx->index->records, &rec, 1);
 	dbox_index_append_record(&rec, str);
@@ -791,7 +831,7 @@
 		ret = -1;
 	}
 	dbox_index_unlock_range(ctx->index, ctx->output_offset, str_len(str));
-	return ret;
+	return ret < 0 ? -1 : 0;
 }
 
 static int dbox_index_write_header(struct dbox_index *index)
--- a/src/lib-storage/index/dbox/dbox-index.h	Sun Oct 21 18:54:32 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-index.h	Sun Oct 21 18:58:00 2007 +0300
@@ -80,6 +80,8 @@
 	unsigned int file_offset;
 
 	enum dbox_index_file_status status;
+	const char *data;
+
 	unsigned int expunges:1;
 	unsigned int dirty:1;
 	unsigned int locked:1;
@@ -105,8 +107,6 @@
 /* Try to lock index file for recreating. Returns 1 if ok, 0 if file already
    contains locks, -1 if error. */
 int dbox_index_try_lock_recreate(struct dbox_index *index);
-/* Lock index file for syncing. Returns 0 if ok, -1 if error. */
-int dbox_index_lock_sync(struct dbox_index *index);
 
 struct dbox_index_append_context *
 dbox_index_append_begin(struct dbox_index *index);
@@ -117,6 +117,8 @@
 			   uoff_t mail_size,
 			   struct dbox_file **file_r,
 			   struct ostream **output_r);
+void dbox_index_append_file(struct dbox_index_append_context *ctx,
+			    struct dbox_file *file);
 /* Assign file_ids to all appended files. */
 int dbox_index_append_assign_file_ids(struct dbox_index_append_context *ctx);
 /* Returns 0 if ok, -1 if error. */
--- a/src/lib-storage/index/dbox/dbox-mail.c	Sun Oct 21 18:54:32 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-mail.c	Sun Oct 21 18:58:00 2007 +0300
@@ -187,7 +187,7 @@
 	struct dbox_mail *mail = (struct dbox_mail *)_mail;
 	struct index_mail_data *data = &mail->imail.data;
 	struct istream *input;
-	uoff_t offset;
+	uoff_t offset, size;
 	uint32_t uid;
 	bool expunged;
 	int ret;
@@ -197,8 +197,7 @@
 			return -1;
 
 		ret = dbox_file_get_mail_stream(mail->open_file, offset, &uid,
-						&data->physical_size, &input,
-						&expunged);
+						&size, &input, &expunged);
 		if (ret < 0)
 			return -1;
 		if (ret > 0 && expunged) {
@@ -211,6 +210,7 @@
 				i_stream_unref(&input);
 			return -1;
 		}
+		data->physical_size = size;
 		data->stream = input;
 	}
 
--- a/src/lib-storage/index/dbox/dbox-storage.h	Sun Oct 21 18:54:32 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-storage.h	Sun Oct 21 18:58:00 2007 +0300
@@ -43,6 +43,8 @@
 	uint32_t dbox_ext_id, dbox_hdr_ext_id;
 	/* timestamp when the mailbox was last modified interactively */
 	time_t last_interactive_change;
+	/* set while rebuilding indexes with converted maildir files */
+	struct maildir_keywords_sync_ctx *maildir_sync_keywords;
 
 	uoff_t rotate_size, rotate_min_size;
 	unsigned int rotate_days;
--- a/src/lib-storage/index/dbox/dbox-sync-rebuild.c	Sun Oct 21 18:54:32 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-sync-rebuild.c	Sun Oct 21 18:58:00 2007 +0300
@@ -1,7 +1,10 @@
 /* Copyright (c) 2007 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "array.h"
 #include "dbox-storage.h"
+#include "../maildir/maildir-uidlist.h"
+#include "../maildir/maildir-keywords.h"
 #include "dbox-index.h"
 #include "dbox-file.h"
 #include "dbox-sync.h"
@@ -11,7 +14,11 @@
 
 struct dbox_sync_rebuild_context {
 	struct dbox_mailbox *mbox;
+	struct dbox_index_append_context *append_ctx;
 	struct mail_index_transaction *trans;
+
+	struct maildir_uidlist *maildir_uidlist;
+	struct maildir_keywords *mk;
 };
 
 static int dbox_sync_set_uidvalidity(struct dbox_sync_rebuild_context *ctx)
@@ -62,6 +69,7 @@
 {
 	uint32_t seq, uid;
 	uoff_t metadata_offset, physical_size;
+	const char *fname;
 	bool expunged;
 	int ret;
 
@@ -83,6 +91,18 @@
 		i_warning("%s: Header contains wrong UID %u", file->path, uid);
 		return 0;
 	}
+	if (file->maildir_file) {
+		i_assert(uid == 0);
+		fname = strrchr(file->path, '/');
+		i_assert(fname != NULL);
+		if (!maildir_uidlist_get_uid(ctx->maildir_uidlist, fname + 1,
+					     &uid)) {
+			/* FIXME: not in uidlist, give it an uid */
+			return 0;
+		}
+		file->append_count = 1;
+		file->last_append_uid = uid;
+	}
 
 	metadata_offset =
 		dbox_file_get_metadata_offset(file, *offset, physical_size);
@@ -95,6 +115,7 @@
 	}
 	if (!expunged) {
 		mail_index_append(ctx->trans, uid, &seq);
+		file->maildir_append_seq = seq;
 		dbox_sync_index_metadata(ctx, file, seq);
 	}
 	return 1;
@@ -132,12 +153,39 @@
 	return 0;
 }
 
+static int
+dbox_sync_index_maildir_file(struct dbox_sync_rebuild_context *ctx,
+			     const char *fname)
+{
+	struct dbox_file *file;
+	uoff_t offset = 0;
+	int ret;
+
+	if (ctx->mbox->maildir_sync_keywords == NULL) {
+		ctx->maildir_uidlist =
+			maildir_uidlist_init_readonly(&ctx->mbox->ibox);
+		ctx->mk = maildir_keywords_init_readonly(&ctx->mbox->ibox.box);
+		ctx->mbox->maildir_sync_keywords =
+			maildir_keywords_sync_init(ctx->mk,
+						   ctx->mbox->ibox.index);
+
+		if (maildir_uidlist_refresh(ctx->maildir_uidlist) < 0)
+			return -1;
+	}
+
+	file = dbox_file_init_new_maildir(ctx->mbox, fname);
+	if ((ret = dbox_sync_index_file_next(ctx, file, &offset)) > 0)
+		dbox_index_append_file(ctx->append_ctx, file);
+	dbox_file_unref(&file);
+	return ret;
+}
+
 static int dbox_sync_index_rebuild_ctx(struct dbox_sync_rebuild_context *ctx)
 {
 	struct mail_storage *storage = ctx->mbox->ibox.box.storage;
 	DIR *dir;
 	struct dirent *d;
-	int ret = 0;
+	int ret = 1;
 
 	if (dbox_sync_set_uidvalidity(ctx) < 0)
 		return -1;
@@ -153,13 +201,15 @@
 		return -1;
 	}
 	errno = 0;
-	for (; ret == 0 && (d = readdir(dir)) != NULL; errno = 0) {
+	for (; ret > 0 && (d = readdir(dir)) != NULL; errno = 0) {
 		if (strncmp(d->d_name, DBOX_MAIL_FILE_UID_PREFIX,
 			    sizeof(DBOX_MAIL_FILE_UID_PREFIX)-1) == 0)
 			ret = dbox_sync_index_uid_file(ctx, d->d_name);
 		else if (strncmp(d->d_name, DBOX_MAIL_FILE_MULTI_PREFIX,
 				 sizeof(DBOX_MAIL_FILE_MULTI_PREFIX)-1) == 0)
 			ret = dbox_sync_index_multi_file(ctx, d->d_name);
+		else if (strstr(d->d_name, ":2,") != NULL)
+			ret = dbox_sync_index_maildir_file(ctx, d->d_name);
 	}
 	if (errno != 0) {
 		mail_storage_set_critical(storage,
@@ -175,6 +225,25 @@
 	return ret;
 }
 
+static void dbox_sync_update_maildir_ids(struct dbox_sync_rebuild_context *ctx)
+{
+	struct dbox_mail_index_record rec;
+	struct dbox_file *const *files;
+	unsigned int i, count;
+
+	memset(&rec, 0, sizeof(rec));
+	files = array_get(&ctx->mbox->open_files, &count);
+	for (i = 0; i < count; i++) {
+		if (!files[i]->maildir_file)
+			continue;
+
+		i_assert(files[i]->file_id != 0);
+		rec.file_id = files[i]->file_id;
+		mail_index_update_ext(ctx->trans, files[i]->maildir_append_seq,
+				      ctx->mbox->dbox_ext_id, &rec, NULL);
+	}
+}
+
 int dbox_sync_index_rebuild(struct dbox_mailbox *mbox)
 {
 	struct dbox_sync_rebuild_context ctx;
@@ -185,6 +254,7 @@
 
 	memset(&ctx, 0, sizeof(ctx));
 	ctx.mbox = mbox;
+	ctx.append_ctx = dbox_index_append_begin(mbox->dbox_index);
 	view = mail_index_view_open(mbox->ibox.index);
 	ctx.trans = mail_index_transaction_begin(view,
 					MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
@@ -192,8 +262,26 @@
 
 	if ((ret = dbox_sync_index_rebuild_ctx(&ctx)) < 0)
 		mail_index_transaction_rollback(&ctx.trans);
+	else {
+		ret = dbox_index_append_assign_file_ids(ctx.append_ctx);
+		if (ret == 0) {
+			dbox_sync_update_maildir_ids(&ctx);
+			ret = mail_index_transaction_commit(&ctx.trans,
+							    &seq, &offset);
+		}
+	}
+	mail_index_view_close(&view);
+
+	if (ret == 0)
+		ret = dbox_index_append_commit(&ctx.append_ctx);
 	else
-		ret = mail_index_transaction_commit(&ctx.trans, &seq, &offset);
-	mail_index_view_close(&view);
+		dbox_index_append_rollback(&ctx.append_ctx);
+
+	if (mbox->maildir_sync_keywords != NULL)
+		maildir_keywords_sync_deinit(&mbox->maildir_sync_keywords);
+	if (ctx.mk != NULL)
+		maildir_keywords_deinit(&ctx.mk);
+	if (ctx.maildir_uidlist != NULL)
+		maildir_uidlist_deinit(&ctx.maildir_uidlist);
 	return ret;
 }