view src/lib-storage/list/index-mailbox-list-sync.c @ 6275:913b188f4dd4 HEAD

Removed explicit locking from views and maps. They were already locked all the time when they were used. Because of this change several functions can no longer fail, so they were changed to return void, and a lot of pointless error handling was removed.
author Timo Sirainen <tss@iki.fi>
date Sun, 12 Aug 2007 16:43:05 +0300
parents a19931ec66db
children 5f66277bbe40
line wrap: on
line source

/* Copyright (C) 2006 Timo Sirainen */

#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "index-storage.h"
#include "mailbox-list-index.h"
#include "index-mailbox-list.h"
#include "maildir/maildir-sync.h"

#include <sys/stat.h>

#define INDEX_LIST_STORAGE_CONTEXT(obj) \
	MODULE_CONTEXT(obj, index_list_storage_module)

#define CACHED_STATUS_ITEMS \
	(STATUS_MESSAGES | STATUS_UNSEEN | STATUS_RECENT | \
	 STATUS_UIDNEXT | STATUS_UIDVALIDITY)

struct index_list_mailbox {
	union mailbox_module_context module_ctx;

	uint32_t log_seq;
	uoff_t log_offset;
};

struct index_list_map {
	const char *name;
	unsigned int eid_offset;
	unsigned int status_offset;
};
#undef DEF
#define DEF(a, b, c) \
	{ a, offsetof(struct index_mailbox_list, b), \
	  offsetof(struct mailbox_status, c) }
static struct index_list_map index_list_map[] = {
	DEF("msgs", eid_messages, messages),
	DEF("unseen", eid_unseen, unseen),
	DEF("recent", eid_recent, recent),
	DEF("uid_validity", eid_uid_validity, uidvalidity),
	DEF("uidnext", eid_uidnext, uidnext),
	{ NULL, 0, 0 }
};

static void (*index_list_next_hook_mailbox_created)(struct mailbox *box);

static MODULE_CONTEXT_DEFINE_INIT(index_list_storage_module,
				  &mail_storage_module_register);

static int index_list_box_close(struct mailbox *box)
{
	struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box);

	return ibox->module_ctx.super.close(box);
}

static int index_list_update_mail_index(struct index_mailbox_list *ilist,
					struct mailbox *box)
{
	struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box);
	struct mail_index_sync_ctx *mail_sync_ctx;
	struct mail_index_view *view;
	struct mail_index_transaction *trans;
	struct mail_index_sync_rec sync_rec;
	int ret;

	if (ibox->log_seq == 0)
		return 0;

	ret = mail_index_sync_begin(ilist->mail_index,
				    &mail_sync_ctx, &view, &trans,
				    ibox->log_seq, ibox->log_offset, 0);
	if (ret <= 0)
		return ret;

	/* we should have only external transactions in here, for which we
	   don't need to do anything but write them to the index */
	while (mail_index_sync_next(mail_sync_ctx, &sync_rec) > 0)
		;

	return mail_index_sync_commit(&mail_sync_ctx);
}

static int
index_list_mailbox_open_unchanged_view(struct mailbox *box,
				       struct mail_index_view **view_r,
				       uint32_t *seq_r)
{
	struct mailbox_list *list;
	struct index_mailbox_list *ilist;
	struct mail_index_view *view;
	uint32_t uid, seq;
	int ret;

	list = mail_storage_get_list(box->storage);
	ilist = INDEX_LIST_CONTEXT(list);

	if (ilist == NULL) {
		/* indexing disabled */
		return 0;
	}

	ret = mailbox_list_index_lookup(ilist->list_sync_view, box->name, &uid);
	if (ret <= 0)
		return ret;

	/* make sure we're synced */
	if (index_list_update_mail_index(ilist, box) < 0)
		return -1;

	/* found from list index. lookup the mail index record for it */
	view = mail_index_view_open(ilist->mail_index);
	mail_index_lookup_uid_range(view, uid, uid, &seq, &seq);
	if (seq == 0) {
		mail_index_view_close(&view);
		return 0;
	}

	t_push();
	ret = box->v.list_index_has_changed(box, view, seq);
	t_pop();
	if (ret != 0) {
		/* error / mailbox has changed. we'll need to sync it. */
		mail_index_view_close(&view);
		return ret < 0 ? -1 : 0;
	}

	*view_r = view;
	*seq_r = seq;
	return 1;
}

