changeset 10653:5fe70b5da1b8 HEAD

lib-storage: Moved mailbox directory creation code to mailbox_list backend.
author Timo Sirainen <tss@iki.fi>
date Sun, 07 Feb 2010 01:50:49 +0200
parents bdb1ea37ccee
children 302a4f807276
files src/lib-storage/index/cydir/cydir-storage.c src/lib-storage/index/dbox-common/dbox-storage.c src/lib-storage/index/index-storage.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/index/shared/shared-list.c src/lib-storage/list/mailbox-list-fs.c src/lib-storage/list/mailbox-list-maildir.c src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage.c src/lib-storage/mailbox-list-private.h src/lib-storage/mailbox-list.h src/plugins/acl/acl-mailbox-list.c src/plugins/acl/acl-mailbox.c
diffstat 14 files changed, 402 insertions(+), 290 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/cydir/cydir-storage.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/index/cydir/cydir-storage.c	Sun Feb 07 01:50:49 2010 +0200
@@ -48,25 +48,6 @@
 		set->subscription_fname = CYDIR_SUBSCRIPTION_FILE_NAME;
 }
 
-static int create_cydir(struct mail_storage *storage, struct mailbox_list *list,
-			const char *path)
-{
-	const char *origin;
-	mode_t mode;
-	gid_t gid;
-
-	mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
-	if (mkdir_parents_chgrp(path, mode, gid, origin) < 0 &&
-	    errno != EEXIST) {
-		if (!mail_storage_set_error_from_errno(storage)) {
-			mail_storage_set_critical(storage,
-				"mkdir(%s) failed: %m", path);
-		}
-		return -1;
-	}
-	return 0;
-}
-
 static struct mailbox *
 cydir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
 		    const char *name, struct istream *input,
