diff src/lib-storage/index/dbox-multi/mdbox-storage.c @ 9977:0bb321c347ae HEAD

Split dbox (single-dbox) and mdbox (multi-dbox) into separate storage backends. This cleans up the code, makes it faster and also fixes some bugs. Super-fast maildir migration code was also dropped, at least for now.
author Timo Sirainen <tss@iki.fi>
date Tue, 06 Oct 2009 19:22:42 -0400
parents
children d1d810348e65
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c	Tue Oct 06 19:22:42 2009 -0400
@@ -0,0 +1,505 @@
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "str.h"
+#include "hex-binary.h"
+#include "randgen.h"
+#include "mkdir-parents.h"
+#include "unlink-directory.h"
+#include "unlink-old-files.h"
+#include "index-mail.h"
+#include "mail-copy.h"
+#include "mail-index-modseq.h"
+#include "mailbox-uidvalidity.h"
+#include "dbox-mail.h"
+#include "dbox-save.h"
+#include "mdbox-map.h"
+#include "mdbox-file.h"
+#include "mdbox-sync.h"
+#include "mdbox-storage-rebuild.h"
+#include "mdbox-storage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#define MDBOX_LIST_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, mdbox_mailbox_list_module)
+
+struct mdbox_mailbox_list {
+	union mailbox_list_module_context module_ctx;
+};
+
+extern struct mail_storage mdbox_storage;
+extern struct mailbox mdbox_mailbox;
+extern struct dbox_storage_vfuncs mdbox_dbox_storage_vfuncs;
+
+static MODULE_CONTEXT_DEFINE_INIT(mdbox_mailbox_list_module,
+				  &mailbox_list_module_register);
+
+static struct mail_storage *mdbox_storage_alloc(void)
+{
+	struct mdbox_storage *storage;
+	pool_t pool;
+
+	pool = pool_alloconly_create("dbox storage", 512+256);
+	storage = p_new(pool, struct mdbox_storage, 1);
+	storage->storage.v = mdbox_dbox_storage_vfuncs;
+	storage->storage.storage = mdbox_storage;
+	storage->storage.storage.pool = pool;
+	return &storage->storage.storage;
+}
+
+static int
+mdbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
+		     const char **error_r)
+{
+	struct mdbox_storage *storage = (struct mdbox_storage *)_storage;
+	const char *dir;
+
+	storage->set = mail_storage_get_driver_settings(_storage);
+	i_assert(storage->set->mdbox_max_open_files >= 2);
+
+	if (*ns->list->set.mailbox_dir_name == '\0') {
+		*error_r = "dbox: MAILBOXDIR must not be empty";
+		return -1;
+	}
+
+	_storage->unique_root_dir =
+		p_strdup(_storage->pool, ns->list->set.root_dir);
+
+	dir = mailbox_list_get_path(ns->list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
+	storage->storage_dir = p_strconcat(_storage->pool, dir,
+					   "/"MDBOX_GLOBAL_DIR_NAME, NULL);
+	storage->alt_storage_dir = p_strconcat(_storage->pool,
+					       ns->list->set.alt_dir,
+					       "/"MDBOX_GLOBAL_DIR_NAME, NULL);
+	i_array_init(&storage->open_files,
+		     I_MIN(storage->set->mdbox_max_open_files, 128));
+
+	storage->map = dbox_map_init(storage, ns->list, storage->storage_dir);
+	return 0;
+}
+
+static void mdbox_storage_destroy(struct mail_storage *_storage)
+{
+	struct mdbox_storage *storage = (struct mdbox_storage *)_storage;
+
+	if (storage->storage.files_corrupted) {
+		if (mdbox_storage_rebuild(storage) < 0)
+			return;
+	}
+
+	mdbox_files_free(storage);
+	dbox_map_deinit(&storage->map);
+	array_free(&storage->open_files);
+	index_storage_destroy(_storage);
+}
+
+struct mailbox *
+mdbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
+		    const char *name, struct istream *input,
+		    enum mailbox_flags flags)
+{
+	struct mdbox_mailbox *mbox;
+	pool_t pool;
+
+	/* dbox can't work without index files */
+	flags &= ~MAILBOX_FLAG_NO_INDEX_FILES;
+
+	pool = pool_alloconly_create("mdbox mailbox", 1024+512);
+	mbox = p_new(pool, struct mdbox_mailbox, 1);
+	mbox->ibox.box = mdbox_mailbox;
+	mbox->ibox.box.pool = pool;
+	mbox->ibox.box.storage = storage;
+	mbox->ibox.box.list = list;
+	mbox->ibox.mail_vfuncs = &mdbox_mail_vfuncs;
+
+	mbox->ibox.save_commit_pre = mdbox_transaction_save_commit_pre;
+	mbox->ibox.save_commit_post = mdbox_transaction_save_commit_post;
+	mbox->ibox.save_rollback = mdbox_transaction_save_rollback;
+
+	index_storage_mailbox_alloc(&mbox->ibox, name, input, flags,
+				    DBOX_INDEX_PREFIX);
+	mail_index_set_fsync_types(mbox->ibox.index,
+				   MAIL_INDEX_SYNC_TYPE_APPEND |
+				   MAIL_INDEX_SYNC_TYPE_EXPUNGE);
+
+	mbox->ibox.index_flags |= MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS |
+		MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY;
+
+	mbox->storage = (struct mdbox_storage *)storage;
+	mbox->ext_id =
+		mail_index_ext_register(mbox->ibox.index, "mdbox", 0,
+					sizeof(struct mdbox_mail_index_record),
+					sizeof(uint32_t));
+	mbox->hdr_ext_id =
+		mail_index_ext_register(mbox->ibox.index, "mdbox-hdr",
+					sizeof(struct mdbox_index_header), 0, 0);
+	mbox->guid_ext_id =
+		mail_index_ext_register(mbox->ibox.index, "guid",
+					0, MAIL_GUID_128_SIZE, 1);
+	return &mbox->ibox.box;
+}
+
+int mdbox_read_header(struct mdbox_mailbox *mbox,
+		      struct mdbox_index_header *hdr)
+{
+	const void *data;
+	size_t data_size;
+
+	mail_index_get_header_ext(mbox->ibox.view, mbox->hdr_ext_id,
+				  &data, &data_size);
+	if (data_size < MDBOX_INDEX_HEADER_MIN_SIZE &&
+	    (!mbox->creating || data_size != 0)) {
+		mail_storage_set_critical(&mbox->storage->storage.storage,
+			"dbox %s: Invalid dbox header size",
+			mbox->ibox.box.path);
+		return -1;
+	}
+	memset(hdr, 0, sizeof(*hdr));
+	memcpy(hdr, data, I_MIN(data_size, sizeof(*hdr)));
+	return 0;
+}
+
+void mdbox_update_header(struct mdbox_mailbox *mbox,
+			 struct mail_index_transaction *trans,
+			 const struct mailbox_update *update)
+{
+	struct mdbox_index_header hdr, new_hdr;
+
+	if (mdbox_read_header(mbox, &hdr) < 0)
+		memset(&hdr, 0, sizeof(hdr));
+
+	new_hdr = hdr;
+
+	if (update != NULL && !mail_guid_128_is_empty(update->mailbox_guid)) {
+		memcpy(new_hdr.mailbox_guid, update->mailbox_guid,
+		       sizeof(new_hdr.mailbox_guid));
+	} else if (mail_guid_128_is_empty(new_hdr.mailbox_guid)) {
+		mail_generate_guid_128(new_hdr.mailbox_guid);
+	}
+
+	new_hdr.map_uid_validity =
+		dbox_map_get_uid_validity(mbox->storage->map);
+	if (memcmp(&hdr, &new_hdr, sizeof(hdr)) != 0) {
+		mail_index_update_header_ext(trans, mbox->hdr_ext_id, 0,
+					     &new_hdr, sizeof(new_hdr));
+	}
+}
+
+static int mdbox_write_index_header(struct mailbox *box,
+				    const struct mailbox_update *update)
+{
+	struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+	struct mail_index_transaction *trans;
+	const struct mail_index_header *hdr;
+	uint32_t uid_validity, uid_next;
+
+	if (dbox_map_open(mbox->storage->map, TRUE) < 0)
+		return -1;
+
+	hdr = mail_index_get_header(mbox->ibox.view);
+	trans = mail_index_transaction_begin(mbox->ibox.view, 0);
+	mdbox_update_header(mbox, trans, update);
+
+	if (update != NULL && update->uid_validity != 0)
+		uid_validity = update->uid_validity;
+	else if (hdr->uid_validity == 0) {
+		/* set uidvalidity */
+		uid_validity = dbox_get_uidvalidity_next(box->list);
+	}
+
+	if (hdr->uid_validity != uid_validity) {
+		mail_index_update_header(trans,
+			offsetof(struct mail_index_header, uid_validity),
+			&uid_validity, sizeof(uid_validity), TRUE);
+	}
+	if (update != NULL && hdr->next_uid < update->min_next_uid) {
+		uid_next = update->min_next_uid;
+		mail_index_update_header(trans,
+			offsetof(struct mail_index_header, next_uid),
+			&uid_next, sizeof(uid_next), TRUE);
+	}
+	if (update != NULL && update->min_highest_modseq != 0 &&
+	    mail_index_modseq_get_highest(mbox->ibox.view) <
+	    					update->min_highest_modseq) {
+		mail_index_update_highest_modseq(trans,
+						 update->min_highest_modseq);
+	}
+
+	if (mail_index_transaction_commit(&trans) < 0) {
+		mail_storage_set_internal_error(box->storage);
+		mail_index_reset_error(mbox->ibox.index);
+		return -1;
+	}
+	return 0;
+}
+
+static int mdbox_mailbox_create_indexes(struct mailbox *box,
+					const struct mailbox_update *update)
+{
+	struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+	const char *origin;
+	mode_t mode;
+	gid_t gid;
+	int ret;
+
+	mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid, &origin);
+	if (mkdir_parents_chgrp(box->path, mode, gid, origin) == 0) {
+		/* create indexes immediately with the dbox header */
+		if (index_storage_mailbox_open(box) < 0)
+			return -1;
+		mbox->creating = TRUE;
+		ret = mdbox_write_index_header(box, update);
+		mbox->creating = FALSE;
+		if (ret < 0)
+			return -1;
+	} else if (errno != EEXIST) {
+		if (!mail_storage_set_error_from_errno(box->storage)) {
+			mail_storage_set_critical(box->storage,
+				"mkdir(%s) failed: %m", box->path);
+		}
+		return -1;
+	}
+	return 0;
+}
+
+static void mdbox_storage_get_status_guid(struct mailbox *box,
+					  struct mailbox_status *status_r)
+{
+	struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+	struct mdbox_index_header hdr;
+
+	if (mdbox_read_header(mbox, &hdr) < 0)
+		memset(&hdr, 0, sizeof(hdr));
+
+	if (mail_guid_128_is_empty(hdr.mailbox_guid)) {
+		/* regenerate it */
+		if (mdbox_write_index_header(box, NULL) < 0 ||
+		    mdbox_read_header(mbox, &hdr) < 0)
+			return;
+	}
+	memcpy(status_r->mailbox_guid, hdr.mailbox_guid,
+	       sizeof(status_r->mailbox_guid));
+}
+
+static void
+mdbox_storage_get_status(struct mailbox *box, enum mailbox_status_items items,
+			 struct mailbox_status *status_r)
+{
+	index_storage_get_status(box, items, status_r);
+
+	if ((items & STATUS_GUID) != 0)
+		mdbox_storage_get_status_guid(box, status_r);
+}
+
+static int
+mdbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
+{
+	if (!box->opened) {
+		if (index_storage_mailbox_open(box) < 0)
+			return -1;
+	}
+	return mdbox_write_index_header(box, update);
+}
+
+static int
+mdbox_mailbox_unref_mails(struct mailbox_list *list, const char *path)
+{
+	struct mdbox_storage *storage =
+		(struct mdbox_storage *)list->ns->storage;
+	const struct mail_storage_settings *old_set;
+	struct mail_storage_settings tmp_set;
+	struct mailbox *box;
+	struct mdbox_mailbox *mbox;
+	const struct mail_index_header *hdr;
+	const struct mdbox_mail_index_record *dbox_rec;
+	struct dbox_map_transaction_context *map_trans;
+	ARRAY_TYPE(uint32_t) map_uids;
+	const void *data;
+	bool expunged;
+	uint32_t seq;
+	int ret;
+
+	old_set = list->mail_set;
+	tmp_set = *list->mail_set;
+	tmp_set.mail_full_filesystem_access = TRUE;
+	list->mail_set = &tmp_set;
+	box = mdbox_mailbox_alloc(&storage->storage.storage, list, path, NULL,
+				  MAILBOX_FLAG_IGNORE_ACLS |
+				  MAILBOX_FLAG_KEEP_RECENT);
+	ret = mailbox_open(box);
+	list->mail_set = old_set;
+	if (ret < 0) {
+		mailbox_close(&box);
+		return -1;
+	}
+	mbox = (struct mdbox_mailbox *)box;
+
+	/* get a list of all map_uids in this mailbox */
+	i_array_init(&map_uids, 128);
+	hdr = mail_index_get_header(mbox->ibox.view);
+	for (seq = 1; seq <= hdr->messages_count; seq++) {
+		mail_index_lookup_ext(mbox->ibox.view, seq, mbox->ext_id,
+				      &data, &expunged);
+		dbox_rec = data;
+		if (dbox_rec == NULL) {
+			/* no multi-mails */
+			break;
+		}
+		if (dbox_rec->map_uid != 0)
+			array_append(&map_uids, &dbox_rec->map_uid, 1);
+	}
+
+	/* unreference the map_uids */
+	map_trans = dbox_map_transaction_begin(storage->map, FALSE);
+	ret = dbox_map_update_refcounts(map_trans, &map_uids, -1);
+	if (ret == 0)
+		ret = dbox_map_transaction_commit(map_trans);
+	dbox_map_transaction_free(&map_trans);
+	array_free(&map_uids);
+	mailbox_close(&box);
+	return ret;
+}
+
+static int
+mdbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+	struct mdbox_mailbox_list *mlist = MDBOX_LIST_CONTEXT(list);
+	const char *trash_dest;
+	int ret;
+
+	/* Make sure the indexes are closed before trying to delete the
+	   directory that contains them. It can still fail with some NFS
+	   implementations if indexes are opened by another session, but
+	   that can't really be helped. */
+	index_storage_destroy_unrefed();
+
+	/* delete the index and control directories */
+	if (mlist->module_ctx.super.delete_mailbox(list, name) < 0)
+		return -1;
+
+	if ((ret = dbox_list_delete_mailbox1(list, name, &trash_dest)) < 0)
+		return -1;
+	if (ret > 0) {
+		if (mdbox_mailbox_unref_mails(list, trash_dest) < 0) {
+			/* we've already renamed it. there's no going back. */
+			mailbox_list_set_internal_error(list);
+			ret = -1;
+		}
+	}
+	return dbox_list_delete_mailbox2(list, name, ret, trash_dest);
+}
+
+static int
+mdbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+			  struct mailbox_list *newlist, const char *newname,
+			  bool rename_children)
+{
+	struct mdbox_mailbox_list *oldmlist = MDBOX_LIST_CONTEXT(oldlist);
+
+	if (oldmlist->module_ctx.super.
+	    		rename_mailbox(oldlist, oldname, newlist, newname,
+				       rename_children) < 0)
+		return -1;
+	return dbox_list_rename_mailbox(oldlist, oldname, newlist, newname,
+					rename_children);
+}
+
+static void dbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
+				  struct mailbox_list *list)
+{
+	struct mdbox_mailbox_list *mlist;
+
+	mlist = p_new(list->pool, struct mdbox_mailbox_list, 1);
+	mlist->module_ctx.super = list->v;
+
+	list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
+	list->v.delete_mailbox = mdbox_list_delete_mailbox;
+	list->v.rename_mailbox = mdbox_list_rename_mailbox;
+	list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre;
+
+	MODULE_CONTEXT_SET(list, mdbox_mailbox_list_module, mlist);
+}
+
+struct mail_storage mdbox_storage = {
+	MEMBER(name) MDBOX_STORAGE_NAME,
+	MEMBER(class_flags) MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT,
+
+	{
+                mdbox_get_setting_parser_info,
+		mdbox_storage_alloc,
+		mdbox_storage_create,
+		mdbox_storage_destroy,
+		dbox_storage_add_list,
+		dbox_storage_get_list_settings,
+		NULL,
+		mdbox_mailbox_alloc,
+		mdbox_sync_purge
+	}
+};
+
+struct mailbox mdbox_mailbox = {
+	MEMBER(name) NULL,
+	MEMBER(storage) NULL,
+	MEMBER(list) NULL,
+
+	{
+		index_storage_is_readonly,
+		index_storage_allow_new_keywords,
+		index_storage_mailbox_enable,
+		dbox_mailbox_open,
+		index_storage_mailbox_close,
+		dbox_mailbox_create,
+		mdbox_mailbox_update,
+		mdbox_storage_get_status,
+		NULL,
+		NULL,
+		mdbox_storage_sync_init,
+		index_mailbox_sync_next,
+		index_mailbox_sync_deinit,
+		NULL,
+		dbox_notify_changes,
+		index_transaction_begin,
+		index_transaction_commit,
+		index_transaction_rollback,
+		index_transaction_set_max_modseq,
+		index_keywords_create,
+		index_keywords_create_from_indexes,
+		index_keywords_ref,
+		index_keywords_unref,
+		index_keyword_is_valid,
+		index_storage_get_seq_range,
+		index_storage_get_uid_range,
+		index_storage_get_expunges,
+		NULL,
+		NULL,
+		NULL,
+		dbox_mail_alloc,
+		index_header_lookup_init,
+		index_header_lookup_deinit,
+		index_storage_search_init,
+		index_storage_search_deinit,
+		index_storage_search_next_nonblock,
+		index_storage_search_next_update_seq,
+		mdbox_save_alloc,
+		mdbox_save_begin,
+		dbox_save_continue,
+		mdbox_save_finish,
+		mdbox_save_cancel,
+		mdbox_copy,
+		index_storage_is_inconsistent
+	}
+};
+
+struct dbox_storage_vfuncs mdbox_dbox_storage_vfuncs = {
+	mdbox_file_unrefed,
+	mdbox_file_create_fd,
+	mdbox_mail_open,
+	mdbox_mailbox_create_indexes
+};