changeset 817:86cf24da85f1 HEAD

Added :INDEX=<dir> for both Maildir and mbox to specify different location where to store index files. This would allow keeping mailboxes accessible through NFS but still keep the indexes in fast local disk. Did also some other related cleanups and minor fixes.
author Timo Sirainen <tss@iki.fi>
date Fri, 20 Dec 2002 09:53:51 +0200
parents ae9b55f7164e
children 3d8767d31fe2
files doc/mail-storages.txt dovecot-example.conf src/lib-index/mail-index.h src/lib-index/maildir/maildir-index.c src/lib-index/maildir/maildir-index.h src/lib-index/maildir/maildir-open.c src/lib-index/maildir/maildir-rebuild.c src/lib-index/maildir/maildir-sync.c src/lib-index/mbox/mbox-append.c src/lib-index/mbox/mbox-index.c src/lib-index/mbox/mbox-lock.c src/lib-index/mbox/mbox-rebuild.c src/lib-index/mbox/mbox-rewrite.c src/lib-index/mbox/mbox-sync-full.c src/lib-index/mbox/mbox-sync.c src/lib-storage/index/maildir/maildir-copy.c src/lib-storage/index/maildir/maildir-expunge.c src/lib-storage/index/maildir/maildir-save.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/mbox/mbox-expunge.c src/lib-storage/index/mbox/mbox-list.c src/lib-storage/index/mbox/mbox-save.c src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/mail-storage.h src/lib/unlink-directory.c
diffstat 25 files changed, 247 insertions(+), 179 deletions(-) [+]
line wrap: on
line diff
--- a/doc/mail-storages.txt	Fri Dec 20 09:37:37 2002 +0200
+++ b/doc/mail-storages.txt	Fri Dec 20 09:53:51 2002 +0200
@@ -29,6 +29,31 @@
 MAIL environment for mbox is: <root folder>|<INBOX path>[:INBOX=<path>].
 For example ~/Mail:INBOX=/var/mail/username.
 
+indexes
+-------
+
+It's possible to specify different location for index files. This could be
+wanted if mailboxes are accessed via remote filesystem (eg. NFS). Indexes
+are mmap()ed and fcntl() locked, so they may not work too well with NFS, or
+at least they'll work a bit slowly.
+
+Indexes don't contain anything really important in them, so it doesn't
+matter if they're lost or are out of sync. Dovecot just rebuilds or
+refreshes them. This would allow creating a cluster where each computer
+keeps the indexes locally while accessing the actual mailboxes via NFS. To
+avoid unnecessarily rebuilding indexes all the time, the users should be
+redirected to their primary server whenever possible.
+
+Currently the only annoying thing with recreating indexes is that the
+message UIDs will change, which may cause more traffic to clients, and
+depending on client software may cause loss of some message-specific
+settings. This will be fixed later by storing the UIDs permanently to
+mailboxes.
+
+Adding :INDEX=<dir> to MAIL environment overrides the default location. The
+given directory must exist.
+
+
 Detecting what to use
 ---------------------
 
--- a/dovecot-example.conf	Fri Dec 20 09:37:37 2002 +0200
+++ b/dovecot-example.conf	Fri Dec 20 09:53:51 2002 +0200
@@ -154,7 +154,7 @@
 #
 #   maildir:/var/mail/%1u/%u/Maildir
 #   mbox:~/mail/:INBOX=/var/mail/%u
-#   mbox:/var/mail/%d/%n/
+#   mbox:/var/mail/%d/%n/:INDEX=/var/indexes/%d/%n
 #
 #default_mail_env = 
 
--- a/src/lib-index/mail-index.h	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/mail-index.h	Fri Dec 20 09:53:51 2002 +0200
@@ -355,11 +355,11 @@
 
 	char *dir; /* directory where to place the index files */
 	char *filepath; /* index file path */
+	char *mailbox_path; /* file/directory for mailbox location */
 	MailDataField default_cache_fields, never_cache_fields;
 	unsigned int indexid;
 	unsigned int sync_id;
 
-	char *mbox_path; /* mbox-specific path to the actual mbox file */
 	int mbox_fd;
 	IStream *mbox_stream;
 	MailLockType mbox_lock_type;
--- a/src/lib-index/maildir/maildir-index.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/maildir/maildir-index.c	Fri Dec 20 09:53:51 2002 +0200
@@ -133,15 +133,17 @@
 	return flags_str->str;
 }
 
-MailIndex *maildir_index_alloc(const char *dir)
+MailIndex *maildir_index_alloc(const char *dir, const char *maildir)
 {
 	MailIndex *index;
 
 	i_assert(dir != NULL);
+	i_assert(maildir != NULL);
 
 	index = i_new(MailIndex, 1);
 	memcpy(index, &maildir_index, sizeof(MailIndex));
 
+	index->mailbox_path = i_strdup(maildir);
 	mail_index_init(index, dir);
 	return index;
 }
@@ -150,6 +152,7 @@
 {
 	mail_index_close(index);
 	i_free(index->dir);
+	i_free(index->mailbox_path);
 	i_free(index);
 }
 
