changeset 6668:4a3cc2968040 HEAD

Support for looking up dbox files from an alternative directory if they're not found from the primary directory.
author Timo Sirainen <tss@iki.fi>
date Sat, 03 Nov 2007 16:09:05 +0200
parents b6c76ea86e2a
children dd9d618fefb9
files src/lib-storage/index/cydir/cydir-storage.c src/lib-storage/index/dbox/dbox-file-maildir.c 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-save.c src/lib-storage/index/dbox/dbox-storage.c src/lib-storage/index/dbox/dbox-storage.h src/lib-storage/index/dbox/dbox-sync-file.c src/lib-storage/index/dbox/dbox-sync-rebuild.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/mailbox-list-private.h src/lib-storage/mailbox-list.c
diffstat 14 files changed, 207 insertions(+), 88 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/cydir/cydir-storage.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/cydir/cydir-storage.c	Sat Nov 03 16:09:05 2007 +0200
@@ -58,7 +58,8 @@
 
 	if (debug)
 		i_info("cydir: data=%s", data);
-	return mailbox_list_settings_parse(data, list_set, layout_r, error_r);
+	return mailbox_list_settings_parse(data, list_set, layout_r, NULL,
+					   error_r);
 }
 
 static struct mail_storage *cydir_alloc(void)
--- a/src/lib-storage/index/dbox/dbox-file-maildir.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-file-maildir.c	Sat Nov 03 16:09:05 2007 +0200
@@ -16,18 +16,13 @@
 	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);
+				   file->fname, &flags, &keyword_indexes);
 	str = t_str_new(64);
 	if (key == DBOX_METADATA_FLAGS)
 		dbox_mail_metadata_flags_append(str, flags);
@@ -43,7 +38,6 @@
 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;
 
@@ -59,7 +53,7 @@
 				return NULL;
 			}
 		} else {
-			if (stat(file->path, &st) < 0) {
+			if (stat(dbox_file_get_path(file), &st) < 0) {
 				if (errno == ENOENT)
 					return NULL;
 				dbox_file_set_syscall_error(file, "stat");
@@ -71,10 +65,8 @@
 		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);
+		maildir_filename_get_size(file->fname,
+					  MAILDIR_EXTRA_VIRTUAL_SIZE, &size);
 		return dec2str(size);
 	case DBOX_METADATA_EXPUNGED:
 	case DBOX_METADATA_EXT_REF:
--- a/src/lib-storage/index/dbox/dbox-file.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-file.c	Sat Nov 03 16:09:05 2007 +0200
@@ -33,12 +33,12 @@
 
 static int dbox_file_metadata_skip_header(struct dbox_file *file);
 
-static char *dbox_generate_tmp_filename(const char *path)
+static char *dbox_generate_tmp_filename(void)
 {
 	static unsigned int create_count = 0;
 
-	return i_strdup_printf("%s/temp.%s.P%sQ%uM%s.%s",
-			       path, dec2str(ioloop_timeval.tv_sec), my_pid,
+	return i_strdup_printf("temp.%s.P%sQ%uM%s.%s",
+			       dec2str(ioloop_timeval.tv_sec), my_pid,
 			       create_count++,
 			       dec2str(ioloop_timeval.tv_usec), my_hostname);
 }
@@ -46,14 +46,16 @@
 void dbox_file_set_syscall_error(struct dbox_file *file, const char *function)
 {
 	mail_storage_set_critical(file->mbox->ibox.box.storage,
-				  "%s(%s) failed: %m", function, file->path);
+				  "%s(%s) failed: %m", function,
+				  dbox_file_get_path(file));
 }
 
 static void
 dbox_file_set_corrupted(struct dbox_file *file, const char *reason)
 {
 	mail_storage_set_critical(file->mbox->ibox.box.storage,
-				  "%s corrupted: %s", file->path, reason);
+				  "%s corrupted: %s", dbox_file_get_path(file),
+				  reason);
 }
 
 
@@ -91,7 +93,8 @@
 			dbox_file_set_syscall_error(file, "close");
 		file->fd = -1;
 	}
