changeset 5962:3e01ee1a2864 HEAD

Don't require locking uidlist unless mail has keywords or we want to return UIDs to caller.
author Timo Sirainen <tss@iki.fi>
date Thu, 12 Jul 2007 05:51:03 +0300
parents 7aa61bb91ba3
children 9bef18690777
files src/lib-storage/index/maildir/maildir-save.c
diffstat 1 files changed, 78 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/maildir/maildir-save.c	Thu Jul 12 05:50:10 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-save.c	Thu Jul 12 05:51:03 2007 +0300
@@ -12,6 +12,7 @@
 #include "index-mail.h"
 #include "maildir-storage.h"
 #include "maildir-uidlist.h"
+#include "maildir-keywords.h"
 #include "maildir-filename.h"
 #include "maildir-sync.h"
 
@@ -25,7 +26,6 @@
 struct maildir_filename {
 	struct maildir_filename *next;
 	const char *basename;
-	const char *saved_dest_fname; /* only if it had keywords */
 
 	uoff_t size;
 	enum mail_flags flags;
@@ -40,11 +40,13 @@
 	struct maildir_mailbox *mbox;
 	struct mail_index_transaction *trans;
 	struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
+	struct maildir_keywords_sync_ctx *keywords_sync_ctx;
 	struct maildir_index_sync_context *sync_ctx;
 	struct mail *mail, *cur_dest_mail;
 
 	const char *tmpdir, *newdir, *curdir;
 	struct maildir_filename *files, **files_tail, *file_last;
+	unsigned int files_count;
 
 	buffer_t *keywords_buffer;
 	ARRAY_TYPE(keyword_indexes) keywords_array;
@@ -56,6 +58,8 @@
 	uint32_t first_seq, seq;
 
 	unsigned int want_mails:1;
+	unsigned int have_keywords:1;
+	unsigned int locked:1;
 	unsigned int failed:1;
 	unsigned int moving:1;
 	unsigned int finished:1;
@@ -130,8 +134,7 @@
 	   synced state. in that case it's cheap to update index file.
 	   this can't be completely trusted because uidlist isn't locked,
 	   but if there are some changes we can deal with it. */
-	ctx->want_mails = maildir_sync_is_synced(mbox) ||
-		(t->ictx.flags & MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS) != 0;
+	ctx->want_mails = maildir_sync_is_synced(mbox);
 
 	ctx->keywords_buffer = buffer_create_const_data(pool, NULL, 0);
 	array_create_from_buffer(&ctx->keywords_array, ctx->keywords_buffer,
@@ -195,6 +198,7 @@
 	i_assert(*ctx->files_tail == NULL);
 	*ctx->files_tail = mf;
 	ctx->files_tail = &mf->next;
+	ctx->files_count++;
 
 	if (keywords != NULL) {
 		i_assert(sizeof(keywords->idx[0]) == sizeof(unsigned int));
@@ -203,6 +207,7 @@
 		mf->keywords_count = keywords->count;
 		memcpy(mf + 1, keywords->idx,
 		       sizeof(unsigned int) * keywords->count);
+		ctx->have_keywords = TRUE;
 	}
 
 	if (ctx->want_mails) {
@@ -235,7 +240,7 @@
 		if (ctx->input == NULL) {
 			/* FIXME: copying with hardlinking. we could copy the
 			   cached data directly */
-			ctx->cur_dest_mail = 0;
+			ctx->cur_dest_mail = NULL;
 		} else {
 			tee = tee_i_stream_create(ctx->input, default_pool);
 			ctx->input =
@@ -278,20 +283,11 @@
 		return FALSE;
 	}
 
-	/* If we're unlinking already copied files, ctx->sync_ctx could be
-	   NULL by now. So we use the saved filename if it exists. */
-	if (mf->saved_dest_fname != NULL) {
-		*fname_r = mf->saved_dest_fname;
-		return FALSE;
-	}
-
 	buffer_update_const_data(ctx->keywords_buffer, mf + 1,
 				 mf->keywords_count * sizeof(unsigned int));
-	*fname_r = maildir_filename_set_flags(
-			maildir_sync_get_keywords_sync_ctx(ctx->sync_ctx),
-			basename, mf->flags & MAIL_FLAGS_MASK,
-			&ctx->keywords_array);
-	mf->saved_dest_fname = p_strdup(ctx->pool, *fname_r);
+	*fname_r = maildir_filename_set_flags(ctx->keywords_sync_ctx, basename,
+					      mf->flags & MAIL_FLAGS_MASK,
+					      &ctx->keywords_array);
 	return FALSE;
 }
 
@@ -546,6 +542,7 @@
 		*fm = NULL;
 		ctx->files_tail = fm;
 		ctx->file_last = NULL;
+		ctx->files_count--;
 
 		t_pop();
 		return -1;
@@ -596,20 +593,34 @@
 	i_assert(ctx->output == NULL);
 	i_assert(ctx->finished);
 
-	if (maildir_uidlist_sync_init(ctx->mbox->uidlist, TRUE,
-				      &ctx->uidlist_sync_ctx) <= 0) {
-		/* error or timeout - our transaction is broken */
-		maildir_transaction_save_rollback(ctx);
-		return -1;
+	/* if we want to assign UIDs or keywords, we require uidlist lock */
+	if ((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS) == 0 &&
+	    !ctx->have_keywords) {
+		/* assign the UIDs if we happen to get a lock */
+		ctx->locked = maildir_uidlist_try_lock(ctx->mbox->uidlist) > 0;
+	} else {
+		if (maildir_uidlist_lock(ctx->mbox->uidlist) <= 0) {
+			/* error or timeout - our transaction is broken */
+			maildir_transaction_save_rollback(ctx);
+			return -1;
+		}
+		ctx->locked = TRUE;
 	}
 
-	/* Start syncing so that keywords_sync_ctx gets set.. */
-	if (maildir_sync_index_begin(ctx->mbox, NULL, &ctx->sync_ctx) < 0) {
-		maildir_transaction_save_rollback(ctx);
-		return -1;
-	}
+	if (ctx->locked) {
+		ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, TRUE,
+						&ctx->uidlist_sync_ctx);
+		i_assert(ret > 0); /* already locked, shouldn't fail */
 
-	if (ctx->want_mails) {
+		if (maildir_sync_index_begin(ctx->mbox, NULL,
+					     &ctx->sync_ctx) < 0) {
+			maildir_transaction_save_rollback(ctx);
+			return -1;
+		}
+
+		ctx->keywords_sync_ctx =
+			maildir_sync_get_keywords_sync_ctx(ctx->sync_ctx);
+
 		/* now that uidlist is locked, make sure all the existing mails
 		   have been added to index. we don't really look into the
 		   maildir, just add all the new mails listed in
@@ -620,12 +631,14 @@
 		}
 		sync_commit = TRUE;
 
+		/* if messages were added to index, assign them UIDs */
 		first_uid = maildir_uidlist_get_next_uid(ctx->mbox->uidlist);
 		i_assert(first_uid != 0);
 		mail_index_append_assign_uids(ctx->trans, first_uid, &next_uid);
 
+		/* this will work even if index isn't updated */
 		*t->ictx.first_saved_uid = first_uid;
-		*t->ictx.last_saved_uid = next_uid - 1;
+		*t->ictx.last_saved_uid = first_uid + ctx->files_count - 1;
 	}
 
 	flags = MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
@@ -646,27 +659,48 @@
 			ret = maildir_file_move(ctx, mf->basename,
 						dest, newdir);
 		}
-		if (ret == 0) {
-			ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
-							dest, flags);
-			i_assert(ret != 0);
-			ret = ret < 0 ? -1 : 0;
-		}
 		t_pop();
 	}
 