@@ -199,8 +202,10 @@
 	new_fname = maildir_filename_set_flags(old_fname, flags);
 
 	if (strcmp(old_fname, new_fname) != 0) {
-		old_path = t_strconcat(index->dir, "/cur/", old_fname, NULL);
-		new_path = t_strconcat(index->dir, "/cur/", new_fname, NULL);
+		old_path = t_strconcat(index->mailbox_path,
+				       "/cur/", old_fname, NULL);
+		new_path = t_strconcat(index->mailbox_path,
+				       "/cur/", new_fname, NULL);
 
 		/* minor problem: new_path is overwritten if it exists.. */
 		if (rename(old_path, new_path) < 0) {
--- a/src/lib-index/maildir/maildir-index.h	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/maildir/maildir-index.h	Fri Dec 20 09:53:51 2002 +0200
@@ -6,7 +6,7 @@
 /* ":2,DFRST" - leave the 2 extra for other clients' additions */
 #define MAILDIR_LOCATION_EXTRA_SPACE 10
 
-MailIndex *maildir_index_alloc(const char *dir);
+MailIndex *maildir_index_alloc(const char *dir, const char *maildir);
 
 MailFlags maildir_filename_get_flags(const char *fname,
 				     MailFlags default_flags);
--- a/src/lib-index/maildir/maildir-open.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/maildir/maildir-open.c	Fri Dec 20 09:53:51 2002 +0200
@@ -32,7 +32,7 @@
 		return NULL;
 	}
 
-	path = t_strconcat(index->dir, "/cur/", fname, NULL);
+	path = t_strconcat(index->mailbox_path, "/cur/", fname, NULL);
 	fd = open(path, O_RDONLY);
 	if (fd == -1) {
 		if (errno == ENOENT) {
--- a/src/lib-index/maildir/maildir-rebuild.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/maildir/maildir-rebuild.c	Fri Dec 20 09:53:51 2002 +0200
@@ -39,12 +39,12 @@
 		return FALSE;
 
 	/* rebuild cur/ directory */
-	cur_dir = t_strconcat(index->dir, "/cur", NULL);
+	cur_dir = t_strconcat(index->mailbox_path, "/cur", NULL);
 	if (!maildir_index_build_dir(index, cur_dir, NULL))
 		return FALSE;
 
 	/* also see if there's new mail */
-	new_dir = t_strconcat(index->dir, "/new", NULL);
+	new_dir = t_strconcat(index->mailbox_path, "/new", NULL);
 	if (!maildir_index_build_dir(index, new_dir, cur_dir))
 		return FALSE;
 
--- a/src/lib-index/maildir/maildir-sync.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/maildir/maildir-sync.c	Fri Dec 20 09:53:51 2002 +0200
@@ -284,7 +284,7 @@
 	/* cur/ and new/ directories can have new mail - sync the cur/ first
 	   so it'll be a bit bit faster since we haven't yet added the new
 	   mail. */
-        cur_dir = t_strconcat(index->dir, "/cur", NULL);
+        cur_dir = t_strconcat(index->mailbox_path, "/cur", NULL);
 	if (stat(cur_dir, &std) < 0)
 		return index_file_set_syscall_error(index, cur_dir, "stat()");
 
@@ -295,7 +295,7 @@
 	}
 
 	/* move mail from new/ to cur/ */
-	new_dir = t_strconcat(index->dir, "/new", NULL);
+	new_dir = t_strconcat(index->mailbox_path, "/new", NULL);
 	if (stat(new_dir, &std) < 0)
 		return index_file_set_syscall_error(index, new_dir, "stat()");
 
--- a/src/lib-index/mbox/mbox-append.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/mbox/mbox-append.c	Fri Dec 20 09:53:51 2002 +0200
@@ -38,7 +38,7 @@
 		   b) not a From-line */
 		index_set_error(index, "Error indexing mbox file %s: "
 				"From-line not found where expected",
-				index->mbox_path);
+				index->mailbox_path);
 		index->set_flags |= MAIL_INDEX_FLAG_FSCK;
 		return FALSE;
 	}
@@ -130,7 +130,7 @@
 				index_set_error(index,
 						"Error indexing mbox file %s: "
 						"LF not found where expected",
-						index->mbox_path);
+						index->mailbox_path);
 
 				index->set_flags |= MAIL_INDEX_FLAG_FSCK;
 				return FALSE;
--- a/src/lib-index/mbox/mbox-index.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/mbox/mbox-index.c	Fri Dec 20 09:53:51 2002 +0200
@@ -24,7 +24,7 @@
 	i_assert(function != NULL);
 
 	index_set_error(index, "%s failed with mbox file %s: %m",
-			function, index->mbox_path);
+			function, index->mailbox_path);
 	return FALSE;
 }
 
@@ -35,7 +35,7 @@
 
 	i_assert(index->mbox_fd == -1);
 
-	fd = open(index->mbox_path, O_RDWR);
+	fd = open(index->mailbox_path, O_RDWR);
 	if (fd == -1) {
 		mbox_set_syscall_error(index, "open()");
 		return FALSE;
@@ -727,7 +727,7 @@
 	index->mbox_fd = -1;
 	index->mbox_sync_counter = (unsigned int)-1;
 
-	index->mbox_path = i_strdup(mbox_path);
+	index->mailbox_path = i_strdup(mbox_path);
 	mail_index_init(index, dir);
 	return index;
 }
@@ -736,8 +736,8 @@
 {
         mbox_file_close_fd(index);
 	mail_index_close(index);
-	i_free(index->mbox_path);
 	i_free(index->dir);
+	i_free(index->mailbox_path);
 	i_free(index);
 }
 
--- a/src/lib-index/mbox/mbox-lock.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/mbox/mbox-lock.c	Fri Dec 20 09:53:51 2002 +0200
@@ -87,7 +87,7 @@
 			index->mailbox_lock_timeout = TRUE;
 			index_set_error(index, "Timeout while waiting for "
 					"release of flock() lock for mbox file "
-					"%s", index->mbox_path);
+					"%s", index->mailbox_path);
 			return FALSE;
 		}
 
@@ -127,7 +127,7 @@
 			index->mailbox_lock_timeout = TRUE;
 			index_set_error(index, "Timeout while waiting for "
 					"release of fcntl() lock for mbox file "
-					"%s", index->mbox_path);
+					"%s", index->mailbox_path);
 			return FALSE;
 		}
 