@@ -114,7 +95,8 @@
 		/* exists, open it */
 	} else if (errno == ENOENT && strcmp(box->name, "INBOX") == 0) {
 		/* INBOX always exists, create it */
-		if (create_cydir(box->storage, box->list, box->path) < 0)
+		if (box->list->v.create_mailbox_dir(box->list,
+						    box->name, FALSE) < 0)
 			return -1;
 	} else if (errno == ENOENT) {
 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
@@ -136,21 +118,11 @@
 cydir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
 		     bool directory)
 {
-	const char *path;
-	struct stat st;
+	if (directory &&
+	    (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
+		return 0;
 
-	path = mailbox_list_get_path(box->list, box->name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	if (stat(path, &st) == 0) {
-		mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
-				       "Mailbox already exists");
-		return -1;
-	}
-
-	if (create_cydir(box->storage, box->list, path) < 0)
-		return -1;
-
-	return directory || update == NULL ? 0 :
+	return update == NULL ? 0 :
 		index_storage_mailbox_update(box, update);
 }
 
--- a/src/lib-storage/index/dbox-common/dbox-storage.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/index/dbox-common/dbox-storage.c	Sun Feb 07 01:50:49 2010 +0200
@@ -157,48 +157,17 @@
 int dbox_mailbox_create(struct mailbox *box,
 			const struct mailbox_update *update, bool directory)
 {
-	const char *path, *alt_path, *origin;
-	struct stat st;
+	struct dbox_storage *storage = (struct dbox_storage *)box->storage;
 
-	path = mailbox_list_get_path(box->list, box->name,
-				     directory ? MAILBOX_LIST_PATH_TYPE_DIR :
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	if (stat(path, &st) == 0) {
-		mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
-				       "Mailbox already exists");
-		return -1;
-	}
-
-	if (directory) {
-		mode_t mode;
-		gid_t gid;
+	if (directory &&
+	    (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
+		return 0;
 
-		mailbox_list_get_dir_permissions(box->list, NULL, &mode,
-						 &gid, &origin);
-		if (mkdir_parents_chgrp(path, mode, gid, origin) == 0)
-			return 0;
-		else if (errno == EEXIST) {
-			mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
-					       "Mailbox already exists");
-		} else if (!mail_storage_set_error_from_errno(box->storage)) {
-			mail_storage_set_critical(box->storage,
-						  "mkdir(%s) failed: %m", path);
-		}
+	if (index_storage_mailbox_open(box) < 0)
 		return -1;
-	}
-
-	/* make sure the alt path doesn't exist yet. it shouldn't (except with
-	   race conditions with RENAME/DELETE), but if something crashed and
-	   left it lying around we don't want to start overwriting files in
-	   it. */
-	alt_path = dbox_get_alt_path(box->list, path);
-	if (alt_path != NULL && stat(alt_path, &st) == 0) {
-		mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
-				       "Mailbox already exists");
+	if (storage->v.mailbox_create_indexes(box, update) < 0)
 		return -1;
-	}
-
-	return dbox_mailbox_create_indexes(box, update);
+	return 0;
 }
 
 int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx ATTR_UNUSED,
--- a/src/lib-storage/index/index-storage.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/index/index-storage.c	Sun Feb 07 01:50:49 2010 +0200
@@ -451,8 +451,6 @@
 {
 	struct mailbox *box = &ibox->box;
 	const char *path;
-	gid_t dir_gid;
-	const char *origin, *dir_origin;
 	string_t *vname;
 
 	if (name != NULL) {
@@ -487,18 +485,11 @@
 	ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL;
 	ibox->index = index_storage_alloc(box->list, name, flags, index_prefix);
 
-	if (box->file_create_mode == 0) {
-		mailbox_list_get_permissions(box->list, name,
-					     &box->file_create_mode,
-					     &box->file_create_gid, &origin);
-		box->file_create_gid_origin = p_strdup(box->pool, origin);
-		mailbox_list_get_dir_permissions(box->list, name,
-						 &box->dir_create_mode,
-						 &dir_gid, &dir_origin);
-		mail_index_set_permissions(ibox->index,
-					   box->file_create_mode,
-					   box->file_create_gid, origin);
-	}
+	if (box->file_create_mode == 0)
+		mailbox_refresh_permissions(box);
+	mail_index_set_permissions(ibox->index, box->file_create_mode,
+				   box->file_create_gid,
+				   box->file_create_gid_origin);
 }
 
 int index_storage_mailbox_enable(struct mailbox *box,
--- a/src/lib-storage/index/maildir/maildir-storage.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Sun Feb 07 01:50:49 2010 +0200
@@ -23,8 +23,6 @@
 #include <unistd.h>
 #include <sys/stat.h>
 
-#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
-
 #define MAILDIR_LIST_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, maildir_mailbox_list_module)
 
@@ -293,19 +291,16 @@
 }
 
 /* create or fix maildir, ignore if it already exists */
-static int
-create_maildir(struct mail_storage *storage, struct mail_namespace *ns,
-	       const char *dir, mode_t mode, gid_t gid, const char *gid_origin,
-	       bool verify)
+static int create_maildir(struct mailbox *box, bool verify)
 {
 	const char *path;
 	unsigned int i;
 	int ret;
 
 	if (!verify) {
-		ret = maildir_check_tmp(storage, dir);
+		ret = maildir_check_tmp(box->storage, box->path);
 		if (ret > 0) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(box->storage,
 				MAIL_ERROR_EXISTS, "Mailbox already exists");
 			return -1;
 		}
@@ -314,9 +309,10 @@
 	}
 
 	for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) {
-		path = t_strconcat(dir, "/", maildir_subdirs[i], NULL);
-		if (mkdir_verify(storage, ns, path, mode, gid,
-				 gid_origin, verify) < 0)
+		path = t_strconcat(box->path, "/", maildir_subdirs[i], NULL);
+		if (mkdir_verify(box->storage, box->list->ns, path,
+				 box->dir_create_mode, box->file_create_gid,
+				 box->file_create_gid_origin, verify) < 0)
 			return -1;
 	}
 	return 0;
@@ -327,15 +323,6 @@
 	(void)maildir_uidlist_lock_touch(mbox->uidlist);
 }
 
-static mode_t get_dir_mode(mode_t mode)
-{
-	/* add the execute bit if either read or write bit is set */
-	if ((mode & 0600) != 0) mode |= 0100;
-	if ((mode & 0060) != 0) mode |= 0010;
-	if ((mode & 0006) != 0) mode |= 0001;
-	return mode;
-}
-
 static struct mailbox *
 maildir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
 		      const char *name, struct istream *input,
@@ -374,26 +361,9 @@
 	struct stat st;
 	const char *shared_path;
 
-	/* for shared mailboxes get the create mode from the
-	   permissions of dovecot-shared file. */
 	shared_path = t_strconcat(box->path, "/dovecot-shared", NULL);
-	if (stat(shared_path, &st) == 0) {
-		if ((st.st_mode & S_ISGID) != 0 ||
-		    (st.st_mode & 0060) == 0) {
-			/* Ignore GID */
-			st.st_gid = (gid_t)-1;
-		}
-		mail_index_set_permissions(mbox->ibox.index,
-					   st.st_mode & 0666, st.st_gid,
-					   shared_path);
-
-		box->file_create_mode = st.st_mode & 0666;
-		box->dir_create_mode = get_dir_mode(st.st_mode & 0666);
-		box->file_create_gid = st.st_gid;
-		mbox->ibox.box.file_create_gid_origin =
-			p_strdup(box->pool, shared_path);
+	if (stat(shared_path, &st) == 0)
 		box->private_flags_mask = MAIL_SEEN;
-	}
 
 	if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) {
 		if (maildir_uidlist_lock(mbox->uidlist) <= 0)
@@ -412,9 +382,6 @@
 static int maildir_mailbox_open(struct mailbox *box)
 {
 	struct stat st;
-	const char *gid_origin;
-	mode_t mode;
-	gid_t gid;
 	int ret;
 	bool inbox;
 
@@ -440,10 +407,7 @@
 	/* tmp/ directory doesn't exist. does the maildir? */
 	if (inbox || (*box->name != '\0' && stat(box->path, &st) == 0)) {
 		/* yes, we'll need to create the missing dirs */
-		mailbox_list_get_dir_permissions(box->list, box->name,
-						 &mode, &gid, &gid_origin);
-		if (create_maildir(box->storage, box->list->ns, box->path,
-				   mode, gid, gid_origin, TRUE) < 0)
+		if (create_maildir(box, TRUE) < 0)
 			return -1;
 
 		return maildir_mailbox_open_existing(box);
@@ -458,40 +422,31 @@
 	}
 }
 
-static int
-maildir_create_shared(struct mail_storage *storage, struct mail_namespace *ns,
-		      const char *dir, mode_t mode, gid_t gid,
-		      const char *gid_origin)
+static int maildir_create_shared(struct mailbox *box)
 {
 	const char *path;
 	mode_t old_mask;
 	int fd;
 
-	/* add the execute bit if either read or write bit is set */
-	if ((mode & 0600) != 0) mode |= 0100;
-	if ((mode & 0060) != 0) mode |= 0010;
-	if ((mode & 0006) != 0) mode |= 0001;
-
-	if (create_maildir(storage, ns, dir, mode, gid, gid_origin, FALSE) < 0)
-		return -1;
-
-	old_mask = umask(0777 ^ mode);
-	path = t_strconcat(dir, "/dovecot-shared", NULL);
-	fd = open(path, O_WRONLY | O_CREAT, mode & 0666);
+	old_mask = umask(0);
+	path = t_strconcat(box->path, "/dovecot-shared", NULL);
+	fd = open(path, O_WRONLY | O_CREAT, box->file_create_mode);
 	umask(old_mask);
 
 	if (fd == -1) {
-		mail_storage_set_critical(storage, "open(%s) failed: %m", path);
+		mail_storage_set_critical(box->storage, "open(%s) failed: %m",
+					  path);
 		return -1;
 	}
 
-	if (fchown(fd, (uid_t)-1, gid) < 0) {
+	if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
 		if (errno == EPERM) {
-			mail_storage_set_critical(storage, "%s",
+			mail_storage_set_critical(box->storage, "%s",
 				eperm_error_get_chgrp("fchown", path,
-						      gid, gid_origin));
+					box->file_create_gid,
+					box->file_create_gid_origin));
 		} else {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(box->storage,
 				"fchown(%s) failed: %m", path);
 		}
 	}
@@ -533,65 +488,27 @@
 maildir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
 		       bool directory)
 {
+	const char *root_dir, *shared_path;
 	struct stat st;
-	const char *path, *root_dir, *shared_path, *gid_origin;
-	mode_t old_mask;
-	int fd;
+
+	if (directory &&
+	    (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
+		return 0;
 
-	path = mailbox_list_get_path(box->list, box->name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (create_maildir(box, FALSE) < 0)
+		return -1;
+
+	/* if dovecot-shared exists in the root dir, copy it to newly
+	   created mailboxes */
 	root_dir = mailbox_list_get_path(box->list, NULL,
 					 MAILBOX_LIST_PATH_TYPE_MAILBOX);
-
-	/* if dovecot-shared exists in the root dir, create the mailbox using
-	   its permissions and gid, and copy the dovecot-shared inside it. */
 	shared_path = t_strconcat(root_dir, "/dovecot-shared", NULL);
 	if (stat(shared_path, &st) == 0) {
-		gid_origin = shared_path;
-		if (maildir_create_shared(box->storage, box->list->ns, path,
-					  st.st_mode & 0666, st.st_gid,
-					  gid_origin) < 0)
-			return -1;
-	} else {
-		mailbox_list_get_dir_permissions(box->list, NULL, &st.st_mode,
-						 &st.st_gid, &gid_origin);
-		if (create_maildir(box->storage, box->list->ns, path,
-				   st.st_mode, st.st_gid, gid_origin,
-				   FALSE) < 0)
+		if (maildir_create_shared(box) < 0)
 			return -1;
 	}
 
-	/* Maildir++ spec wants that maildirfolder named file is created for
-	   all subfolders. */
-	path = t_strconcat(path, "/" MAILDIR_SUBFOLDER_FILENAME, NULL);
-	old_mask = umask(0777 ^ (st.st_mode & 0666));
-	fd = open(path, O_CREAT | O_WRONLY, 0666);
-	umask(old_mask);
-	if (fd != -1) {
-		/* if dovecot-shared exists, use the same group */
-		if (st.st_gid == (gid_t)-1) {
-			/* doesn't exist */
-		} else if (fchown(fd, (uid_t)-1, st.st_gid) == 0) {
-			/* ok */
-		} else if (errno == EPERM) {
-			mail_storage_set_critical(box->storage, "%s",
-				eperm_error_get_chgrp("fchown", path,
-						     st.st_gid, gid_origin));
-		} else {
-			mail_storage_set_critical(box->storage,
-				"fchown(%s) failed: %m", path);
-		}
-		(void)close(fd);
-	} else if (errno == ENOENT) {
-		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
-			"Mailbox was deleted while it was being created");
-		return -1;
-	} else {
-		mail_storage_set_critical(box->storage,
-			"open(%s, O_CREAT) failed: %m", path);
-	}
-	return directory || update == NULL ? 0 :
-		maildir_mailbox_update(box, update);
+	return update == NULL ? 0 : maildir_mailbox_update(box, update);
 }
 
 static void
--- a/src/lib-storage/index/mbox/mbox-storage.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Sun Feb 07 01:50:49 2010 +0200
@@ -515,70 +515,23 @@
 mbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
 		    bool directory)
 {
-	struct mail_storage *storage = box->storage;
-	const char *path, *p, *origin;
-	struct stat st;
-	mode_t mode;
-	gid_t gid;
-	int fd;
+	int fd, ret;
+
+	if (directory &&
+	    (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
+		return 0;
 
-	/* make sure it doesn't exist already */
-	path = mailbox_list_get_path(box->list, box->name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	if (stat(path, &st) == 0) {
-		mail_storage_set_error(storage, MAIL_ERROR_EXISTS,
-				       "Mailbox already exists");
-		return -1;
-	}
-
-	if (errno != ENOENT) {
-		if (errno == ENOTDIR) {
-			mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE,
-				"Mailbox doesn't allow inferior mailboxes");
-		} else if (!mail_storage_set_error_from_errno(storage)) {
-			mail_storage_set_critical(storage,
-				"stat() failed for mbox file %s: %m", path);
+	/* create the mbox file */
+	ret = mailbox_create_fd(box, box->path, O_RDWR | O_CREAT | O_EXCL, &fd);
+	if (ret <= 0) {
+		if (ret == 0) {
+			mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
+					       "Mailbox already exists");
 		}
 		return -1;
 	}
-
-	/* create the hierarchy if needed */
-	p = directory ? path + strlen(path) : strrchr(path, '/');
-	if (p != NULL) {
-		p = t_strdup_until(path, p);
-		mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid,
-						 &origin);
-		if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 &&
-		    errno != EEXIST) {
-			if (!mail_storage_set_error_from_errno(storage)) {
-				mail_storage_set_critical(storage,
-					"mkdir_parents(%s) failed: %m", p);
-			}
-			return -1;
-		}
-
-		if (directory) {
-			/* wanted to create only the directory */
-			return 0;
-		}
-	}
-
-	/* create the mailbox file */
-	fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660);
-	if (fd != -1) {
-		(void)close(fd);
-		return update == NULL ? 0 : mbox_mailbox_update(box, update);
-	}
-
-	if (errno == EEXIST) {
-		/* mailbox was just created between stat() and open() call.. */
-		mail_storage_set_error(storage, MAIL_ERROR_EXISTS,
-				       "Mailbox already exists");
-	} else if (!mail_storage_set_error_from_errno(storage)) {
-		mail_storage_set_critical(storage,
-			"Can't create mailbox %s: %m", box->name);
-	}
-	return -1;
+	(void)close(fd);
+	return update == NULL ? 0 : mbox_mailbox_update(box, update);
 }
 
 static void mbox_mailbox_close(struct mailbox *box)
--- a/src/lib-storage/index/shared/shared-list.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/index/shared/shared-list.c	Sun Feb 07 01:50:49 2010 +0200
@@ -95,7 +95,9 @@
 	    shared_storage_get_namespace(&ns, &name) < 0) {
 		switch (type) {
 		case MAILBOX_LIST_PATH_TYPE_DIR:
+		case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
 		case MAILBOX_LIST_PATH_TYPE_MAILBOX:
+		case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
 		case MAILBOX_LIST_PATH_TYPE_CONTROL:
 			break;
 		case MAILBOX_LIST_PATH_TYPE_INDEX:
@@ -231,6 +233,21 @@
 }
 
 static int
+shared_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
+			       bool directory)
+{
+	struct mail_namespace *ns = list->ns;
+	int ret;
+
+	if (shared_storage_get_namespace(&ns, &name) < 0)
+		return -1;
+	ret = ns->list->v.create_mailbox_dir(ns->list, name, directory);
+	if (ret < 0)
+		shared_list_copy_error(list, ns);
+	return ret;
+}
+
+static int
 shared_list_delete_mailbox(struct mailbox_list *list, const char *name)
 {
 	struct mail_namespace *ns = list->ns;
@@ -337,6 +354,7 @@
 		shared_list_iter_deinit,
 		NULL,
 		shared_list_set_subscribed,
+		shared_list_create_mailbox_dir,
 		shared_list_delete_mailbox,
 		shared_list_delete_dir,
 		shared_list_rename_mailbox,
--- a/src/lib-storage/list/mailbox-list-fs.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/list/mailbox-list-fs.c	Sun Feb 07 01:50:49 2010 +0200
@@ -142,17 +142,23 @@
 		 enum mailbox_list_path_type type)
 {
 	const struct mailbox_list_settings *set = &_list->set;
-	const char *path;
+	const char *path, *root_dir;
 
 	if (name == NULL) {
 		/* return root directories */
 		switch (type) {
 		case MAILBOX_LIST_PATH_TYPE_DIR:
 			return set->root_dir;
+		case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
+			return _list->set.alt_dir;
 		case MAILBOX_LIST_PATH_TYPE_MAILBOX:
 			path = t_strconcat(set->root_dir, "/",
 					   set->mailbox_dir_name, NULL);
 			return t_strndup(path, strlen(path)-1);
+		case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
+			path = t_strconcat(set->alt_dir, "/",
+					   set->mailbox_dir_name, NULL);
+			return t_strndup(path, strlen(path)-1);
 		case MAILBOX_LIST_PATH_TYPE_CONTROL:
 			return set->control_dir != NULL ?
 				set->control_dir : set->root_dir;
@@ -168,14 +174,25 @@
 	if (mailbox_list_try_get_absolute_path(_list, &name))
 		return name;
 
+	root_dir = set->root_dir;
 	switch (type) {
 	case MAILBOX_LIST_PATH_TYPE_DIR:
 		if (*set->maildir_name != '\0')
 			return t_strdup_printf("%s/%s%s", set->root_dir,
 					       set->mailbox_dir_name, name);
 		break;
+	case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
+		if (*set->maildir_name != '\0')
+			return t_strdup_printf("%s/%s%s", set->alt_dir,
+					       set->mailbox_dir_name, name);
+		break;
 	case MAILBOX_LIST_PATH_TYPE_MAILBOX:
 		break;
+	case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
+		if (set->alt_dir == NULL)
+			return NULL;
+		root_dir = set->alt_dir;
+		break;
 	case MAILBOX_LIST_PATH_TYPE_CONTROL:
 		if (set->control_dir != NULL)
 			return t_strdup_printf("%s/%s%s", set->control_dir,
@@ -191,19 +208,23 @@
 		break;
 	}
 
-	/* If INBOX is a file, index and control directories are located
-	   in root directory. */
-	if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL &&
-	    ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 ||
-	     type == MAILBOX_LIST_PATH_TYPE_MAILBOX ||
-	     type == MAILBOX_LIST_PATH_TYPE_DIR))
-		return set->inbox_path;
+	if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR ||
+	    type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) {
+		/* don't use inbox_path */
+	} else if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL) {
+		/* If INBOX is a file, index and control directories are
+		   located in root directory. */
+		if ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 ||
+		    type == MAILBOX_LIST_PATH_TYPE_MAILBOX ||
+		    type == MAILBOX_LIST_PATH_TYPE_DIR)
+			return set->inbox_path;
+	}
 
 	if (*set->maildir_name == '\0') {
-		return t_strdup_printf("%s/%s%s", set->root_dir,
+		return t_strdup_printf("%s/%s%s", root_dir,
 				       set->mailbox_dir_name, name);
 	} else {
-		return t_strdup_printf("%s/%s%s/%s", set->root_dir,
+		return t_strdup_printf("%s/%s%s/%s", root_dir,
 				       set->mailbox_dir_name, name,
 				       set->maildir_name);
 	}
@@ -275,6 +296,61 @@
 				       name, set);
 }
 