-	/* if we didn't call maildir_sync_index() we could skip over
-	   transactions by committing the changes */
-	if (maildir_sync_index_finish(&ctx->sync_ctx, ret < 0,
-				      !sync_commit) < 0)
-		ret = -1;
+	if (ret == 0 && ctx->uidlist_sync_ctx != NULL) {
+		/* everything was moved successfully. update our internal
+		   state. */
+		for (mf = ctx->files; mf != NULL; mf = mf->next) {
+			t_push();
+			newdir = maildir_get_updated_filename(ctx, mf, &dest);
+
+			ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
+							dest, flags);
+			i_assert(ret > 0);
+			t_pop();
+		}
+	}
+
+	if (ctx->uidlist_sync_ctx != NULL) {
+		/* update dovecot-uidlist file. */
+		if (maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx) < 0)
+			ret = -1;
+	}
+
+	if (sync_commit) {
+		/* It doesn't matter if index syncing fails */
+		(void)maildir_sync_index_finish(&ctx->sync_ctx,
+						ret < 0, !sync_commit);
+	}
 
 	if (ret < 0) {
+		ctx->keywords_sync_ctx = !ctx->have_keywords ? NULL :
+			maildir_keywords_sync_init(ctx->mbox->keywords,
+						   ctx->mbox->ibox.index);
+
 		/* unlink the files we just moved in an attempt to rollback
 		   the transaction. uidlist is still locked, so at least other
 		   Dovecot instances haven't yet seen the files. */
 		maildir_transaction_unlink_copied_files(ctx, mf);
 
+		maildir_keywords_sync_deinit(ctx->keywords_sync_ctx);
+		ctx->keywords_sync_ctx = NULL;
+
 		/* returning failure finishes the save_context */
 		maildir_transaction_save_rollback(ctx);
 	}
@@ -676,9 +710,8 @@
 
 void maildir_transaction_save_commit_post(struct maildir_save_context *ctx)
 {
-	/* uidlist locks the syncing. don't release it until save's transaction
-	   has been written to disk. */
-	(void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
+	if (ctx->locked)
+		maildir_uidlist_unlock(ctx->mbox->uidlist);
 
 	if (ctx->mail != NULL)
 		index_mail_free(ctx->mail);
@@ -719,8 +752,10 @@
 	if (hardlinks)
 		maildir_transaction_unlink_copied_files(ctx, NULL);
 
-	if (ctx->uidlist_sync_ctx != NULL)
+	if (ctx->locked) {
 		(void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
+		maildir_uidlist_unlock(ctx->mbox->uidlist);
+	}
 	if (ctx->sync_ctx != NULL)
 		(void)maildir_sync_index_finish(&ctx->sync_ctx, TRUE, FALSE);