static int
index_list_get_cached_status(struct mailbox *box, struct mailbox_status *status)
{
	struct mailbox_list *list;
	struct index_mailbox_list *ilist;
	struct mail_index_view *view;
	const void *data;
	uint32_t seq, *ext_id_p, *counter_p;
	unsigned int i;
	int ret;

	memset(status, 0, sizeof(*status));

	ret = index_list_mailbox_open_unchanged_view(box, &view, &seq);
	if (ret <= 0)
		return ret;

	list = mail_storage_get_list(box->storage);
	ilist = INDEX_LIST_CONTEXT(list);
	for (i = 0; index_list_map[i].name != NULL; i++) {
		ext_id_p = PTR_OFFSET(ilist, index_list_map[i].eid_offset);
		ret = mail_index_lookup_ext(view, seq, *ext_id_p, &data);
		if (ret <= 0 || data == NULL)
			break;

		counter_p = PTR_OFFSET(status, index_list_map[i].status_offset);
		*counter_p = *(const uint32_t *)data;
	}

	mail_index_view_close(&view);
	return 1;
}

static void
index_list_get_status(struct mailbox *box, enum mailbox_status_items items,
		      struct mailbox_status *status)
{
	struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box);

	if ((items & ~CACHED_STATUS_ITEMS) == 0) {
		if (index_list_get_cached_status(box, status) > 0)
			return;
		/* nonsynced / error, fallback to doing it the slow way */
	}

	ibox->module_ctx.super.get_status(box, items, status);
}

static int index_list_lookup_or_create(struct index_mailbox_list *ilist,
				       struct mailbox *box, uint32_t *uid_r)
{
	struct mailbox_list_index_sync_ctx *sync_ctx;
	int ret;

	ret = mailbox_list_index_lookup(ilist->list_sync_view,
					box->name, uid_r);
	if (ret > 0) {
		/* we'll need the mailbox synced since we're updating its
		   contents based on what it already contains */
		if (index_list_update_mail_index(ilist, box) < 0)
			return -1;
		return 1;
	} else if (ret < 0)
		return -1;

	/* create the mailbox by doing a partial sync with the mailbox name
	   as the sync root path */
	if (mailbox_list_index_sync_init(ilist->list_index, box->name,
					 MAILBOX_LIST_SYNC_FLAG_PARTIAL,
					 &sync_ctx) < 0)
		return -1;
	if (mailbox_list_index_sync_commit(&sync_ctx) < 0)
		return -1;

	ret = mailbox_list_index_lookup(ilist->list_sync_view,
					box->name, uid_r);
	if (ret != 0)
		return ret < 0 ? -1 : 0;

	mail_storage_set_critical(box->storage,
		"mailbox index: Created mailbox %s not found", box->name);
	return -1;
}

static int
index_list_update(struct index_mailbox_list *ilist, struct mailbox *box,
		  struct mail_index_view *view, uint32_t seq,
		  const struct mailbox_status *status)
{
	struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box);
	struct mail_index_transaction *trans;
	const void *data;
	const uint32_t *counter_p;
	uint32_t *ext_id_p;
	unsigned int i;
	int ret = 1;

	trans = mail_index_transaction_begin(view,
					MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);

	/* update counters */
	for (i = 0; index_list_map[i].name != NULL; i++) {
		ext_id_p = PTR_OFFSET(ilist, index_list_map[i].eid_offset);
		ret = mail_index_lookup_ext(view, seq, *ext_id_p, &data);
		if (ret <= 0)
			break;

		counter_p = CONST_PTR_OFFSET(status,
					     index_list_map[i].status_offset);
		if (data == NULL ||
		    *(const uint32_t *)data != *counter_p) {
			mail_index_update_ext(trans, seq, *ext_id_p,
					      counter_p, NULL);
		}
	}

	if (box->v.list_index_update_sync(box, trans, seq) < 0)
		ret = -1;
	if (ret <= 0) {
		mail_index_transaction_rollback(&trans);
		return -1;
	}

	return mail_index_transaction_commit(&trans, &ibox->log_seq,
					     &ibox->log_offset);
}