+static int
+fs_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
+			   bool directory)
+{
+	const char *path, *alt_path, *gid_origin, *p;
+	struct stat st;
+	mode_t mode;
+	gid_t gid;
+	bool create_parent_dir;
+
+	/* make sure the alt path doesn't exist yet. it shouldn't (except with
+	   race conditions with RENAME/DELETE), but if something crashed and
+	   left it lying around we don't want to start overwriting files in
+	   it. */
+	if (!directory) {
+		alt_path = mailbox_list_get_path(list, name,
+					MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX);
+		if (alt_path != NULL && stat(alt_path, &st) == 0) {
+			mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+					       "Mailbox already exists");
+			return -1;
+		}
+	}
+
+	path = mailbox_list_get_path(list, name,
+				     directory ? MAILBOX_LIST_PATH_TYPE_DIR :
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	create_parent_dir = !directory &&
+		(list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0;
+	if (create_parent_dir) {
+		/* we only need to make sure that the parent directory exists */
+		p = strrchr(path, '/');
+		if (p == NULL)
+			return 0;
+		path = t_strdup_until(path, p);
+	}
+
+	mailbox_list_get_dir_permissions(list, NULL, &mode,
+					 &gid, &gid_origin);
+	if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0)
+		return 0;
+	else if (errno == EEXIST) {
+		if (create_parent_dir)
+			return 0;
+		mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+				       "Mailbox already exists");
+	} else if (errno == ENOTDIR) {
+		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+			"Mailbox doesn't allow inferior mailboxes");
+	} else if (!mailbox_list_set_error_from_errno(list)) {
+		mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path);
+	}
+	return -1;
+}
+
 static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name)
 {
 	/* let the backend handle the rest */
@@ -461,6 +537,7 @@
 		fs_list_iter_deinit,
 		NULL,
 		fs_list_set_subscribed,
+		fs_list_create_mailbox_dir,
 		fs_list_delete_mailbox,
 		fs_list_delete_dir,
 		fs_list_rename_mailbox,
--- a/src/lib-storage/list/mailbox-list-maildir.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/list/mailbox-list-maildir.c	Sun Feb 07 01:50:49 2010 +0200
@@ -3,12 +3,15 @@
 #include "lib.h"
 #include "array.h"
 #include "hostpid.h"
+#include "eacces-error.h"
+#include "mkdir-parents.h"
 #include "subscription-file.h"
 #include "mailbox-list-maildir.h"
 
 #include <stdio.h>
 #include <sys/stat.h>
 
+#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
 #define MAILDIR_GLOBAL_TEMP_PREFIX "temp."
 #define IMAPDIR_GLOBAL_TEMP_PREFIX ".temp."
 
@@ -168,12 +171,17 @@
 maildir_list_get_path(struct mailbox_list *_list, const char *name,
 		      enum mailbox_list_path_type type)
 {
+	const char *root_dir;
+
 	if (name == NULL) {
 		/* return root directories */
 		switch (type) {
 		case MAILBOX_LIST_PATH_TYPE_DIR:
 		case MAILBOX_LIST_PATH_TYPE_MAILBOX:
 			return _list->set.root_dir;
+		case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
+		case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
+			return _list->set.alt_dir;
 		case MAILBOX_LIST_PATH_TYPE_CONTROL:
 			return _list->set.control_dir != NULL ?
 				_list->set.control_dir : _list->set.root_dir;
@@ -188,10 +196,17 @@
 	    (*name == '/' || *name == '~'))
 		return maildir_list_get_absolute_path(_list, name);
 
+	root_dir = _list->set.root_dir;
 	switch (type) {
 	case MAILBOX_LIST_PATH_TYPE_DIR:
 	case MAILBOX_LIST_PATH_TYPE_MAILBOX:
 		break;
+	case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
+	case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
+		if (_list->set.alt_dir == NULL)
+			return NULL;
+		root_dir = _list->set.alt_dir;
+		break;
 	case MAILBOX_LIST_PATH_TYPE_CONTROL:
 		if (_list->set.control_dir != NULL) {
 			return maildir_list_get_dirname_path(_list,
@@ -208,10 +223,13 @@
 		break;
 	}
 
-	if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL)
+	if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR ||
+	    type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) {
+		/* don't use inbox_path */
+	} else if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL)
 		return _list->set.inbox_path;
 
-	return maildir_list_get_dirname_path(_list, _list->set.root_dir, name);
+	return maildir_list_get_dirname_path(_list, root_dir, name);
 }
 
 static int
@@ -381,6 +399,92 @@
 }
 
 static int