-	i_free(file->path);
+	i_free(file->current_path);
+	i_free(file->fname);
 	i_free(file);
 }
 
@@ -129,8 +132,8 @@
 }
 
 static char *
-dbox_file_id_get_path(struct dbox_mailbox *mbox, unsigned int file_id,
-		      bool *maildir_file_r)
+dbox_file_id_get_fname(struct dbox_mailbox *mbox, unsigned int file_id,
+		       bool *maildir_file_r)
 {
 	struct dbox_index_record *rec;
 	const char *p;
@@ -138,8 +141,7 @@
 	*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);
+		return i_strdup_printf(DBOX_MAIL_FILE_UID_FORMAT, file_id);
 	}
 
 	rec = dbox_index_record_lookup(mbox->dbox_index, file_id);
@@ -147,11 +149,10 @@
 		/* 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", p + 1);
 	}
 
-	return i_strdup_printf("%s/"DBOX_MAIL_FILE_MULTI_FORMAT,
-			       mbox->path, file_id);
+	return i_strdup_printf(DBOX_MAIL_FILE_MULTI_FORMAT, file_id);
 }
 
 struct dbox_file *
@@ -177,10 +178,16 @@
 	file->mbox = mbox;
 	if (file_id != 0) {
 		file->file_id = file_id;
-		file->path = dbox_file_id_get_path(mbox, file_id, &maildir);
+		file->fname = dbox_file_id_get_fname(mbox, file_id, &maildir);
 		file->maildir_file = maildir;
 	} else {
-		file->path = dbox_generate_tmp_filename(mbox->path);
+		file->fname = dbox_generate_tmp_filename();
+	}
+	if (file->maildir_file || file_id == 0) {
+		/* newly created files and maildir files always exist in the
+		   primary path */
+		file->current_path =
+			i_strdup_printf("%s/%s", mbox->path, file->fname);
 	}
 	file->fd = -1;
 
@@ -196,33 +203,41 @@
 
 	file = dbox_file_init(mbox, 0);
 	file->maildir_file = TRUE;
-	file->path = i_strdup_printf("%s/%s", mbox->path, fname);
+	file->fname = i_strdup(fname);
 	return file;
 }
 
 int dbox_file_assign_id(struct dbox_file *file, unsigned int file_id)
 {
-	char *new_path;
+	struct dbox_mailbox *mbox = file->mbox;
+	const char *old_path;
+	char *new_fname, *new_path;
 	bool maildir;
 
 	i_assert(file->file_id == 0);
 	i_assert(file_id != 0);
 
 	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,
+		old_path = dbox_file_get_path(file);
+		new_fname = dbox_file_id_get_fname(mbox, file_id, &maildir);
+		new_path = i_strdup_printf("%s/%s", mbox->path, new_fname);
+
+		if (rename(old_path, new_path) < 0) {
+			mail_storage_set_critical(mbox->ibox.box.storage,
 						  "rename(%s, %s) failed: %m",
-						  file->path, new_path);
+						  old_path, new_path);
+			i_free(new_fname);
 			i_free(new_path);
 			return -1;
 		}
-		i_free(file->path);
-		file->path = new_path;
+		i_free(file->fname);
+		i_free(file->current_path);
+		file->fname = new_fname;
+		file->current_path = new_path;
 	}
 
 	file->file_id = file_id;
-	array_append(&file->mbox->open_files, &file, 1);
+	array_append(&mbox->open_files, &file, 1);
 	return 0;
 }
 
@@ -362,23 +377,53 @@
 	return dbox_file_parse_header(file, line) < 0 ? 0 : 1;
 }
 