@@ -163,7 +163,7 @@
 
 		if (stat(path, &st) == 0) {
 			/* see if there's been any changes in mbox */
-			if (stat(index->mbox_path, &st) < 0) {
+			if (stat(index->mailbox_path, &st) < 0) {
 				mbox_set_syscall_error(index, "stat()");
 				break;
 			}
@@ -332,14 +332,15 @@
 
 	/* make .lock file first to protect overwriting the file */
 	if (use_dotlock && index->mbox_dotlock_ino == 0) {
-		if (!mbox_lock_dotlock(index, index->mbox_path, max_wait_time,
+		if (!mbox_lock_dotlock(index, index->mailbox_path,
+				       max_wait_time,
 				       lock_type == MAIL_LOCK_SHARED &&
 				       !use_read_dotlock))
 			return FALSE;
 	}
 
 	/* now we need to have the file itself locked. open it if needed. */
-	if (stat(index->mbox_path, &st) < 0)
+	if (stat(index->mailbox_path, &st) < 0)
 		return mbox_set_syscall_error(index, "stat()");
 
 	if (st.st_dev != index->mbox_dev || st.st_ino != index->mbox_ino)
@@ -382,7 +383,7 @@
 	}
 
 	if (index->mbox_dotlock_ino != 0) {
-		if (!mbox_unlock_dotlock(index, index->mbox_path))
+		if (!mbox_unlock_dotlock(index, index->mailbox_path))
 			failed = TRUE;
 	}
 
--- a/src/lib-index/mbox/mbox-rebuild.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/mbox/mbox-rebuild.c	Fri Dec 20 09:53:51 2002 +0200
@@ -56,7 +56,7 @@
 		return FALSE;
 
 	/* update sync stamp */
-	if (stat(index->mbox_path, &st) < 0)
+	if (stat(index->mailbox_path, &st) < 0)
 		return mbox_set_syscall_error(index, "fstat()");
 
 	index->file_sync_stamp = st.st_mtime;
--- a/src/lib-index/mbox/mbox-rewrite.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/mbox/mbox-rewrite.c	Fri Dec 20 09:53:51 2002 +0200
@@ -61,13 +61,13 @@
 	i_stream_set_read_limit(input, end_offset);
 	if (o_stream_send_istream(output, input) < 0) {
 		index_set_error(index, "Error rewriting mbox file %s: %s",
-				index->mbox_path,
+				index->mailbox_path,
 				strerror(output->stream_errno));
 		failed = TRUE;
 	} else if (input->v_offset < end_offset) {
 		/* fsck should have noticed it.. */
 		index_set_error(index, "Error rewriting mbox file %s: "
-				"Unexpected end of file", index->mbox_path);
+				"Unexpected end of file", index->mailbox_path);
 		failed = TRUE;
 	} else {
 		failed = FALSE;
@@ -315,7 +315,7 @@
 	if (input->v_offset >= end_offset) {
 		/* fsck should have noticed it.. */
 		index_set_error(index, "Error rewriting mbox file %s: "
-				"Unexpected end of file", index->mbox_path);
+				"Unexpected end of file", index->mailbox_path);
 		return FALSE;
 	}
 
@@ -539,7 +539,7 @@
 
 	if (!dirty_found) {
 		index_set_error(index, "Expected dirty messages not found "
-				"from mbox file %s", index->mbox_path);
+				"from mbox file %s", index->mailbox_path);
 		failed = TRUE;
 	}
 
--- a/src/lib-index/mbox/mbox-sync-full.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/mbox/mbox-sync-full.c	Fri Dec 20 09:53:51 2002 +0200
@@ -205,7 +205,7 @@
 	if (i_stream_read_data(input, &data, &size, 5) > 0 &&
 	    strncmp((const char *) data, "From ", 5) != 0) {
 		index_set_error(index, "File isn't in mbox format: %s",
-				index->mbox_path);
+				index->mailbox_path);
 		return FALSE;
 	}
 
@@ -232,7 +232,7 @@
 				index_set_error(index,
 						"Error syncing mbox file %s: "
 						"LF not found where expected",
-						index->mbox_path);
+						index->mailbox_path);
 				return FALSE;
 			}
 		}
--- a/src/lib-index/mbox/mbox-sync.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-index/mbox/mbox-sync.c	Fri Dec 20 09:53:51 2002 +0200
@@ -93,14 +93,14 @@
 	}
 
 	count = 0;