+maildir_list_create_maildirfolder_file(struct mailbox_list *list,
+				       const char *dir)
+{
+	const char *path, *gid_origin;
+	mode_t mode, old_mask;
+	gid_t gid;
+	int fd;
+
+	/* Maildir++ spec wants that maildirfolder named file is created for
+	   all subfolders. */
+	mailbox_list_get_permissions(list, NULL, &mode, &gid, &gid_origin);
+
+	path = t_strconcat(dir, "/" MAILDIR_SUBFOLDER_FILENAME, NULL);
+	old_mask = umask(0);
+	fd = open(path, O_CREAT | O_WRONLY, mode);
+	umask(old_mask);
+	if (fd != -1) {
+		/* ok */
+	} else if (errno == ENOENT) {
+		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+			"Mailbox was deleted while it was being created");
+		return -1;
+	} else {
+		mailbox_list_set_critical(list,
+			"open(%s, O_CREAT) failed: %m", path);
+		return -1;
+	}
+
+	if (gid != (gid_t)-1) {
+		if (fchown(fd, (uid_t)-1, gid) == 0) {
+			/* ok */
+		} else if (errno == EPERM) {
+			mailbox_list_set_critical(list, "%s",
+				eperm_error_get_chgrp("fchown", path,
+						      gid, gid_origin));
+		} else {
+			mailbox_list_set_critical(list,
+				"fchown(%s) failed: %m", path);
+		}
+	}
+	(void)close(fd);
+	return 0;
+}
+
+static int
+maildir_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
+				bool directory ATTR_UNUSED)
+{
+	const char *path, *gid_origin, *p;
+	mode_t mode;
+	gid_t gid;
+	bool create_parent_dir;
+
+	path = mailbox_list_get_path(list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	create_parent_dir = !directory &&
+		(list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0;
+	if (create_parent_dir) {
+		/* we only need to make sure that the parent directory exists */
+		p = strrchr(path, '/');
+		if (p == NULL)
+			return 0;
+		path = t_strdup_until(path, p);
+	}
+
+	mailbox_list_get_dir_permissions(list, NULL, &mode,
+					 &gid, &gid_origin);
+	if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0) {
+		/* ok */
+	} else if (errno == EEXIST) {
+		if (!create_parent_dir) {
+			mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+					       "Mailbox already exists");
+			return -1;
+		}
+	} else if (mailbox_list_set_error_from_errno(list)) {
+		return -1;
+	} else {
+		mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path);
+		return -1;
+	}
+	return create_parent_dir ? 0 :
+		maildir_list_create_maildirfolder_file(list, path);
+}
+
+static int
 maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
 {
 	/* let the backend handle the rest */
@@ -461,7 +565,8 @@
 struct mailbox_list maildir_mailbox_list = {
 	.name = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS,
 	.hierarchy_sep = '.',
-	.props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME,
+	.props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME |
+		MAILBOX_LIST_PROP_NO_NOSELECT,
 	.mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
 
 	{
@@ -480,6 +585,7 @@
 		maildir_list_iter_deinit,
 		NULL,
 		maildir_list_set_subscribed,
+		maildir_list_create_mailbox_dir,
 		maildir_list_delete_mailbox,
 		maildir_list_delete_dir,
 		maildir_list_rename_mailbox,
@@ -490,7 +596,8 @@
 struct mailbox_list imapdir_mailbox_list = {
 	.name = MAILBOX_LIST_NAME_IMAPDIR,
 	.hierarchy_sep = '.',
-	.props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME,
+	.props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME |
+		MAILBOX_LIST_PROP_NO_NOSELECT,
 	.mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
 
 	{
@@ -509,6 +616,7 @@
 		maildir_list_iter_deinit,
 		NULL,
 		maildir_list_set_subscribed,
+		maildir_list_create_mailbox_dir,
 		maildir_list_delete_mailbox,
 		maildir_list_delete_dir,
 		maildir_list_rename_mailbox,
--- a/src/lib-storage/mail-storage-private.h	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/mail-storage-private.h	Sun Feb 07 01:50:49 2010 +0200
@@ -430,5 +430,10 @@
 int mail_set_aborted(struct mail *mail);
 void mail_set_expunged(struct mail *mail);
 void mailbox_set_deleted(struct mailbox *box);
+void mailbox_refresh_permissions(struct mailbox *box);
+
+/* Returns -1 if error, 0 if failed with EEXIST, 1 if ok */
+int mailbox_create_fd(struct mailbox *box, const char *path, int flags,
+		      int *fd_r);
 
 #endif
--- a/src/lib-storage/mail-storage.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/mail-storage.c	Sun Feb 07 01:50:49 2010 +0200
@@ -4,6 +4,7 @@
 #include "ioloop.h"
 #include "array.h"
 #include "llist.h"
+#include "eacces-error.h"
 #include "mkdir-parents.h"
 #include "var-expand.h"
 #include "mail-index-private.h"
@@ -520,6 +521,17 @@
 		return -1;
 	}
 
+	if (box->list->v.create_mailbox_dir(box->list, box->name,
+					    directory) < 0) {
+		const char *str;
+		enum mail_error error;
+
+		str = mailbox_list_get_last_error(box->list, &error);
+		mail_storage_set_error(box->storage, error, str);
+		return -1;
+	}
+	mailbox_refresh_permissions(box);
+
 	return box->v.create(box, update, directory);
 }
 
@@ -1079,3 +1091,74 @@
 			       "Mailbox was deleted under us");
 	box->mailbox_deleted = TRUE;
 }
+
+void mailbox_refresh_permissions(struct mailbox *box)
+{
+	const char *origin, *dir_origin;
+	gid_t dir_gid;
+
+	if (box->input != NULL) {
+		box->file_create_mode = 0600;
+		box->dir_create_mode = 0700;
+		box->file_create_gid = (gid_t)-1;
+		box->file_create_gid_origin = "defaults";
+		return;
+	}
+
+	mailbox_list_get_permissions(box->list, box->name,
+				     &box->file_create_mode,
+				     &box->file_create_gid, &origin);
+
+	box->file_create_gid_origin = p_strdup(box->pool, origin);
+	mailbox_list_get_dir_permissions(box->list, box->name,
+					 &box->dir_create_mode,
+					 &dir_gid, &dir_origin);
+}
+
+int mailbox_create_fd(struct mailbox *box, const char *path, int flags,
+		      int *fd_r)
+{
+	mode_t old_mask;
+	int fd;
+
+	i_assert(box->file_create_mode != 0);
+	i_assert((flags & O_CREAT) != 0);
+
+	*fd_r = -1;
+
+	old_mask = umask(0);
+	fd = open(path, flags, box->file_create_mode);
+	umask(old_mask);
+
+	if (fd != -1) {
+		/* ok */
+	} else if (errno == EEXIST) {
+		/* O_EXCL used, caller will handle this error */
+		return 0;
+	} else if (errno == ENOENT) {
+		mailbox_set_deleted(box);
+		return -1;
+	} else if (mail_storage_set_error_from_errno(box->storage)) {
+		return -1;
+	} else {
+		mail_storage_set_critical(box->storage,
+			"open(%s, O_CREAT) failed: %m", path);
+		return -1;
+	}
+
+	if (box->file_create_gid != (gid_t)-1) {
+		if (fchown(fd, (uid_t)-1, box->file_create_gid) == 0) {
+			/* ok */
+		} else if (errno == EPERM) {
+			mail_storage_set_critical(box->storage, "%s",
+				eperm_error_get_chgrp("fchown", path,
+					box->file_create_gid,
+					box->file_create_gid_origin));
+		} else {
+			mail_storage_set_critical(box->storage,
+				"fchown(%s) failed: %m", path);
+		}
+	}
+	*fd_r = fd;
+	return 1;
+}
--- a/src/lib-storage/mailbox-list-private.h	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/mailbox-list-private.h	Sun Feb 07 01:50:49 2010 +0200
@@ -57,6 +57,8 @@
 
 	int (*set_subscribed)(struct mailbox_list *list,
 			      const char *name, bool set);
+	int (*create_mailbox_dir)(struct mailbox_list *list, const char *name,
+				  bool directory);
 	int (*delete_mailbox)(struct mailbox_list *list, const char *name);
 	int (*delete_dir)(struct mailbox_list *list, const char *name);
 	int (*rename_mailbox)(struct mailbox_list *oldlist, const char *oldname,
--- a/src/lib-storage/mailbox-list.h	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/lib-storage/mailbox-list.h	Sun Feb 07 01:50:49 2010 +0200
@@ -17,7 +17,9 @@
 
 enum mailbox_list_properties {
 	/* maildir_name must always be empty */
-	MAILBOX_LIST_PROP_NO_MAILDIR_NAME       = 0x01
+	MAILBOX_LIST_PROP_NO_MAILDIR_NAME	= 0x01,
+	/* no support for \noselect directories, only mailboxes */
+	MAILBOX_LIST_PROP_NO_NOSELECT		= 0x02
 };
 
 enum mailbox_list_flags {
@@ -76,8 +78,10 @@
 enum mailbox_list_path_type {
 	/* Return directory's path (eg. ~/dbox/INBOX) */
 	MAILBOX_LIST_PATH_TYPE_DIR,
+	MAILBOX_LIST_PATH_TYPE_ALT_DIR,
 	/* Return mailbox path (eg. ~/dbox/INBOX/dbox-Mails) */
 	MAILBOX_LIST_PATH_TYPE_MAILBOX,
+	MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX,
 	/* Return control directory */
 	MAILBOX_LIST_PATH_TYPE_CONTROL,
 	/* Return index file directory */
--- a/src/plugins/acl/acl-mailbox-list.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/plugins/acl/acl-mailbox-list.c	Sun Feb 07 01:50:49 2010 +0200
@@ -463,6 +463,30 @@
 }
 
 static int
+acl_mailbox_list_create_dir(struct mailbox_list *list, const char *name,
+			    bool directory)
+{
+	struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
+	int ret;
+
+	/* we're looking up CREATE permission from our parent's rights */
+	ret = acl_mailbox_list_have_right(list, name, TRUE,
+					  ACL_STORAGE_RIGHT_CREATE, NULL);
+	if (ret <= 0) {
+		if (ret < 0)
+			return -1;
+		/* Note that if user didn't have LOOKUP permission to parent
+		   mailbox, this may reveal the mailbox's existence to user.
+		   Can't help it. */
+		mailbox_list_set_error(list, MAIL_ERROR_PERM,
+				       MAIL_ERRSTR_NO_PERMISSION);
+		return -1;
+	}
+	return alist->module_ctx.super.
+		create_mailbox_dir(list, name, directory);
+}
+
+static int
 acl_mailbox_list_delete(struct mailbox_list *list, const char *name)
 {
 	struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
@@ -600,6 +624,7 @@
 	list->v.iter_next = acl_mailbox_list_iter_next;
 	list->v.iter_deinit = acl_mailbox_list_iter_deinit;
 	list->v.get_mailbox_name_status = acl_get_mailbox_name_status;
+	list->v.create_mailbox_dir = acl_mailbox_list_create_dir;
 	list->v.delete_mailbox = acl_mailbox_list_delete;
 	list->v.rename_mailbox = acl_mailbox_list_rename;
 
--- a/src/plugins/acl/acl-mailbox.c	Sat Feb 06 23:41:33 2010 +0200
+++ b/src/plugins/acl/acl-mailbox.c	Sun Feb 07 01:50:49 2010 +0200
@@ -124,26 +124,14 @@
 	acl_object_list_deinit(&iter);
 	acl_object_deinit(&parent_aclobj);
 }
+
 static int
 acl_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
 		   bool directory)
 {
 	struct acl_mailbox *abox = ACL_CONTEXT(box);
-	int ret;
 
-	/* we're looking up CREATE permission from our parent's rights */
-	ret = acl_mailbox_list_have_right(box->list, box->name, TRUE,
-					  ACL_STORAGE_RIGHT_CREATE, NULL);
-	if (ret <= 0) {
-		if (ret < 0)
-			return -1;
-		/* Note that if user didn't have LOOKUP permission to parent
-		   mailbox, this may reveal the mailbox's existence to user.
-		   Can't help it. */
-		mail_storage_set_error(box->storage, MAIL_ERROR_PERM,
-				       MAIL_ERRSTR_NO_PERMISSION);
-		return -1;
-	}
+	/* we already checked permissions in list.mailbox_create_dir(). */
 	if (abox->module_ctx.super.create(box, update, directory) < 0)
 		return -1;