+static int dbox_file_open_fd(struct dbox_file *file)
+{
+	const char *path;
+	int i;
+
+	/* try the primary path first */
+	path = t_strdup_printf("%s/%s", file->mbox->path, file->fname);
+	for (i = 0;; i++) {
+		file->fd = open(path, O_RDWR);
+		if (file->fd != -1)
+			break;
+
+		if (errno != ENOENT) {
+			mail_storage_set_critical(file->mbox->ibox.box.storage,
+						  "open(%s) failed: %m", path);
+			return -1;
+		}
+
+		if (file->mbox->alt_path == NULL || i == 1) {
+			/* file doesn't exist */
+			return 0;
+		}
+
+		/* try the alternative path */
+		path = t_strdup_printf("%s/%s", file->mbox->alt_path,
+				       file->fname);
+	}
+	return 1;
+}
+
 static int dbox_file_open(struct dbox_file *file, bool read_header,
 			  bool *deleted_r)
 {
+	int ret;
+
 	i_assert(file->input == NULL);
 
 	*deleted_r = FALSE;
 
-	if (file->fd == -1)
-		file->fd = open(file->path, O_RDWR);
 	if (file->fd == -1) {
-		if (errno == ENOENT) {
+		ret = dbox_file_open_fd(file);
+		if (ret <= 0) {
+			if (ret < 0)
+				return -1;
 			*deleted_r = TRUE;
 			return 1;
 		}
-
-		dbox_file_set_syscall_error(file, "open");
-		return -1;
 	}
 
 	file->input = i_stream_create_fd(file->fd, MAIL_READ_BLOCK_SIZE, FALSE);
@@ -393,10 +438,14 @@
 
 	i_assert(file->fd == -1);
 
-	file->fd = open(file->path, O_RDWR | O_CREAT | O_TRUNC, 0600);
+	if (file->current_path == NULL) {
+		file->current_path =
+			i_strdup_printf("%s/%s", file->mbox->path, file->fname);
+	}
+	file->fd = open(file->current_path, O_RDWR | O_CREAT | O_TRUNC, 0600);
 	if (file->fd == -1) {
 		mail_storage_set_critical(file->mbox->ibox.box.storage,
-			"open(%s, O_CREAT) failed: %m", file->path);
+			"open(%s, O_CREAT) failed: %m", file->current_path);
 		return -1;
 	}
 	file->output = o_stream_create_fd_file(file->fd, 0, FALSE);
@@ -437,6 +486,13 @@
 		return dbox_file_open(file, read_header, deleted_r);
 }
 
+const char *dbox_file_get_path(struct dbox_file *file)
+{
+	i_assert(file->current_path != NULL);
+
+	return file->current_path;
+}
+
 static int
 dbox_file_get_maildir_data(struct dbox_file *file, uint32_t *uid_r,
 			   uoff_t *physical_size_r)
@@ -911,7 +967,8 @@
 		len = len - file->metadata_len + DBOX_EXTRA_SPACE;
 		ret = dbox_file_write_empty_block(file, offset, len);
 	} else {
-		i_error("%s: Metadata changed unexpectedly", file->path);
+		i_error("%s: Metadata changed unexpectedly",
+			dbox_file_get_path(file));
 		ret = 0;
 	}
 
--- a/src/lib-storage/index/dbox/dbox-file.h	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-file.h	Sat Nov 03 16:09:05 2007 +0200
@@ -108,7 +108,9 @@
 	time_t create_time;
 	uoff_t output_stream_offset;
 
-	char *path;
+	char *fname;
+	char *current_path;
+
 	int fd;
 	struct istream *input;
 	struct ostream *output;
@@ -148,6 +150,10 @@
 int dbox_file_open_or_create(struct dbox_file *file, bool read_header,
 			     bool *deleted_r);
 
+/* Returns the current fulle path for an opened/created file. It's an error to
+   call this function for a non-opened file. */
+const char *dbox_file_get_path(struct dbox_file *file);
+
 /* Seek to given offset in file and return the message's input stream, UID
    and physical size. Returns 1 if ok, 0 if file/offset is corrupted,
    -1 if I/O error. */
--- a/src/lib-storage/index/dbox/dbox-index.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-index.c	Sat Nov 03 16:09:05 2007 +0200
@@ -718,7 +718,7 @@
 		if ((ret = dbox_file_get_append_stream(file, mail_size,
 						       output_r)) <= 0) {
 			i_assert(ret < 0);
-			(void)unlink(file->path);
+			(void)unlink(dbox_file_get_path(file));
 			dbox_file_unref(&file);
 			return -1;
 		}