-	while (stat(index->mbox_path, &st) < 0) {
+	while (stat(index->mailbox_path, &st) < 0) {
 		if (errno != ENOENT || ++count == 3)
 			return mbox_set_syscall_error(index, "stat()");
 
 		/* mbox was deleted by someone - happens with some MUAs
 		   when all mail is expunged. easiest way to deal with this
 		   is to recreate the file. */
-		fd = open(index->mbox_path, O_RDWR | O_CREAT | O_EXCL, 0660);
+		fd = open(index->mailbox_path, O_RDWR | O_CREAT | O_EXCL, 0660);
 		if (fd != -1)
 			(void)close(fd);
 		else if (errno != EEXIST)
--- a/src/lib-storage/index/maildir/maildir-copy.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/index/maildir/maildir-copy.c	Fri Dec 20 09:53:51 2002 +0200
@@ -32,7 +32,8 @@
 
 	/* link the file */
 	fname = index->lookup_field(index, rec, DATA_FIELD_LOCATION);
-	if (str_ppath(src, sizeof(src), index->dir, "cur/", fname) < 0) {
+	if (str_ppath(src, sizeof(src),
+		      index->mailbox_path, "cur/", fname) < 0) {
 		mail_storage_set_critical(ctx->storage, "Filename too long: %s",
 					  fname);
 		return FALSE;
@@ -41,7 +42,7 @@
 	fname = maildir_filename_set_flags(maildir_generate_tmp_filename(),
 					   flags);
 	if (str_ppath(dest, sizeof(dest),
-		      ctx->dest->index->dir, "new/", fname) < 0) {
+		      ctx->dest->index->mailbox_path, "new/", fname) < 0) {
 		mail_storage_set_critical(ctx->storage, "Filename too long: %s",
 					  fname);
 		return FALSE;
--- a/src/lib-storage/index/maildir/maildir-expunge.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/index/maildir/maildir-expunge.c	Fri Dec 20 09:53:51 2002 +0200
@@ -14,7 +14,7 @@
 					  DATA_FIELD_LOCATION);
 	if (fname != NULL) {
 		if (str_ppath(path, sizeof(path),
-			      ibox->index->dir, "cur/", fname) < 0) {
+			      ibox->index->mailbox_path, "cur/", fname) < 0) {
 			mail_storage_set_critical(ibox->box.storage,
 						  "Filename too long: %s",
 						  fname);
--- a/src/lib-storage/index/maildir/maildir-save.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/index/maildir/maildir-save.c	Fri Dec 20 09:53:51 2002 +0200
@@ -98,7 +98,7 @@
 	t_push();
 
 	/* create the file into tmp/ directory */
-	tmpdir = t_strconcat(ibox->index->dir, "/tmp", NULL);
+	tmpdir = t_strconcat(ibox->index->mailbox_path, "/tmp", NULL);
 	fname = maildir_read_into_tmp(box->storage, tmpdir, data, data_size);
 	if (fname == NULL) {
 		t_pop();
@@ -107,7 +107,7 @@
 	tmp_path = t_strconcat(tmpdir, "/", fname, NULL);
 
 	fname = maildir_filename_set_flags(fname, flags);
-	new_path = t_strconcat(ibox->index->dir, "/new/", fname, NULL);
+	new_path = t_strconcat(ibox->index->mailbox_path, "/new/", fname, NULL);
 
 	/* set the internal_date by modifying mtime */
 	buf.actime = ioloop_time;
--- a/src/lib-storage/index/maildir/maildir-storage.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Fri Dec 20 09:53:51 2002 +0200
@@ -27,31 +27,49 @@
 static MailStorage *maildir_create(const char *data, const char *user)
 {
 	MailStorage *storage;
-	const char *home, *path;
+	const char *home, *path, *root_dir, *index_dir, *p;
+
+	root_dir = index_dir = NULL;
 
 	if (data == NULL || *data == '\0') {
 		/* we'll need to figure out the maildir location ourself.
 		   it's either root dir if we've already chroot()ed, or
 		   $HOME/Maildir otherwise */
 		if (access("/cur", R_OK|W_OK|X_OK) == 0)
-			data = "/";
+			root_dir = "/";
 		else {
 			home = getenv("HOME");
 			if (home != NULL) {
 				path = t_strconcat(home, "/Maildir", NULL);
 				if (access(path, R_OK|W_OK|X_OK) == 0)
-					data = path;
+					root_dir = path;
 			}
 		}
+	} else {
+		/* <Maildir> [:INDEX=<dir>] */
+		p = strchr(data, ':');
+		if (p == NULL)
+			root_dir = data;
+		else {
+			root_dir = t_strdup_until(data, p);
+
+			p++;
+			if (strncmp(p, "INDEX=", 6) == 0)
+				index_dir = t_strcut(p+6, ':');
+		}
 	}
 
-	if (data == NULL)
+	if (root_dir == NULL)
 		return NULL;
 
+	if (index_dir == NULL)
+		index_dir = root_dir;
+
 	storage = i_new(MailStorage, 1);
 	memcpy(storage, &maildir_storage, sizeof(MailStorage));
 
-	storage->dir = i_strdup(data);
+	storage->dir = i_strdup(root_dir);
+	storage->index_dir = i_strdup(index_dir);
 	storage->user = i_strdup(user);
 	storage->callbacks = i_new(MailStorageCallbacks, 1);
 	return storage;
@@ -59,9 +77,11 @@
 
 static void maildir_free(MailStorage *storage)
 {
+	i_free(storage->dir);
+	i_free(storage->index_dir);
+	i_free(storage->user);
+	i_free(storage->error);
 	i_free(storage->callbacks);
-	i_free(storage->dir);
-	i_free(storage->user);
 	i_free(storage);
 }
 
@@ -81,18 +101,21 @@
 		strchr(name, '*') == NULL && strchr(name, '%') == NULL;
 }
 
+static const char *maildir_get_path(MailStorage *storage, const char *name)
+{
+	return t_strconcat(storage->dir, "/.", name, NULL);
+}
+
 /* create or fix maildir, ignore if it already exists */
 static int create_maildir(const char *dir, int verify)
 {
-	const char **tmp;
-	char path[PATH_MAX];
+	const char **tmp, *path;
 
 	if (mkdir(dir, CREATE_MODE) == -1 && (errno != EEXIST || !verify))
 		return FALSE;
 
 	for (tmp = maildirs; *tmp != NULL; tmp++) {
-		if (str_path(path, sizeof(path), dir, *tmp) < 0)
-			return FALSE;
+		path = t_strconcat(dir, "/", *tmp, NULL);
 
 		if (mkdir(path, CREATE_MODE) == -1 &&
 		    (errno != EEXIST || !verify))
@@ -102,38 +125,42 @@
 	return TRUE;
 }
 
-static int verify_inbox(MailStorage *storage, const char *dir)
+static int create_index_dir(MailStorage *storage, const char *name)
 {
-	const char **tmp;
-	char src[PATH_MAX], dest[PATH_MAX];
+	const char *dir;
 
-	/* first make sure the cur/ new/ and tmp/ dirs exist in root dir */
-	(void)create_maildir(dir, TRUE);
+	if (strcmp(storage->index_dir, storage->dir) == 0)
+		return TRUE;
 
-	/* create the .INBOX directory */
-	if (str_path(dest, sizeof(dest), dir, ".INBOX") < 0) {
-		mail_storage_set_critical(storage, "Path too long: %s", dir);
+	dir = t_strconcat(storage->index_dir, "/.", name, NULL);
+	if (mkdir(dir, CREATE_MODE) == -1 && errno != EEXIST) {
+		mail_storage_set_critical(storage,
+					  "Can't create directory %s: %m", dir);
 		return FALSE;
 	}
 
-	if (mkdir(dest, CREATE_MODE) == -1 && errno != EEXIST) {
+	return TRUE;
+}
+
+static int verify_inbox(MailStorage *storage)
+{
+	const char **tmp, *src, *dest, *inbox;
+
+	/* first make sure the cur/ new/ and tmp/ dirs exist in root dir */
+	(void)create_maildir(storage->dir, TRUE);
+
+	/* create the .INBOX directory */
+	inbox = maildir_get_path(storage, "INBOX");
+	if (mkdir(inbox, CREATE_MODE) == -1 && errno != EEXIST) {
 		mail_storage_set_critical(storage, "Can't create directory "
-					  "%s: %m", dest);
+					  "%s: %m", inbox);
 		return FALSE;
 	}
 
 	/* then symlink the cur/ new/ and tmp/ into the .INBOX/ directory */
 	for (tmp = maildirs; *tmp != NULL; tmp++) {
-		if (str_path(src, sizeof(src), "..", *tmp) < 0) {
-			mail_storage_set_critical(storage, "Path too long: %s",
-						  *tmp);
-			return FALSE;
-		}
-		if (str_ppath(dest, sizeof(dest), dir, ".INBOX/", *tmp) < 0) {
-			mail_storage_set_critical(storage, "Path too long: %s",
-						  dir);
-			return FALSE;
-		}
+		src = t_strconcat("../", *tmp, NULL);
+		dest = t_strconcat(inbox, "/", *tmp, NULL);
 
 		if (symlink(src, dest) == -1 && errno != EEXIST) {
 			mail_storage_set_critical(storage, "symlink(%s, %s) "
@@ -142,7 +169,8 @@
 		}
 	}
 
-	return TRUE;
+	/* make sure the index directories exist */
+	return create_index_dir(storage, "INBOX");
 }
 
 static Mailbox *maildir_open(MailStorage *storage, const char *name,
@@ -150,13 +178,14 @@
 {
 	IndexMailbox *ibox;
 	MailIndex *index;
-	const char *path;
+	const char *path, *index_dir;
 
 	path = t_strconcat(storage->dir, "/.", name, NULL);
+	index_dir = t_strconcat(storage->index_dir, "/.", name, NULL);
 
-	index = index_storage_lookup_ref(path);
+	index = index_storage_lookup_ref(index_dir);
 	if (index == NULL) {
-		index = maildir_index_alloc(path);
+		index = maildir_index_alloc(index_dir, path);
 		index_storage_add(index);
 	}
 
@@ -184,14 +213,14 @@
 static Mailbox *maildir_open_mailbox(MailStorage *storage, const char *name,
 				     int readonly, int fast)
 {
+	const char *path;
 	struct stat st;
-	char path[PATH_MAX];
 
 	mail_storage_clear_error(storage);
 
 	name = inbox_fix_case(storage, name);
 	if (strcmp(name, "INBOX") == 0) {
-		if (!verify_inbox(storage, storage->dir))
+		if (!verify_inbox(storage))
 			return NULL;
 		return maildir_open(storage, "INBOX", readonly, fast);
 	}
@@ -201,16 +230,14 @@
 		return FALSE;
 	}
 
-	if (str_ppath(path, sizeof(path), storage->dir, ".", name) < 0) {
-		mail_storage_set_critical(storage, "Mailbox name too long: %s",
-					  name);
-		return FALSE;
-	}
-
+	path = maildir_get_path(storage, name);
 	if (stat(path, &st) == 0) {
 		/* exists - make sure the required directories are also there */
 		(void)create_maildir(path, TRUE);
 
+		/* make sure the index directories exist */
+		(void)create_index_dir(storage, name);
+
 		return maildir_open(storage, name, readonly, fast);
 	} else if (errno == ENOENT) {
 		mail_storage_set_error(storage, "Mailbox doesn't exist: %s",
@@ -225,7 +252,7 @@
 
 static int maildir_create_mailbox(MailStorage *storage, const char *name)
 {
-	char path[PATH_MAX];
+	const char *path;
 
 	mail_storage_clear_error(storage);
 
@@ -235,12 +262,7 @@
 		return FALSE;
 	}
 
-	if (str_ppath(path, sizeof(path), storage->dir, ".", name) < 0) {
-		mail_storage_set_critical(storage, "Mailbox name too long: %s",
-					  name);
-		return FALSE;
-	}
-
+	path = maildir_get_path(storage, name);
 	if (create_maildir(path, FALSE))
 		return TRUE;
 	else if (errno == EEXIST) {
@@ -256,7 +278,7 @@
 static int maildir_delete_mailbox(MailStorage *storage, const char *name)
 {
 	struct stat st;
-	char src[PATH_MAX], dest[PATH_MAX];
+	const char *src, *dest, *index_dir;
 	int count;
 
 	mail_storage_clear_error(storage);
@@ -273,27 +295,28 @@
 	}
 
 	/* rename the .maildir into ..maildir which marks it as being
-	   deleted. this way we never see partially deleted maildirs. */
-	if (str_ppath(src, sizeof(src), storage->dir, ".", name) < 0) {
-		mail_storage_set_critical(storage, "Mailbox name too long: %s",
-					  name);
-		return FALSE;
-	}
-
-	if (str_ppath(dest, sizeof(dest), storage->dir, "..", name) < 0) {
-		mail_storage_set_critical(storage, "Mailbox name too long: %s",
-					  name);
-		return FALSE;
-	}
-
+	   deleted. delete indexes before the actual maildir. this way we
+	   never see partially deleted mailboxes. */
+	src = maildir_get_path(storage, name);
+	dest = t_strconcat(storage->dir, "/..", name, NULL);
 	if (stat(src, &st) != 0 && errno == ENOENT) {
 		mail_storage_set_error(storage, "Mailbox doesn't exist: %s",
 				       name);
 		return FALSE;
 	}
 
+	if (strcmp(storage->index_dir, storage->dir) != 0) {
+		index_dir = t_strconcat(storage->index_dir, "/.", name, NULL);
+		if (!unlink_directory(index_dir)) {
+			mail_storage_set_critical(storage,
+						  "unlink_directory(%s) "
+						  "failed: %m", index_dir);
+			return FALSE;
+		}
+	}
+
 	count = 0;
-	while (rename(src, dest) == -1 && count < 2) {
+	while (rename(src, dest) < 0 && count < 2) {
 		if (errno != EEXIST) {
 			mail_storage_set_critical(storage,
 						  "rename(%s, %s) failed: %m",
@@ -316,24 +339,19 @@
 					  "failed: %m", dest);
 		return FALSE;
 	}
+
 	return TRUE;
 }
 
 static int move_inbox_data(MailStorage *storage, const char *newdir)
 {
-	const char **tmp;
-	char oldpath[PATH_MAX], newpath[PATH_MAX];
+	const char **tmp, *oldpath, *newpath;
 
 	/* newpath points to the destination folder directory, which contains
 	   symlinks to real INBOX directories. unlink() the symlinks and
 	   move the real cur/ directory here. */
 	for (tmp = maildirs; *tmp != NULL; tmp++) {
-		if (str_path(newpath, sizeof(newpath), newdir, *tmp) < 0) {
-			mail_storage_set_critical(storage, "Path too long: %s",
-						  newdir);
-			return FALSE;
-		}
-
+		newpath = t_strconcat(newdir, "/", *tmp, NULL);
 		if (unlink(newpath) == -1 && errno != EEXIST) {
 			mail_storage_set_critical(storage,
 						  "unlink(%s) failed: %m",
@@ -342,15 +360,8 @@
 		}
 	}
 
-	if (str_path(oldpath, sizeof(oldpath), storage->dir, "cur") < 0) {
-		mail_storage_set_critical(storage, "Path too long: %s",
-					  storage->dir);
-		return FALSE;
-	}
-	if (str_path(newpath, sizeof(newpath), newdir, "cur") < 0) {
-		mail_storage_set_critical(storage, "Path too long: %s", newdir);
-		return FALSE;
-	}
+	oldpath = t_strconcat(storage->dir, "/cur", NULL);
+	newpath = t_strconcat(newdir, "/cur", NULL);
 
 	if (rename(oldpath, newpath) != 0) {
 		mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
@@ -363,26 +374,39 @@
 	return TRUE;
 }
 
+static int rename_indexes(MailStorage *storage,
+			  const char *oldname, const char *newname)
+{
+	const char *oldpath, *newpath;
+
+	if (strcmp(storage->index_dir, storage->dir) == 0)
+		return TRUE;
+
+	/* Rename it's index. */
+	oldpath = t_strconcat(storage->index_dir, "/.", oldname, NULL);
+	newpath = t_strconcat(storage->index_dir, "/.", newname, NULL);
+
+	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
+		mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
+					  oldpath, newpath);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static void rename_subfolder(MailStorage *storage, const char *name,
 			     MailboxFlags flags __attr_unused__, void *context)
 {
 	RenameContext *ctx = context;
-	char oldpath[PATH_MAX], newpath[PATH_MAX];
+	const char *newname, *oldpath, *newpath;
 
 	i_assert(ctx->oldnamelen <= strlen(name));
 
-	if (str_ppath(oldpath, sizeof(oldpath), storage->dir, ".", name) < 0) {
-		mail_storage_set_critical(storage, "Mailbox name too long: %s",
-					  name);
-		return;
-	}
+	newname = t_strconcat(ctx->newname, ".", name + ctx->oldnamelen, NULL);
 
-	if (i_snprintf(newpath, sizeof(newpath), "%s/.%s.%s", storage->dir,
-		       ctx->newname, name + ctx->oldnamelen) < 0) {
-		mail_storage_set_critical(storage, "Mailbox name too long: %s",
-					  newpath);
-		return;
-	}
+	oldpath = maildir_get_path(storage, name);
+	newpath = maildir_get_path(storage, newname);
 
 	/* FIXME: it's possible to merge two folders if either one of them
 	   doesn't have existing root folder. We could check this but I'm not
@@ -397,13 +421,15 @@
 		mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
 					  oldpath, newpath);
 	}
+
+	(void)rename_indexes(storage, name, newname);
 }
 
 static int maildir_rename_mailbox(MailStorage *storage, const char *oldname,
 				  const char *newname)
 {
 	RenameContext ctx;
-	char oldpath[PATH_MAX], newpath[PATH_MAX];
+	const char *oldpath, *newpath;
 	int ret;
 
 	mail_storage_clear_error(storage);
@@ -421,25 +447,17 @@
 
 	   NOTE: it's possible to rename a nonexisting folder which has
 	   subfolders. In that case we should ignore the rename() error. */
-	if (str_ppath(oldpath, sizeof(oldpath),
-		      storage->dir, ".", oldname) < 0) {
-		mail_storage_set_critical(storage, "Mailbox name too long: %s",
-					  oldname);
-		return FALSE;
-	}
-
-	if (str_ppath(newpath, sizeof(newpath),
-		      storage->dir, ".", newname) < 0) {
-		mail_storage_set_critical(storage, "Mailbox name too long: %s",
-					  newname);
-		return FALSE;
-	}
+	oldpath = maildir_get_path(storage, oldname);
+	newpath = maildir_get_path(storage, newname);
 
 	ret = rename(oldpath, newpath);
 	if (ret == 0 || (errno == ENOENT && strcmp(oldname, "INBOX") != 0)) {
 		if (strcmp(oldname, "INBOX") == 0)
 			return move_inbox_data(storage, newpath);
 
+		if (!rename_indexes(storage, oldname, newname))
+			return FALSE;
+
 		ctx.found = ret == 0;
 		ctx.oldnamelen = strlen(oldname)+1;
 		ctx.newname = newname;
@@ -472,7 +490,7 @@
 					   MailboxNameStatus *status)
 {
 	struct stat st;
-	char path[PATH_MAX];
+	const char *path;
 
 	mail_storage_clear_error(storage);
 
@@ -482,8 +500,8 @@
 		return TRUE;
 	}
 
-	if (str_ppath(path, sizeof(path), storage->dir, ".", name) == 0 &&
-	    stat(path, &st) == 0) {
+	path = maildir_get_path(storage, name);
+	if (stat(path, &st) == 0) {
 		*status = MAILBOX_NAME_EXISTS;
 		return TRUE;
 	} else if (errno == ENOENT) {
@@ -519,6 +537,7 @@
 	NULL,
 	NULL,
 	NULL,
+	NULL,
 	NULL, NULL
 };
 
--- a/src/lib-storage/index/mbox/mbox-expunge.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-expunge.c	Fri Dec 20 09:53:51 2002 +0200
@@ -142,7 +142,7 @@
 	if (ftruncate(ibox->index->mbox_fd, (off_t)output->offset) < 0) {
 		mail_storage_set_error(ibox->box.storage, "ftruncate() failed "
 				       "for mbox file %s: %m",
-				       ibox->index->mbox_path);
+				       ibox->index->mailbox_path);
 		failed = TRUE;
 	}
 
--- a/src/lib-storage/index/mbox/mbox-list.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-list.c	Fri Dec 20 09:53:51 2002 +0200
@@ -110,7 +110,9 @@
 
 		if (S_ISDIR(st.st_mode)) {
 			/* subdirectory, scan it too */
+			t_push();
 			func(storage, listpath, MAILBOX_NOSELECT, context);
+			t_pop();
 
 			if (!mbox_find_path(storage, glob, func,
 					    context, listpath)) {
@@ -119,7 +121,9 @@
 			}
 		} else if (match > 0 &&
 			   strcmp(fullpath, storage->inbox_file) != 0) {
+			t_push();
 			func(storage, listpath, MAILBOX_NOINFERIORS, context);
+			t_pop();
 		}
 	}
 
--- a/src/lib-storage/index/mbox/mbox-save.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-save.c	Fri Dec 20 09:53:51 2002 +0200
@@ -183,7 +183,7 @@
 		return FALSE;
 
 	index = ibox->index;
-	mbox_path = index->mbox_path;
+	mbox_path = index->mailbox_path;
 	if (!mbox_seek_to_end(box->storage, index->mbox_fd, mbox_path, &pos))
 		failed = TRUE;
 	else {
--- a/src/lib-storage/index/mbox/mbox-storage.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Fri Dec 20 09:53:51 2002 +0200
@@ -72,10 +72,10 @@
 static MailStorage *mbox_create(const char *data, const char *user)
 {
 	MailStorage *storage;
-	const char *root_dir, *inbox_file, *p;
+	const char *root_dir, *inbox_file, *index_dir, *p;
 	struct stat st;
 
-	root_dir = inbox_file = NULL;
+	root_dir = inbox_file = index_dir = NULL;
 
 	if (data == NULL || *data == '\0') {
 		/* we'll need to figure out the mail location ourself.
@@ -83,8 +83,8 @@
 		   either $HOME/mail or $HOME/Mail */
 		root_dir = get_root_dir();
 	} else {
-		/* <root folder> | <INBOX path> [:INBOX=<path>]
-		   [:<reserved for future>] */
+		/* <root folder> | <INBOX path>
+		   [:INBOX=<path>] [:INDEX=<dir>] */
 		p = strchr(data, ':');
 		if (p == NULL) {
 			if (stat(data, &st) == 0 && S_ISDIR(st.st_mode))
@@ -95,8 +95,14 @@
 			}
 		} else {
 			root_dir = t_strdup_until(data, p);
-			if (strncmp(p+1, "INBOX=", 6) == 0)
-				inbox_file = t_strcut(p+7, ':');
+			do {
+				p++;
+				if (strncmp(p, "INBOX=", 6) == 0)
+					inbox_file = t_strcut(p+6, ':');
+				else if (strncmp(p, "INDEX=", 6) == 0)
+					index_dir = t_strcut(p+6, ':');
+				p = strchr(p, ':');
+			} while (p != NULL);
 		}
 	}
 
@@ -105,12 +111,15 @@
 
 	if (inbox_file == NULL)
 		inbox_file = t_strconcat(root_dir, "/inbox", NULL);
+	if (index_dir == NULL)
+		index_dir = root_dir;
 
 	storage = i_new(MailStorage, 1);
 	memcpy(storage, &mbox_storage, sizeof(MailStorage));
 
 	storage->dir = i_strdup(root_dir);
 	storage->inbox_file = i_strdup(inbox_file);
+	storage->index_dir = i_strdup(index_dir);
 	storage->user = i_strdup(user);
 	storage->callbacks = i_new(MailStorageCallbacks, 1);
 	return storage;
@@ -118,9 +127,12 @@
 
 static void mbox_free(MailStorage *storage)
 {
+	i_free(storage->dir);
+	i_free(storage->inbox_file);
+	i_free(storage->index_dir);
+	i_free(storage->user);
+	i_free(storage->error);
 	i_free(storage->callbacks);
-	i_free(storage->dir);
-	i_free(storage->user);
 	i_free(storage);
 }
 
@@ -149,24 +161,25 @@
 		mbox_is_valid_mask(name);
 }
 
-static const char *mbox_get_index_dir(const char *mbox_path)
+static const char *mbox_get_index_dir(MailStorage *storage, const char *name)
 {
-	const char *p, *rootpath;
+	const char *p;
 
-	p = strrchr(mbox_path, '/');
+	p = strrchr(name, '/');
 	if (p == NULL)
-		return t_strconcat(".imap/", mbox_path, NULL);
+		return t_strconcat(storage->index_dir, "/.imap/", name, NULL);
 	else {
-		rootpath = t_strdup_until(mbox_path, p);
-		return t_strconcat(rootpath, "/.imap/", p+1, NULL);
+		return t_strconcat(storage->index_dir, t_strdup_until(name, p),
+				   "/.imap/", p+1, NULL);
 	}
 }
 
-static int create_mbox_index_dirs(const char *mbox_path, int verify)
+static int create_mbox_index_dirs(MailStorage *storage, const char *name,
+				  int verify)
 {
 	const char *index_dir, *imap_dir;
 
-	index_dir = mbox_get_index_dir(mbox_path);
+	index_dir = mbox_get_index_dir(storage, name);
 	imap_dir = t_strdup_until(index_dir, strstr(index_dir, ".imap/") + 5);
 
 	if (mkdir(imap_dir, CREATE_MODE) == -1 && errno != EEXIST)
@@ -179,7 +192,6 @@
 
 static void verify_inbox(MailStorage *storage)
 {
-	const char *index_dir;
 	int fd;
 
 	/* make sure inbox file itself exists */
@@ -188,8 +200,7 @@
 		(void)close(fd);
 
 	/* make sure the index directories exist */
-	index_dir = t_strconcat(storage->dir, "/INBOX", NULL);
-	(void)create_mbox_index_dirs(index_dir, TRUE);
+	(void)create_mbox_index_dirs(storage, "INBOX", TRUE);
 }
 
 static const char *mbox_get_path(MailStorage *storage, const char *name)
@@ -212,14 +223,13 @@
 		   path = "<inbox_file>/INBOX"
 		   index_dir = "/mail/.imap/INBOX" */
 		path = storage->inbox_file;
-		index_dir = mbox_get_index_dir(t_strconcat(storage->dir,
-							   "/INBOX", NULL));
+		index_dir = mbox_get_index_dir(storage, "/INBOX");
 	} else {
 		/* name = "foo/bar"
 		   path = "/mail/foo/bar"
 		   index_dir = "/mail/foo/.imap/bar" */
 		path = mbox_get_path(storage, name);
-		index_dir = mbox_get_index_dir(path);
+		index_dir = mbox_get_index_dir(storage, name);
 	}
 
 	index = index_storage_lookup_ref(index_dir);
@@ -232,7 +242,7 @@
 				  name, readonly, fast);
 	if (ibox != NULL) {
 		ibox->expunge_locked = mbox_expunge_locked;
-		index_mailbox_check_add(ibox, index->mbox_path);
+		index_mailbox_check_add(ibox, index->mailbox_path);
 	}
 	return (Mailbox *) ibox;
 }
@@ -260,7 +270,7 @@
 	path = mbox_get_path(storage, name);
 	if (stat(path, &st) == 0) {
 		/* exists - make sure the required directories are also there */
-		(void)create_mbox_index_dirs(path, TRUE);
+		(void)create_mbox_index_dirs(storage, name, TRUE);
 
 		return mbox_open(storage, name, readonly, fast);
 	} else if (errno == ENOENT) {
@@ -351,7 +361,7 @@
 	}
 
 	/* next delete the index directory */
-	index_dir = mbox_get_index_dir(path);
+	index_dir = mbox_get_index_dir(storage, name);
 	if (!unlink_directory(index_dir) && errno != ENOENT) {
 		mail_storage_set_critical(storage, "unlink_directory(%s) "
 					  "failed: %m", index_dir);
@@ -394,8 +404,8 @@
 	}
 
 	/* we need to rename the index directory as well */
-	old_indexdir = mbox_get_index_dir(oldpath);
-	new_indexdir = mbox_get_index_dir(newpath);
+	old_indexdir = mbox_get_index_dir(storage, oldname);
+	new_indexdir = mbox_get_index_dir(storage, newname);
 	(void)rename(old_indexdir, new_indexdir);
 
 	return TRUE;
@@ -468,6 +478,7 @@
 	NULL,
 	NULL,
 	NULL,
+	NULL,
 	NULL, NULL
 };
 
--- a/src/lib-storage/mail-storage.h	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib-storage/mail-storage.h	Fri Dec 20 09:53:51 2002 +0200
@@ -116,6 +116,8 @@
 /* private: */
 	char *dir; /* root directory */
 	char *inbox_file; /* INBOX file for mbox */
+	char *index_dir;
+
 	char *user; /* name of user accessing the storage */
 	char *error;
 
--- a/src/lib/unlink-directory.c	Fri Dec 20 09:37:37 2002 +0200
+++ b/src/lib/unlink-directory.c	Fri Dec 20 09:53:51 2002 +0200
@@ -39,7 +39,7 @@
 
 	dirp = opendir(dir);
 	if (dirp == NULL)
-		return FALSE;
+		return errno == ENOENT;
 
 	while ((d = readdir(dirp)) != NULL) {
 		if (d->d_name[0] == '.' &&
@@ -69,7 +69,7 @@
 
 	(void)closedir(dirp);
 
-	if (rmdir(dir) == -1)
+	if (rmdir(dir) == -1 && errno != ENOENT)
 		return FALSE;
 	return TRUE;
 }