static struct mailbox_sync_context *
index_list_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
{
	struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box);
	struct mailbox_sync_context *ctx;

	/* clear any cached log seq/offset */
	ibox->log_seq = (uint32_t)-1;
	ibox->log_offset = 0;

	if (!box->opened) {
		/* check using the mailbox list index if the mailbox has
		   changed. if not, we don't need to open the mailbox yet. */
		struct mail_index_view *view;
		uint32_t seq;
		int ret;

		ret = index_list_mailbox_open_unchanged_view(box, &view, &seq);
		if (ret > 0) {
			ctx = i_new(struct mailbox_sync_context, 1);
			ctx->box = box;
			mail_index_view_close(&view);

			/* no changes, so don't bother checking again before
			   next sync */
			ibox->log_seq = 0;
			return ctx;
		}
	}

	return ibox->module_ctx.super.sync_init(box, flags);
}

static int index_list_sync_next(struct mailbox_sync_context *ctx,
				struct mailbox_sync_rec *sync_rec_r)
{
	struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(ctx->box);

	if (!ctx->box->opened)
		return 0;

	return ibox->module_ctx.super.sync_next(ctx, sync_rec_r);
}

static int index_list_sync_deinit(struct mailbox_sync_context *ctx,
				  enum mailbox_status_items status_items,
				  struct mailbox_status *status_r)
{
	struct mailbox *box = ctx->box;
	struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box);
	struct mailbox_list *list;
	struct index_mailbox_list *ilist;
	struct mail_index_view *view;
	struct mailbox_status tmp_status, *status;
	uint32_t uid, seq;

	if (!box->opened) {
		/* nothing synced. just return the status. */
		i_free(ctx);

		if (status_items != 0)
			index_list_get_status(box, status_items, status_r);
		return 0;
	}

	list = mail_storage_get_list(box->storage);
	ilist = INDEX_LIST_CONTEXT(list);

	if (ilist == NULL) {
		/* indexing disabled */
		return ibox->module_ctx.super.
			sync_deinit(ctx, status_items, status_r);
	}

	/* if status_items == 0, the status_r may be NULL. we really want to
	   know the status anyway, so save it elsewhere then */
	status = status_items == 0 ? &tmp_status : status_r;
	status_items |= CACHED_STATUS_ITEMS;

	if (ibox->module_ctx.super.sync_deinit(ctx, status_items, status) < 0)
		return -1;
	ctx = NULL;

	/* sync mailbox list index */
	if (index_list_lookup_or_create(ilist, box, &uid) < 0) {
		/* just ignore the error */
		return 0;
	}

	view = mail_index_view_open(ilist->mail_index);
	mail_index_lookup_uid_range(view, uid, uid, &seq, &seq);
	if (seq != 0)
		(void)index_list_update(ilist, box, view, seq, status);
	mail_index_view_close(&view);
	return 0;
}

static void index_list_mail_mailbox_opened(struct mailbox *box)
{
	struct index_mailbox_list *ilist =
		INDEX_LIST_CONTEXT(box->storage->list);
	struct index_list_mailbox *ibox;

	if (index_list_next_hook_mailbox_created != NULL)
		index_list_next_hook_mailbox_created(box);

	if (ilist == NULL)
		return;

	ibox = p_new(box->pool, struct index_list_mailbox, 1);
	ibox->module_ctx.super = box->v;
	box->v.close = index_list_box_close;
	box->v.get_status = index_list_get_status;
	box->v.sync_init = index_list_sync_init;
	box->v.sync_next = index_list_sync_next;
	box->v.sync_deinit = index_list_sync_deinit;

	MODULE_CONTEXT_SET(box, index_list_storage_module, ibox);
}

void index_mailbox_list_sync_init_list(struct mailbox_list *list)
{
	struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list);
	unsigned int i;
	uint32_t *ext_id_p;

	for (i = 0; index_list_map[i].name != NULL; i++) {
		ext_id_p = PTR_OFFSET(ilist, index_list_map[i].eid_offset);
		*ext_id_p = mail_index_ext_register(ilist->mail_index,
					index_list_map[i].name, 0,
					sizeof(uint32_t), sizeof(uint32_t));
	}
}

void index_mailbox_list_sync_init(void)
{
	index_list_next_hook_mailbox_created = hook_mailbox_opened;
	hook_mailbox_opened = index_list_mail_mailbox_opened;
}