@@ -784,7 +784,7 @@
 		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);
+					   file->fname);
 
 	} else {
 		rec.status = dbox_file_can_append(file, 0) ?
@@ -806,9 +806,9 @@
 	files = array_get(&ctx->files, &count);
 	for (i = 0; i < count; i++) {
 		if (files[i]->file_id >= ctx->first_new_file_id) {
-			if (unlink(files[i]->path) < 0) {
+			if (unlink(dbox_file_get_path(files[i])) < 0) {
 				i_error("unlink(%s) failed: %m",
-					files[i]->path);
+					dbox_file_get_path(files[i]));
 			}
 			files[i]->deleted = TRUE;
 		} else {
@@ -928,8 +928,10 @@
 		if (file->file_id != 0)
 			dbox_index_unlock_file(ctx->index, file->file_id);
 		else {
-			if (unlink(file->path) < 0)
-				i_error("unlink(%s) failed: %m", file->path);
+			if (unlink(dbox_file_get_path(file)) < 0) {
+				i_error("unlink(%s) failed: %m",
+					dbox_file_get_path(file));
+			}
 		}
 		dbox_file_unref(&file);
 	}
--- a/src/lib-storage/index/dbox/dbox-save.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-save.c	Sat Nov 03 16:09:05 2007 +0200
@@ -74,6 +74,7 @@
 	struct dbox_message_header dbox_msg_hdr;
 	struct dbox_save_mail *save_mail;
 	struct istream *crlf_input;
+	const char *cur_path;
 	enum mail_flags save_flags;
 	const struct stat *st;
 	uoff_t mail_size;
@@ -98,6 +99,7 @@
 		ctx->failed = TRUE;
 		return -1;
 	}
+	cur_path = dbox_file_get_path(ctx->cur_file);
 
 	/* add to index */
 	save_flags = flags & ~MAIL_RECENT;
@@ -134,8 +136,7 @@
 	if (o_stream_send(ctx->cur_output, &dbox_msg_hdr,
 			  sizeof(dbox_msg_hdr)) < 0) {
 		mail_storage_set_critical(_t->box->storage,
-			"o_stream_send(%s) failed: %m",
-			ctx->cur_file->path);
+			"o_stream_send(%s) failed: %m", cur_path);
 		ctx->failed = TRUE;
 	}
 
@@ -152,16 +153,18 @@
 {
 	struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
 	struct mail_storage *storage = &ctx->mbox->storage->storage;
+	const char *cur_path;
 
 	if (ctx->failed)
 		return -1;
 
+	cur_path = dbox_file_get_path(ctx->cur_file);
 	do {
 		if (o_stream_send_istream(ctx->cur_output, ctx->input) < 0) {
 			if (!mail_storage_set_error_from_errno(storage)) {
 				mail_storage_set_critical(storage,
 					"o_stream_send_istream(%s) failed: %m",
-					ctx->cur_file->path);
+					cur_path);
 			}
 			ctx->failed = TRUE;
 			return -1;
@@ -221,6 +224,7 @@
 	struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
 	struct mail_storage *storage = &ctx->mbox->storage->storage;
 	struct dbox_save_mail *save_mail;
+	const char *cur_path;
 	uoff_t offset = 0;
 	unsigned int count;
 
@@ -232,17 +236,17 @@
 				      ctx->cur_received_date);
 
 	if (!ctx->failed) {
+		cur_path = dbox_file_get_path(ctx->cur_file);
 		offset = ctx->cur_output->offset;
 		dbox_save_write_metadata(ctx);
 		if (o_stream_flush(ctx->cur_output) < 0) {
 			mail_storage_set_critical(storage,
-				"o_stream_flush(%s) failed: %m",
-				ctx->cur_file->path);
+				"o_stream_flush(%s) failed: %m", cur_path);
 			ctx->failed = TRUE;
 		}
 	}
 
-	o_stream_destroy(&ctx->cur_output);
+	o_stream_unref(&ctx->cur_output);
 	i_stream_unref(&ctx->input);
 
 	count = array_count(&ctx->mails);
@@ -332,7 +336,7 @@
 	const struct dbox_save_mail *m1 = p1, *m2 = p2;
 	int ret;
 
-	ret = strcmp(m1->file->path, m2->file->path);
+	ret = strcmp(m1->file->fname, m2->file->fname);
 	if (ret == 0) {
 		/* the oldest sequence is first. this is needed for uncommit
 		   to work right. */
--- a/src/lib-storage/index/dbox/dbox-storage.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-storage.c	Sat Nov 03 16:09:05 2007 +0200
@@ -40,7 +40,8 @@
 static int
 dbox_get_list_settings(struct mailbox_list_settings *list_set,
 		       const char *data, enum mail_storage_flags flags,
-		       const char **layout_r, const char **error_r)
+		       const char **layout_r, const char **alt_dir_r,
+		       const char **error_r)
 {
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 
@@ -60,7 +61,8 @@
 
 	if (debug)
 		i_info("dbox: data=%s", data);
-	return mailbox_list_settings_parse(data, list_set, layout_r, error_r);
+	return mailbox_list_settings_parse(data, list_set, layout_r, alt_dir_r,
+					   error_r);
 }
 
 static struct mail_storage *dbox_alloc(void)
@@ -82,10 +84,10 @@
 	struct dbox_storage *storage = (struct dbox_storage *)_storage;
 	struct mailbox_list_settings list_set;
 	struct stat st;
-	const char *layout;
+	const char *layout, *alt_dir;
 
 	if (dbox_get_list_settings(&list_set, data, _storage->flags,
-				   &layout, error_r) < 0)
+				   &layout, &alt_dir, error_r) < 0)
 		return -1;
 	list_set.mail_storage_flags = &_storage->flags;
 	list_set.lock_method = &_storage->lock_method;
@@ -115,6 +117,7 @@
 	if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0)
 		return -1;
 	storage->list_module_ctx.super = _storage->list->v;
+	storage->alt_dir = p_strdup(_storage->pool, alt_dir);
 	_storage->list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
 	_storage->list->v.delete_mailbox = dbox_list_delete_mailbox;
 
@@ -139,6 +142,21 @@
 	return 0;
 }
 
+static const char *
+dbox_get_alt_path(struct dbox_storage *storage, const char *path)
+{
+	unsigned int len;
+
+	if (storage->alt_dir == NULL)
+		return NULL;
+
+	len = strlen(storage->alt_dir);
+	if (strncmp(path, storage->alt_dir, len) != 0)
+		return t_strconcat(storage->alt_dir, path + len, NULL);
+	else
+		return NULL;
+}
+
 static struct mailbox *
 dbox_open(struct dbox_storage *storage, const char *name,
 	  enum mailbox_open_flags flags)
@@ -164,6 +182,7 @@
 	mbox->ibox.mail_vfuncs = &dbox_mail_vfuncs;
 	mbox->ibox.index = index;
 	mbox->path = p_strdup(pool, path);
+	mbox->alt_path = p_strdup(pool, dbox_get_alt_path(storage, path));
 	mbox->storage = storage;
 	mbox->last_interactive_change = ioloop_time;
 
@@ -344,7 +363,7 @@
 {
 	struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
 	struct stat st;
-	const char *src;
+	const char *path, *alt_path;
 
 	/* Make sure the indexes are closed before trying to delete the
 	   directory that contains them. It can still fail with some NFS
@@ -357,14 +376,23 @@
 		return -1;
 
 	/* check if the mailbox actually exists */
-	src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	if (stat(src, &st) != 0 && errno == ENOENT) {
+	path = mailbox_list_get_path(list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (stat(path, &st) != 0 && errno == ENOENT) {
 		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
 			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
 		return -1;
 	}
 
-	return dbox_delete_nonrecursive(list, src, name);
+	if (dbox_delete_nonrecursive(list, path, name) < 0)
+		return -1;
+
+	alt_path = dbox_get_alt_path(storage, path);
+	if (alt_path != NULL) {
+		if (dbox_delete_nonrecursive(list, alt_path, name) < 0)
+			return -1;
+	}
+	return 0;
 }
 
 static void dbox_notify_changes(struct mailbox *box)
--- a/src/lib-storage/index/dbox/dbox-storage.h	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-storage.h	Sat Nov 03 16:09:05 2007 +0200
@@ -28,6 +28,7 @@
 struct dbox_storage {
 	struct mail_storage storage;
 	union mailbox_list_module_context list_module_ctx;
+	const char *alt_dir;
 };
 
 struct dbox_mail_index_record {
@@ -52,7 +53,7 @@
 	ARRAY_DEFINE(open_files, struct dbox_file *);
 	unsigned int max_open_files;
 
-	const char *path;
+	const char *path, *alt_path;
 };
 
 struct dbox_transaction_context {
--- a/src/lib-storage/index/dbox/dbox-sync-file.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-sync-file.c	Sat Nov 03 16:09:05 2007 +0200
@@ -12,10 +12,29 @@
 
 static int dbox_sync_file_unlink(struct dbox_file *file)
 {
-	if (unlink(file->path) < 0) {
-		dbox_file_set_syscall_error(file, "unlink");
-		if (errno != ENOENT)
+	const char *path;
+	int i;
+
+	path = t_strdup_printf("%s/%s", file->mbox->path, file->fname);
+	for (i = 0;; i++) {
+		if (unlink(path) == 0)
+			break;
+
+		if (errno != ENOENT) {
+			mail_storage_set_critical(file->mbox->ibox.box.storage,
+				"unlink(%s) failed: %m", path);
 			return -1;
+		}
+		if (file->mbox->alt_path == NULL || i == 1) {
+			/* not found */
+			i_warning("dbox: File unexpectedly lost: %s/%s",
+				  file->mbox->path, file->fname);
+			break;
+		}
+
+		/* try the alternative path */
+		path = t_strdup_printf("%s/%s", file->mbox->alt_path,
+				       file->fname);
 	}
 	return 0;
 }
@@ -76,6 +95,7 @@
 	struct ostream *output;
 	uint32_t file_id, seq, uid;
 	uoff_t first_offset, offset, physical_size, metadata_offset;
+	const char *out_path;
 	unsigned int i, count;
 	bool expunged;
 	int ret;
@@ -143,12 +163,12 @@
 			(enum mail_flags)MAIL_INDEX_MAIL_FLAG_DIRTY);
 	}
 
+	out_path = out_file == NULL ? NULL :
+		dbox_file_get_path(out_file);
 	if (ret <= 0) {
 		if (out_file != NULL) {
-			if (unlink(out_file->path) < 0) {
-				i_error("unlink(%s) failed: %m",
-					out_file->path);
-			}
+			if (unlink(out_path) < 0)
+				i_error("unlink(%s) failed: %m", out_path);
 			o_stream_unref(&output);
 		}
 	} else if (out_file != NULL) {
@@ -183,6 +203,7 @@
 	struct ostream *output;
 	struct dbox_message_header dbox_msg_hdr;
 	struct dbox_mail_index_record rec;
+	const char *out_path;
 	uint32_t uid;
 	uoff_t size, append_offset;
 	int ret;
@@ -212,13 +233,14 @@
 	dbox_sync_update_metadata(ctx, out_file, NULL, seq);
 
 	/* copy the message */
+	out_path = dbox_file_get_path(out_file);
 	o_stream_cork(output);
 	if (o_stream_send(output, &dbox_msg_hdr, sizeof(dbox_msg_hdr)) < 0 ||
 	    o_stream_send_istream(output, input) < 0 ||
 	    dbox_file_metadata_write_to(out_file, output) < 0 ||
 	    o_stream_flush(output) < 0) {
 		mail_storage_set_critical(&ctx->mbox->storage->storage,
-			"write(%s) failed: %m", out_file->path);
+			"write(%s) failed: %m", out_path);
 		ret = -1;
 	} else {
 		dbox_file_finish_append(out_file);
--- a/src/lib-storage/index/dbox/dbox-sync-rebuild.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/dbox/dbox-sync-rebuild.c	Sat Nov 03 16:09:05 2007 +0200
@@ -69,10 +69,11 @@
 {
 	uint32_t seq, uid;
 	uoff_t metadata_offset, physical_size;
-	const char *fname;
+	const char *path;
 	bool expunged;
 	int ret;
 
+	path = dbox_file_get_path(file);
 	ret = dbox_file_seek_next(file, offset, &uid, &physical_size);
 	if (ret <= 0) {
 		if (ret < 0)
@@ -83,19 +84,17 @@
 			return 0;
 		}
 
-		i_warning("%s: Ignoring broken file (header)", file->path);
+		i_warning("%s: Ignoring broken file (header)", path);
 		return 0;
 	}
 	if ((file->file_id & DBOX_FILE_ID_FLAG_UID) != 0 &&
 	    uid != (file->file_id & ~DBOX_FILE_ID_FLAG_UID)) {
-		i_warning("%s: Header contains wrong UID %u", file->path, uid);
+		i_warning("%s: Header contains wrong UID %u", 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,
+		if (!maildir_uidlist_get_uid(ctx->maildir_uidlist, file->fname,
 					     &uid)) {
 			/* FIXME: not in uidlist, give it an uid */
 			return 0;
@@ -110,7 +109,7 @@
 	if (ret <= 0) {
 		if (ret < 0)
 			return -1;
-		i_warning("%s: Ignoring broken file (metadata)", file->path);
+		i_warning("%s: Ignoring broken file (metadata)", path);
 		return 0;
 	}
 	if (!expunged) {
--- a/src/lib-storage/index/maildir/maildir-storage.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Sat Nov 03 16:09:05 2007 +0200
@@ -108,7 +108,7 @@
 	} else {
 		if (debug)
 			i_info("maildir: data=%s", data);
-		if (mailbox_list_settings_parse(data, list_set, layout_r,
+		if (mailbox_list_settings_parse(data, list_set, layout_r, NULL,
 						error_r) < 0)
 			return -1;
 	}
--- a/src/lib-storage/index/mbox/mbox-storage.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Sat Nov 03 16:09:05 2007 +0200
@@ -316,7 +316,8 @@
 			}
 		} else {
 			if (mailbox_list_settings_parse(data, list_set,
-							layout_r, error_r) < 0)
+							layout_r, NULL,
+							error_r) < 0)
 				return -1;
 		}
 	}
--- a/src/lib-storage/mailbox-list-private.h	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/mailbox-list-private.h	Sat Nov 03 16:09:05 2007 +0200
@@ -98,7 +98,8 @@
 
 int mailbox_list_settings_parse(const char *data,
 				struct mailbox_list_settings *set,
-				const char **layout, const char **error_r);
+				const char **layout, const char **alt_dir_r,
+				const char **error_r);
 
 int mailbox_list_delete_index_control(struct mailbox_list *list,
 				      const char *name);
--- a/src/lib-storage/mailbox-list.c	Sat Nov 03 14:55:14 2007 +0200
+++ b/src/lib-storage/mailbox-list.c	Sat Nov 03 16:09:05 2007 +0200
@@ -109,13 +109,16 @@
 
 int mailbox_list_settings_parse(const char *data,
 				struct mailbox_list_settings *set,
-				const char **layout, const char **error_r)
+				const char **layout, const char **alt_dir_r,
+				const char **error_r)
 {
 	const char *const *tmp, *key, *value;
 
 	i_assert(*data != '\0');
 
 	*error_r = NULL;
+	if (alt_dir_r != NULL)
+		*alt_dir_r = NULL;
 
 	/* <root dir> */
 	tmp = t_strsplit(data, ":");
@@ -138,6 +141,8 @@
 			set->index_dir = fix_path(value);
 		else if (strcmp(key, "CONTROL") == 0)
 			set->control_dir = fix_path(value);
+		else if (strcmp(key, "ALT") == 0 && alt_dir_r != NULL)
+			*alt_dir_r = fix_path(value);
 		else if (strcmp(key, "LAYOUT") == 0)
 			*layout = value;
 		else if (strcmp(key, "SUBSCRIPTIONS") == 0)