changeset 18371:b900b50085fc

dsync: Use storage's mail_error to choose the doveadm exit code. Instead of always assuming that all errors are EX_TEMPFAIL.
author Timo Sirainen <tss@iki.fi>
date Thu, 19 Mar 2015 00:41:19 +0200
parents b28de884c470
children cbdfca7d24a6
files src/doveadm/doveadm-dsync.c src/doveadm/dsync/dsync-brain-mailbox-tree-sync.c src/doveadm/dsync/dsync-brain-mailbox-tree.c src/doveadm/dsync/dsync-brain-mailbox.c src/doveadm/dsync/dsync-brain-mails.c src/doveadm/dsync/dsync-brain-private.h src/doveadm/dsync/dsync-brain.c src/doveadm/dsync/dsync-brain.h src/doveadm/dsync/dsync-ibc-pipe.c src/doveadm/dsync/dsync-ibc-private.h src/doveadm/dsync/dsync-ibc-stream.c src/doveadm/dsync/dsync-ibc.c src/doveadm/dsync/dsync-ibc.h src/doveadm/dsync/dsync-mailbox-export.c src/doveadm/dsync/dsync-mailbox-export.h src/doveadm/dsync/dsync-mailbox-import.c src/doveadm/dsync/dsync-mailbox-import.h src/doveadm/dsync/dsync-mailbox-tree-fill.c src/doveadm/dsync/dsync-mailbox-tree.h
diffstat 19 files changed, 245 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/src/doveadm/doveadm-dsync.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/doveadm-dsync.c	Thu Mar 19 00:41:19 2015 +0200
@@ -322,7 +322,7 @@
 static int
 cmd_dsync_run_local(struct dsync_cmd_context *ctx, struct mail_user *user,
 		    struct dsync_brain *brain, struct dsync_ibc *ibc2,
-		    bool *changes_during_sync_r)
+		    bool *changes_during_sync_r, enum mail_error *mail_error_r)
 {
 	struct dsync_brain *brain2;
 	struct mail_user *user2;
@@ -331,6 +331,8 @@
 	bool brain1_running, brain2_running, changed1, changed2;
 	int ret;
 
+	*mail_error_r = 0;
+
 	if (ctx->local_location_from_arg)
 		location = ctx->ctx.args[0];
 	else {
@@ -389,11 +391,7 @@
 		brain2_running = dsync_brain_run(brain2, &changed2);
 	}
 	*changes_during_sync_r = dsync_brain_has_unexpected_changes(brain2);
-	if (dsync_brain_deinit(&brain2) < 0) {
-		ctx->ctx.exit_code = EX_TEMPFAIL;
-		return -1;
-	}
-	return 0;
+	return dsync_brain_deinit(&brain2, mail_error_r);
 }
 
 static void cmd_dsync_wait_remote(struct dsync_cmd_context *ctx,
@@ -534,6 +532,7 @@
 	struct mail_namespace *ns;
 	const char *const *strp;
 	enum dsync_brain_flags brain_flags;
+	enum mail_error mail_error = 0, mail_error2;
 	bool remote_errors_logged = FALSE;
 	bool changes_during_sync = FALSE;
 	int status = 0, ret = 0;
@@ -605,7 +604,7 @@
 
 	if (ctx->run_type == DSYNC_RUN_TYPE_LOCAL) {
 		if (cmd_dsync_run_local(ctx, user, brain, ibc2,
-					&changes_during_sync) < 0)
+					&changes_during_sync, &mail_error) < 0)
 			ret = -1;
 	} else {
 		cmd_dsync_run_remote(user);
@@ -626,9 +625,15 @@
 		}
 		ctx->ctx.exit_code = 2;
 	}
-	if (dsync_brain_deinit(&brain) < 0) {
-		ctx->ctx.exit_code = EX_TEMPFAIL;
+	if (dsync_brain_deinit(&brain, &mail_error2) < 0)
 		ret = -1;
+	if (ret < 0) {
+		/* tempfail is the default error. prefer to use a non-tempfail
+		   if that exists. */
+		if (mail_error2 != 0 &&
+		    (mail_error == 0 || mail_error == MAIL_ERROR_TEMP))
+			mail_error = mail_error2;
+		doveadm_mail_failed_error(&ctx->ctx, mail_error);
 	}
 	dsync_ibc_deinit(&ibc);
 	if (ibc2 != NULL)
@@ -1039,6 +1044,7 @@
 	string_t *temp_prefix, *state_str = NULL;
 	enum dsync_brain_sync_type sync_type;
 	const char *name, *process_title_prefix = "";
+	enum mail_error mail_error;
 
 	if (_ctx->conn != NULL) {
 		/* doveadm-server connection. start with a success reply.
@@ -1078,8 +1084,8 @@
 	}
 	sync_type = dsync_brain_get_sync_type(brain);
 
-	if (dsync_brain_deinit(&brain) < 0)
-		_ctx->exit_code = EX_TEMPFAIL;
+	if (dsync_brain_deinit(&brain, &mail_error) < 0)
+		doveadm_mail_failed_error(_ctx, mail_error);
 	dsync_ibc_deinit(&ibc);
 
 	if (_ctx->conn != NULL) {
--- a/src/doveadm/dsync/dsync-brain-mailbox-tree-sync.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-brain-mailbox-tree-sync.c	Thu Mar 19 00:41:19 2015 +0200
@@ -8,7 +8,8 @@
 
 static int
 sync_create_box(struct dsync_brain *brain, struct mailbox *box,
-		const guid_128_t mailbox_guid, uint32_t uid_validity)
+		const guid_128_t mailbox_guid, uint32_t uid_validity,
+		enum mail_error *error_r)
 {
 	struct mailbox_metadata metadata;
 	struct mailbox_update update;
@@ -25,6 +26,7 @@
 		if (error != MAIL_ERROR_EXISTS) {
 			i_error("Can't create mailbox %s: %s",
 				mailbox_get_vname(box), errstr);
+			*error_r = error;
 			return -1;
 		}
 	}
@@ -37,7 +39,7 @@
 	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
 		i_error("Can't sync mailbox %s: %s",
 			mailbox_get_vname(box),
-			mailbox_get_last_error(box, NULL));
+			mailbox_get_last_error(box, error_r));
 		return -1;
 	}
 
@@ -50,7 +52,7 @@
 	if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) {
 		i_error("Can't get mailbox GUID %s: %s",
 			mailbox_get_vname(box),
-			mailbox_get_last_error(box, NULL));
+			mailbox_get_last_error(box, error_r));
 		return -1;
 	}
 
@@ -69,7 +71,7 @@
 		if (mailbox_update(box, &update) < 0) {
 			i_error("Can't update mailbox GUID %s: %s",
 				mailbox_get_vname(box),
-				mailbox_get_last_error(box, NULL));
+				mailbox_get_last_error(box, error_r));
 			return -1;
 		}
 		/* verify that the update worked */
@@ -77,13 +79,14 @@
 					 &metadata) < 0) {
 			i_error("Can't get mailbox GUID %s: %s",
 				mailbox_get_vname(box),
-				mailbox_get_last_error(box, NULL));
+				mailbox_get_last_error(box, error_r));
 			return -1;
 		}
 		if (memcmp(mailbox_guid, metadata.guid,
 			   sizeof(metadata.guid)) != 0) {
 			i_error("Backend didn't update mailbox %s GUID",
 				mailbox_get_vname(box));
+			*error_r = MAIL_ERROR_TEMP;
 			return -1;
 		}
 	} else if (ret < 0) {
@@ -100,7 +103,8 @@
 }
 
 int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain,
-			const struct dsync_mailbox_tree_sync_change *change)
+			const struct dsync_mailbox_tree_sync_change *change,
+			enum mail_error *error_r)
 {
 	struct mailbox *box = NULL, *destbox;
 	const char *errstr, *func_name = NULL, *storage_name;
@@ -116,7 +120,7 @@
 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX:
 		/* make sure we're deleting the correct mailbox */
 		ret = dsync_brain_mailbox_alloc(brain, change->mailbox_guid,
-						&box, &errstr);
+						&box, &errstr, error_r);
 		if (ret < 0) {
 			i_error("Mailbox sync: Couldn't allocate mailbox GUID %s: %s",
 				guid_128_to_string(change->mailbox_guid), errstr);
@@ -130,7 +134,7 @@
 					guid_128_to_string(change->mailbox_guid), errstr);
 			}
 			brain->changes_during_sync = TRUE;
-			return ret;
+			return 0;
 		}
 		break;
 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR:
@@ -153,6 +157,7 @@
 		} else {
 			i_error("Mailbox sync: mailbox_list_delete_dir failed: %s",
 				errstr);
+			*error_r = error;
 			return -1;
 		}
 	default:
@@ -162,7 +167,7 @@
 	switch (change->type) {
 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX:
 		ret = sync_create_box(brain, box, change->mailbox_guid,
-				      change->uid_validity);
+				      change->uid_validity, error_r);
 		mailbox_free(&box);
 		return ret;
 	case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR:
@@ -218,6 +223,7 @@
 		} else {
 			i_error("Mailbox %s sync: %s failed: %s",
 				mailbox_get_vname(box), func_name, errstr);
+			*error_r = error;
 		}
 	}
 	mailbox_free(&box);
--- a/src/doveadm/dsync/dsync-brain-mailbox-tree.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-brain-mailbox-tree.c	Thu Mar 19 00:41:19 2015 +0200
@@ -58,8 +58,11 @@
 		if (dsync_mailbox_tree_fill(brain->local_mailbox_tree, ns,
 					    brain->sync_box,
 					    brain->sync_box_guid,
-					    brain->exclude_mailboxes) < 0)
+					    brain->exclude_mailboxes,
+					    &brain->mail_error) < 0) {
 			brain->failed = TRUE;
+			break;
+		}
 	}
 
 	brain->local_tree_iter =
@@ -300,8 +303,11 @@
 					    brain->remote_mailbox_tree,
 					    sync_type, sync_flags);
 	while ((change = dsync_mailbox_trees_sync_next(ctx)) != NULL) {
-		if (dsync_brain_mailbox_tree_sync_change(brain, change) < 0)
+		if (dsync_brain_mailbox_tree_sync_change(brain, change,
+							 &brain->mail_error) < 0) {
 			brain->failed = TRUE;
+			break;
+		}
 	}
 	dsync_mailbox_trees_sync_deinit(&ctx);
 }
--- a/src/doveadm/dsync/dsync-brain-mailbox.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-brain-mailbox.c	Thu Mar 19 00:41:19 2015 +0200
@@ -16,12 +16,11 @@
 static int
 ns_mailbox_try_alloc(struct dsync_brain *brain, struct mail_namespace *ns,
 		     const guid_128_t guid, struct mailbox **box_r,
-		     const char **error_r)
+		     const char **errstr_r, enum mail_error *error_r)
 {
 	enum mailbox_flags flags = 0;
 	struct mailbox *box;
 	enum mailbox_existence existence;
-	enum mail_error err;
 	int ret;
 
 	if (brain->backup_send) {
@@ -32,13 +31,13 @@
 	box = mailbox_alloc_guid(ns->list, guid, flags);
 	ret = mailbox_exists(box, FALSE, &existence);
 	if (ret < 0) {
-		*error_r = mailbox_get_last_error(box, &err);
+		*errstr_r = mailbox_get_last_error(box, error_r);
 		mailbox_free(&box);
 		return -1;
 	}
 	if (existence != MAILBOX_EXISTENCE_SELECT) {
 		mailbox_free(&box);
-		*error_r = existence == MAILBOX_EXISTENCE_NONE ?
+		*errstr_r = existence == MAILBOX_EXISTENCE_NONE ?
 			"Mailbox was already deleted" :
 			"Mailbox is no longer selectable";
 		return 0;
@@ -48,7 +47,8 @@
 }
 
 int dsync_brain_mailbox_alloc(struct dsync_brain *brain, const guid_128_t guid,
-			      struct mailbox **box_r, const char **error_r)
+			      struct mailbox **box_r, const char **errstr_r,
+			      enum mail_error *error_r)
 {
 	struct mail_namespace *ns;
 	int ret;
@@ -58,11 +58,9 @@
 	for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) {
 		if (!dsync_brain_want_namespace(brain, ns))
 			continue;
-		if ((ret = ns_mailbox_try_alloc(brain, ns, guid, box_r, error_r)) != 0) {
-			if (ret < 0)
-				brain->failed = TRUE;
+		if ((ret = ns_mailbox_try_alloc(brain, ns, guid, box_r,
+						errstr_r, error_r)) != 0)
 			return ret;
-		}
 	}
 	return 0;
 }
@@ -329,6 +327,8 @@
 
 void dsync_brain_sync_mailbox_deinit(struct dsync_brain *brain)
 {
+	enum mail_error error;
+
 	i_assert(brain->box != NULL);
 
 	if (brain->require_full_resync) {
@@ -337,11 +337,13 @@
 	}
 	array_append(&brain->remote_mailbox_states, &brain->mailbox_state, 1);
 	if (brain->box_exporter != NULL) {
-		const char *error;
+		const char *errstr;
 
 		i_assert(brain->failed ||
 			 brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_CHANGED);
-		(void)dsync_mailbox_export_deinit(&brain->box_exporter, &error);
+		if (dsync_mailbox_export_deinit(&brain->box_exporter,
+						&errstr, &error) < 0)
+			i_error("Mailbox export failed: %s", errstr);
 	}
 	if (brain->box_importer != NULL) {
 		uint32_t last_common_uid, last_messages_count;
@@ -355,7 +357,8 @@
 						  &last_common_modseq,
 						  &last_common_pvt_modseq,
 						  &last_messages_count,
-						  &changes_during_sync);
+						  &changes_during_sync,
+						  &brain->mail_error);
 	}
 	if (brain->log_scan != NULL)
 		dsync_transaction_log_scan_deinit(&brain->log_scan);
@@ -364,7 +367,8 @@
 	brain->state = brain->pre_box_state;
 }
 
-static int dsync_box_get(struct mailbox *box, struct dsync_mailbox *dsync_box_r)
+static int dsync_box_get(struct mailbox *box, struct dsync_mailbox *dsync_box_r,
+			 enum mail_error *error_r)
 {
 	const enum mailbox_status_items status_items =
 		STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES |
@@ -391,6 +395,7 @@
 		}
 		i_error("Failed to access mailbox %s: %s",
 			mailbox_get_vname(box), errstr);
+		*error_r = error;
 		return -1;
 	}
 
@@ -441,6 +446,7 @@
 	struct mailbox *box;
 	struct dsync_mailbox_node *node;
 	const char *vname = NULL;
+	enum mail_error error;
 	bool synced = FALSE;
 	int ret;
 
@@ -464,9 +470,11 @@
 	}
 	box = mailbox_alloc(node->ns->list, vname, flags);
 	for (;;) {
-		if ((ret = dsync_box_get(box, &dsync_box)) <= 0) {
-			if (ret < 0)
+		if ((ret = dsync_box_get(box, &dsync_box, &error)) <= 0) {
+			if (ret < 0) {
+				brain->mail_error = error;
 				brain->failed = TRUE;
+			}
 			mailbox_free(&box);
 			return ret;
 		}
@@ -498,7 +506,7 @@
 		if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
 			i_error("Can't sync mailbox %s: %s",
 				mailbox_get_vname(box),
-				mailbox_get_last_error(box, NULL));
+				mailbox_get_last_error(box, &brain->mail_error));
 			brain->failed = TRUE;
 			mailbox_free(&box);
 			return -1;
@@ -682,7 +690,7 @@
 	if (mailbox_update(box, &update) < 0) {
 		i_error("Couldn't update mailbox %s metadata: %s",
 			mailbox_get_vname(box),
-			mailbox_get_last_error(box, NULL));
+			mailbox_get_last_error(box, &brain->mail_error));
 		brain->failed = TRUE;
 	}
 	return ret;
@@ -712,7 +720,8 @@
 	const struct dsync_mailbox *dsync_box;
 	struct dsync_mailbox local_dsync_box;
 	struct mailbox *box;
-	const char *error;
+	const char *errstr;
+	enum mail_error error;
 	int ret;
 	bool resync;
 
@@ -727,10 +736,11 @@
 	}
 
 	if (dsync_brain_mailbox_alloc(brain, dsync_box->mailbox_guid,
-				      &box, &error) < 0) {
+				      &box, &errstr, &error) < 0) {
 		i_error("Couldn't allocate mailbox GUID %s: %s",
-			guid_128_to_string(dsync_box->mailbox_guid), error);
-		i_assert(brain->failed);
+			guid_128_to_string(dsync_box->mailbox_guid), errstr);
+		brain->mail_error = error;
+		brain->failed = TRUE;
 		return TRUE;
 	}
 	if (box == NULL) {
@@ -759,15 +769,16 @@
 	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
 		i_error("Can't sync mailbox %s: %s",
 			mailbox_get_vname(box),
-			mailbox_get_last_error(box, NULL));
+			mailbox_get_last_error(box, &brain->mail_error));
 		mailbox_free(&box);
 		brain->failed = TRUE;
 		return TRUE;
 	}
 
-	if ((ret = dsync_box_get(box, &local_dsync_box)) <= 0) {
+	if ((ret = dsync_box_get(box, &local_dsync_box, &error)) <= 0) {
 		mailbox_free(&box);
 		if (ret < 0) {
+			brain->mail_error = error;
 			brain->failed = TRUE;
 			return TRUE;
 		}
--- a/src/doveadm/dsync/dsync-brain-mails.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-brain-mails.c	Thu Mar 19 00:41:19 2015 +0200
@@ -183,7 +183,8 @@
 static void dsync_brain_sync_half_finished(struct dsync_brain *brain)
 {
 	struct dsync_mailbox_state state;
-	const char *error;
+	const char *errstr;
+	enum mail_error error;
 
 	if (brain->box_recv_state < DSYNC_BOX_STATE_RECV_LAST_COMMON ||
 	    brain->box_send_state < DSYNC_BOX_STATE_RECV_LAST_COMMON)
@@ -192,9 +193,10 @@
 	/* finished with this mailbox */
 	if (brain->box_exporter != NULL) {
 		if (dsync_mailbox_export_deinit(&brain->box_exporter,
-						&error) < 0) {
+						&errstr, &error) < 0) {
 			i_error("Exporting mailbox %s failed: %s",
-				mailbox_get_vname(brain->box), error);
+				mailbox_get_vname(brain->box), errstr);
+			brain->mail_error = error;
 			brain->failed = TRUE;
 			return;
 		}
@@ -219,7 +221,8 @@
 						&state.last_common_modseq,
 						&state.last_common_pvt_modseq,
 						&state.last_messages_count,
-						&state.changes_during_sync) < 0) {
+						&state.changes_during_sync,
+						&brain->mail_error) < 0) {
 			brain->failed = TRUE;
 			return;
 		}
--- a/src/doveadm/dsync/dsync-brain-private.h	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-brain-private.h	Thu Mar 19 00:41:19 2015 +0200
@@ -95,6 +95,8 @@
 	/* new states for synced mailboxes */
 	ARRAY_TYPE(dsync_mailbox_state) remote_mailbox_states;
 
+	enum mail_error mail_error;
+
 	unsigned int master_brain:1;
 	unsigned int mail_requests:1;
 	unsigned int backup_send:1;
@@ -119,11 +121,13 @@
 bool dsync_brain_recv_mailbox_tree(struct dsync_brain *brain);
 bool dsync_brain_recv_mailbox_tree_deletes(struct dsync_brain *brain);
 int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain,
-			const struct dsync_mailbox_tree_sync_change *change);
+			const struct dsync_mailbox_tree_sync_change *change,
+			enum mail_error *error_r);
 
 void dsync_brain_sync_mailbox_deinit(struct dsync_brain *brain);
 int dsync_brain_mailbox_alloc(struct dsync_brain *brain, const guid_128_t guid,
-			      struct mailbox **box_r, const char **error_r);
+			      struct mailbox **box_r, const char **errstr_r,
+			      enum mail_error *error_r);
 bool dsync_brain_mailbox_update_pre(struct dsync_brain *brain,
 				    struct mailbox *box,
 				    const struct dsync_mailbox *local_box,
--- a/src/doveadm/dsync/dsync-brain.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-brain.c	Thu Mar 19 00:41:19 2015 +0200
@@ -288,7 +288,7 @@
 	}
 }
 
-int dsync_brain_deinit(struct dsync_brain **_brain)
+int dsync_brain_deinit(struct dsync_brain **_brain, enum mail_error *error_r)
 {
 	struct dsync_brain *brain = *_brain;
 	int ret;
@@ -338,6 +338,9 @@
 
 	ret = brain->failed ? -1 : 0;
 	mail_user_unref(&brain->user);
+
+	*error_r = !brain->failed ? 0 :
+		(brain->mail_error == 0 ? MAIL_ERROR_TEMP : brain->mail_error);
 	pool_unref(&brain->pool);
 	return ret;
 }
@@ -558,18 +561,25 @@
 static bool dsync_brain_finish(struct dsync_brain *brain)
 {
 	const char *error;
+	enum mail_error mail_error;
+	enum dsync_ibc_recv_ret ret;
 
 	if (!brain->master_brain) {
 		dsync_ibc_send_finish(brain->ibc,
-				      brain->failed ? "dsync failed" : NULL);
+				      brain->failed ? "dsync failed" : NULL,
+				      brain->mail_error);
 		brain->state = DSYNC_STATE_DONE;
 		return TRUE;
 	} 
-	if (dsync_ibc_recv_finish(brain->ibc, &error) == DSYNC_IBC_RECV_RET_TRYAGAIN)
+	ret = dsync_ibc_recv_finish(brain->ibc, &error, &mail_error);
+	if (ret == DSYNC_IBC_RECV_RET_TRYAGAIN)
 		return FALSE;
 	if (error != NULL) {
 		i_error("Remote dsync failed: %s", error);
 		brain->failed = TRUE;
+		if (mail_error != 0 &&
+		    (brain->mail_error == 0 || brain->mail_error == MAIL_ERROR_TEMP))
+			brain->mail_error = mail_error;
 	}
 	brain->state = DSYNC_STATE_DONE;
 	return TRUE;
--- a/src/doveadm/dsync/dsync-brain.h	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-brain.h	Thu Mar 19 00:41:19 2015 +0200
@@ -2,6 +2,7 @@
 #define DSYNC_BRAIN_H
 
 #include "guid.h"
+#include "mail-error.h"
 
 struct mail_namespace;
 struct mail_user;
@@ -80,7 +81,7 @@
 dsync_brain_slave_init(struct mail_user *user, struct dsync_ibc *ibc,
 		       bool local, const char *process_title_prefix);
 /* Returns 0 if everything was successful, -1 if syncing failed in some way */
-int dsync_brain_deinit(struct dsync_brain **brain);
+int dsync_brain_deinit(struct dsync_brain **brain, enum mail_error *error_r);
 
 /* Returns TRUE if brain needs to run more, FALSE if it's finished.
    changed_r is TRUE if anything happened during this run. */
--- a/src/doveadm/dsync/dsync-ibc-pipe.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-ibc-pipe.c	Thu Mar 19 00:41:19 2015 +0200
@@ -42,7 +42,10 @@
 			unsigned int count;
 			char hierarchy_sep;
 		} mailbox_delete;
-		const char *finish_error;
+		struct {
+			const char *error;
+			enum mail_error mail_error;
+		} finish;
 	} u;
 };
 
@@ -487,17 +490,20 @@
 }
 
 static void
-dsync_ibc_pipe_send_finish(struct dsync_ibc *ibc, const char *error)
+dsync_ibc_pipe_send_finish(struct dsync_ibc *ibc, const char *error,
+			   enum mail_error mail_error)
 {
 	struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc;
 	struct item *item;
 
 	item = dsync_ibc_pipe_push_item(pipe->remote, ITEM_FINISH);
-	item->u.finish_error = p_strdup(item->pool, error);
+	item->u.finish.error = p_strdup(item->pool, error);
+	item->u.finish.mail_error = mail_error;
 }
 
 static enum dsync_ibc_recv_ret
-dsync_ibc_pipe_recv_finish(struct dsync_ibc *ibc, const char **error_r)
+dsync_ibc_pipe_recv_finish(struct dsync_ibc *ibc, const char **error_r,
+			   enum mail_error *mail_error_r)
 {
 	struct dsync_ibc_pipe *pipe = (struct dsync_ibc_pipe *)ibc;
 	struct item *item;
@@ -506,7 +512,8 @@
 	if (item == NULL)
 		return DSYNC_IBC_RECV_RET_TRYAGAIN;
 
-	*error_r = item->u.finish_error;
+	*error_r = item->u.finish.error;
+	*mail_error_r = item->u.finish.mail_error;
 	return DSYNC_IBC_RECV_RET_OK;
 }
 
--- a/src/doveadm/dsync/dsync-ibc-private.h	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-ibc-private.h	Thu Mar 19 00:41:19 2015 +0200
@@ -68,9 +68,11 @@
 		(*recv_mail)(struct dsync_ibc *ibc,
 			     struct dsync_mail **mail_r);
 
-	void (*send_finish)(struct dsync_ibc *ibc, const char *error);
+	void (*send_finish)(struct dsync_ibc *ibc, const char *error,
+			    enum mail_error mail_error);
 	enum dsync_ibc_recv_ret
-		(*recv_finish)(struct dsync_ibc *ibc, const char **error_r);
+		(*recv_finish)(struct dsync_ibc *ibc, const char **error_r,
+			       enum mail_error *mail_error_r);
 
 	void (*close_mail_streams)(struct dsync_ibc *ibc);
 	bool (*is_send_queue_full)(struct dsync_ibc *ibc);
--- a/src/doveadm/dsync/dsync-ibc-stream.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-ibc-stream.c	Thu Mar 19 00:41:19 2015 +0200
@@ -126,7 +126,7 @@
 	},
 	{ .name = "finish",
 	  .chr = 'F',
-	  .optional_keys = "error"
+	  .optional_keys = "error mail_error"
 	},
 	{ .name = "mailbox_cache_field",
 	  .chr = 'c',
@@ -1827,7 +1827,8 @@
 }
 
 static void
-dsync_ibc_stream_send_finish(struct dsync_ibc *_ibc, const char *error)
+dsync_ibc_stream_send_finish(struct dsync_ibc *_ibc, const char *error,
+			     enum mail_error mail_error)
 {
 	struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc;
 	struct dsync_serializer_encoder *encoder;
@@ -1837,19 +1838,27 @@
 	encoder = dsync_serializer_encode_begin(ibc->serializers[ITEM_FINISH]);
 	if (error != NULL)
 		dsync_serializer_encode_add(encoder, "error", error);
+	if (mail_error != 0) {
+		dsync_serializer_encode_add(encoder, "mail_error",
+					    dec2str(mail_error));
+	}
 	dsync_serializer_encode_finish(&encoder, str);
 	dsync_ibc_stream_send_string(ibc, str);
 }
 
 static enum dsync_ibc_recv_ret
-dsync_ibc_stream_recv_finish(struct dsync_ibc *_ibc, const char **error_r)
+dsync_ibc_stream_recv_finish(struct dsync_ibc *_ibc, const char **error_r,
+			     enum mail_error *mail_error_r)
 {
 	struct dsync_ibc_stream *ibc = (struct dsync_ibc_stream *)_ibc;
 	struct dsync_deserializer_decoder *decoder;
 	const char *value;
 	enum dsync_ibc_recv_ret ret;
+	int i;
 
 	*error_r = NULL;
+	*mail_error_r = 0;
+
 	p_clear(ibc->ret_pool);
 
 	if (ibc->minor_version < DSYNC_PROTOCOL_MINOR_HAVE_FINISH)
@@ -1861,6 +1870,12 @@
 
 	if (dsync_deserializer_decode_try(decoder, "error", &value))
 		*error_r = p_strdup(ibc->ret_pool, value);
+	if (dsync_deserializer_decode_try(decoder, "mail_error", &value) &&
+	    str_to_int(value, &i) < 0) {
+		dsync_ibc_input_error(ibc, decoder, "Invalid mail_error");
+		return DSYNC_IBC_RECV_RET_TRYAGAIN;
+	}
+	*mail_error_r = i;
 	return DSYNC_IBC_RECV_RET_OK;
 }
 
--- a/src/doveadm/dsync/dsync-ibc.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-ibc.c	Thu Mar 19 00:41:19 2015 +0200
@@ -195,15 +195,17 @@
 	return ibc->v.recv_mail(ibc, mail_r);
 }
 
-void dsync_ibc_send_finish(struct dsync_ibc *ibc, const char *error)
+void dsync_ibc_send_finish(struct dsync_ibc *ibc, const char *error,
+			   enum mail_error mail_error)
 {
-	ibc->v.send_finish(ibc, error);
+	ibc->v.send_finish(ibc, error, mail_error);
 }
 
 enum dsync_ibc_recv_ret
-dsync_ibc_recv_finish(struct dsync_ibc *ibc, const char **error_r)
+dsync_ibc_recv_finish(struct dsync_ibc *ibc, const char **error_r,
+		      enum mail_error *mail_error_r)
 {
-	return ibc->v.recv_finish(ibc, error_r);
+	return ibc->v.recv_finish(ibc, error_r, mail_error_r);
 }
 
 void dsync_ibc_close_mail_streams(struct dsync_ibc *ibc)
--- a/src/doveadm/dsync/dsync-ibc.h	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-ibc.h	Thu Mar 19 00:41:19 2015 +0200
@@ -5,6 +5,7 @@
 
 #include "ioloop.h"
 #include "guid.h"
+#include "mail-error.h"
 #include "dsync-brain.h"
 
 struct dsync_mailbox;
@@ -144,9 +145,11 @@
 enum dsync_ibc_recv_ret
 dsync_ibc_recv_mail(struct dsync_ibc *ibc, struct dsync_mail **mail_r);
 
-void dsync_ibc_send_finish(struct dsync_ibc *ibc, const char *error);
+void dsync_ibc_send_finish(struct dsync_ibc *ibc, const char *error,
+			   enum mail_error mail_error);
 enum dsync_ibc_recv_ret
-dsync_ibc_recv_finish(struct dsync_ibc *ibc, const char **error_r);
+dsync_ibc_recv_finish(struct dsync_ibc *ibc, const char **error_r,
+		      enum mail_error *mail_error_r);
 
 /* Close any mail input streams that are kept open. This needs to be called
    before the mail is attempted to be freed (usually on error conditions). */
--- a/src/doveadm/dsync/dsync-mailbox-export.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-export.c	Thu Mar 19 00:41:19 2015 +0200
@@ -54,6 +54,8 @@
 	struct dsync_mail dsync_mail;
 
 	const char *error;
+	enum mail_error mail_error;
+
 	unsigned int body_search_initialized:1;
 	unsigned int auto_export_mails:1;
 	unsigned int mails_have_guids:1;
@@ -72,6 +74,7 @@
 	if (error == MAIL_ERROR_EXPUNGED)
 		return 0;
 
+	exporter->mail_error = error;
 	exporter->error = p_strdup_printf(exporter->pool,
 		"Can't lookup %s for UID=%u: %s",
 		field, mail->uid, errstr);
@@ -163,6 +166,7 @@
 			return dsync_mail_error(exporter, mail, "hdr-stream");
 		return 1;
 	} else if (**guid_r == '\0') {
+		exporter->mail_error = MAIL_ERROR_TEMP;
 		exporter->error = "Backend doesn't support GUIDs, "
 			"sync with header hashes instead";
 		return -1;
@@ -403,7 +407,8 @@
 	    exporter->error == NULL) {
 		exporter->error = p_strdup_printf(exporter->pool,
 			"Mail search failed: %s",
-			mailbox_get_last_error(exporter->box, NULL));
+			mailbox_get_last_error(exporter->box,
+					       &exporter->mail_error));
 	}
 }
 
@@ -534,7 +539,8 @@
 						 attr->key, &value) < 0) {
 			exporter->error = p_strdup_printf(exporter->pool,
 				"Mailbox attribute %s lookup failed: %s", attr->key,
-				mailbox_get_last_error(exporter->box, NULL));
+				mailbox_get_last_error(exporter->box,
+						       &exporter->mail_error));
 			break;
 		}
 		if ((value.flags & MAIL_ATTRIBUTE_VALUE_FLAG_READONLY) != 0) {
@@ -586,7 +592,8 @@
 						 &value) < 0) {
 			exporter->error = p_strdup_printf(exporter->pool,
 				"Mailbox attribute %s lookup failed: %s", key,
-				mailbox_get_last_error(exporter->box, NULL));
+				mailbox_get_last_error(exporter->box,
+						       &exporter->mail_error));
 			return -1;
 		}
 		if ((value.flags & MAIL_ATTRIBUTE_VALUE_FLAG_READONLY) != 0) {
@@ -624,7 +631,8 @@
 	if (mailbox_attribute_iter_deinit(&exporter->attr_iter) < 0) {
 		exporter->error = p_strdup_printf(exporter->pool,
 			"Mailbox attribute iteration failed: %s",
-			mailbox_get_last_error(exporter->box, NULL));
+			mailbox_get_last_error(exporter->box,
+					       &exporter->mail_error));
 		return -1;
 	}
 	if (exporter->attr_type == MAIL_ATTRIBUTE_TYPE_PRIVATE) {
@@ -762,7 +770,8 @@
 	    exporter->error == NULL) {
 		exporter->error = p_strdup_printf(exporter->pool,
 			"Mail search failed: %s",
-			mailbox_get_last_error(exporter->box, NULL));
+			mailbox_get_last_error(exporter->box,
+					       &exporter->mail_error));
 	}
 }
 
@@ -784,6 +793,7 @@
 	} else if (exporter->dsync_mail.uid != 0) {
 		/* mail requested by UID */
 	} else {
+		exporter->mail_error = MAIL_ERROR_TEMP;
 		exporter->error = p_strdup_printf(exporter->pool,
 			"GUID unexpectedly changed for UID=%u GUID=%s",
 			mail->uid, exporter->dsync_mail.guid);
@@ -816,6 +826,7 @@
 
 	instances = hash_table_lookup(exporter->export_guids, request->guid);
 	if (instances == NULL) {
+		exporter->mail_error = MAIL_ERROR_TEMP;
 		exporter->error = p_strdup_printf(exporter->pool,
 			"Remote requested unexpected GUID %s", request->guid);
 		return;
@@ -878,7 +889,7 @@
 }
 
 int dsync_mailbox_export_deinit(struct dsync_mailbox_exporter **_exporter,
-				const char **error_r)
+				const char **errstr_r, enum mail_error *error_r)
 {
 	struct dsync_mailbox_exporter *exporter = *_exporter;
 
@@ -894,9 +905,12 @@
 	hash_table_destroy(&exporter->export_guids);
 	hash_table_destroy(&exporter->changes);
 
-	*error_r = t_strdup(exporter->error);
+	i_assert((exporter->error != NULL) == (exporter->mail_error != 0));
+
+	*error_r = exporter->mail_error;
+	*errstr_r = t_strdup(exporter->error);
 	pool_unref(&exporter->pool);
-	return *error_r != NULL ? -1 : 0;
+	return *errstr_r != NULL ? -1 : 0;
 }
 
 const char *dsync_mailbox_export_get_proctitle(struct dsync_mailbox_exporter *exporter)
--- a/src/doveadm/dsync/dsync-mailbox-export.h	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-export.h	Thu Mar 19 00:41:19 2015 +0200
@@ -23,7 +23,7 @@
 const struct dsync_mail *
 dsync_mailbox_export_next_mail(struct dsync_mailbox_exporter *exporter);
 int dsync_mailbox_export_deinit(struct dsync_mailbox_exporter **exporter,
-				const char **error_r);
+				const char **errstr_r, enum mail_error *error_r);
 
 const char *dsync_mailbox_export_get_proctitle(struct dsync_mailbox_exporter *exporter);
 
--- a/src/doveadm/dsync/dsync-mailbox-import.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-import.c	Thu Mar 19 00:41:19 2015 +0200
@@ -103,6 +103,8 @@
 	uint64_t local_initial_highestmodseq, local_initial_highestpvtmodseq;
 	unsigned int import_pos, import_count;
 
+	enum mail_error mail_error;
+
 	unsigned int failed:1;
 	unsigned int debug:1;
 	unsigned int stateful_import:1;
@@ -277,7 +279,7 @@
 	if (mailbox_attribute_get_stream(importer->trans, type, key, &value) < 0) {
 		i_error("Mailbox %s: Failed to get attribute %s: %s",
 			mailbox_get_vname(importer->box), key,
-			mailbox_get_last_error(importer->box, NULL));
+			mailbox_get_last_error(importer->box, &importer->mail_error));
 		importer->failed = TRUE;
 		return -1;
 	}
@@ -448,6 +450,7 @@
 		   they are the same, fallback to just picking one based on the
 		   value. */
 		if (dsync_attributes_cmp(attr, local_attr, &cmp) < 0) {
+			importer->mail_error = MAIL_ERROR_TEMP;
 			importer->failed = TRUE;
 			return -1;
 		}
@@ -485,7 +488,7 @@
 				  attr->key, &value) < 0) {
 		i_error("Mailbox %s: Failed to set attribute %s: %s",
 			mailbox_get_vname(importer->box), attr->key,
-			mailbox_get_last_error(importer->box, NULL));
+			mailbox_get_last_error(importer->box, &importer->mail_error));
 		/* the attributes aren't vital, don't fail everything just
 		   because of them. */
 	}
@@ -518,6 +521,7 @@
 
 	i_error("Mailbox %s: Can't lookup %s for UID=%u: %s",
 		mailbox_get_vname(mail->box), field, mail->uid, errstr);
+	importer->mail_error = error;
 	importer->failed = TRUE;
 }
 
@@ -833,6 +837,7 @@
 		dsync_import_unexpected_state(importer, t_strdup_printf(
 			"Unexpected GUID mismatch for UID=%u: %s != %s",
 			change->uid, guid, cmp_guid));
+		importer->mail_error = MAIL_ERROR_TEMP;
 		importer->failed = TRUE;
 		return FALSE;
 	}
@@ -852,6 +857,7 @@
 		dsync_import_unexpected_state(importer, t_strdup_printf(
 			"Unexpected GUID mismatch (2) for UID=%u: %s != %s",
 			change->uid, importer->cur_guid, cmp_guid));
+		importer->mail_error = MAIL_ERROR_TEMP;
 		importer->failed = TRUE;
 		return FALSE;
 	}
@@ -1466,6 +1472,7 @@
 		i_error("Mailbox %s: GUIDs not supported, "
 			"sync with header hashes instead",
 			mailbox_get_vname(importer->box));
+		importer->mail_error = MAIL_ERROR_TEMP;
 		importer->failed = TRUE;
 		*result_r = "Error, invalid parameters";
 		return -1;
@@ -1543,6 +1550,7 @@
 		  mailbox_get_vname(importer->box),
 		  change->uid, change->guid);
 	importer->delete_mailbox = TRUE;
+	importer->mail_error = MAIL_ERROR_TEMP;
 	importer->failed = TRUE;
 }
 
@@ -1754,6 +1762,8 @@
 	if (!mail_set_uid(mail, uid))
 		return 0;
 
+	/* NOTE: Errors are logged, but they don't cause the entire import
+	   to fail. */
 	if (dsync_mail_fill(mail, TRUE, dmail_r, &error_field) < 0) {
 		errstr = mailbox_get_last_error(mail->box, &error);
 		if (error == MAIL_ERROR_EXPUNGED)
@@ -2086,7 +2096,8 @@
 		if (mailbox_search_deinit(&importer->search_ctx) < 0) {
 			i_error("Mailbox %s: Search failed: %s",
 				mailbox_get_vname(importer->box),
-				mailbox_get_last_error(importer->box, NULL));
+				mailbox_get_last_error(importer->box,
+						       &importer->mail_error));
 			importer->failed = TRUE;
 		}
 	}
@@ -2267,7 +2278,8 @@
 			i_error("Mailbox %s: Failed to read mail %s uid=%u: %s",
 				mailbox_get_vname(importer->box),
 				error_field, mail->uid,
-				mailbox_get_last_error(importer->box, NULL));
+				mailbox_get_last_error(importer->box,
+						       &importer->mail_error));
 			importer->failed = TRUE;
 			mailbox_save_cancel(&save_ctx);
 			return TRUE;
@@ -2288,7 +2300,8 @@
 	if (mailbox_save_begin(&save_ctx, input) < 0) {
 		i_error("Mailbox %s: Saving failed: %s",
 			mailbox_get_vname(importer->box),
-			mailbox_get_last_error(importer->box, NULL));
+			mailbox_get_last_error(importer->box,
+					       &importer->mail_error));
 		importer->failed = TRUE;
 		return TRUE;
 	}
@@ -2306,11 +2319,13 @@
 			mailbox_get_vname(importer->box),
 			i_stream_get_error(input));
 		mailbox_save_cancel(&save_ctx);
+		importer->mail_error = MAIL_ERROR_TEMP;
 		importer->failed = TRUE;
 	} else if (save_failed) {
 		i_error("Mailbox %s: Saving failed: %s",
 			mailbox_get_vname(importer->box),
-			mailbox_get_last_error(importer->box, NULL));
+			mailbox_get_last_error(importer->box,
+					       &importer->mail_error));
 		mailbox_save_cancel(&save_ctx);
 		importer->failed = TRUE;
 	} else {
@@ -2318,7 +2333,8 @@
 		if (mailbox_save_finish(&save_ctx) < 0) {
 			i_error("Mailbox %s: Saving failed: %s",
 				mailbox_get_vname(importer->box),
-				mailbox_get_last_error(importer->box, NULL));
+				mailbox_get_last_error(importer->box,
+						       &importer->mail_error));
 			importer->failed = TRUE;
 		} else {
 			dsync_mailbox_import_saved_uid(importer,
@@ -2434,7 +2450,7 @@
 		if (mailbox_move(&save_ctx, mail) < 0) {
 			i_error("Mailbox %s: Couldn't move mail within mailbox: %s",
 				mailbox_get_vname(box),
-				mailbox_get_last_error(box, NULL));
+				mailbox_get_last_error(box, &importer->mail_error));
 			ret = -1;
 		} else if (ret > 0) {
 			ret = 0;
@@ -2444,14 +2460,14 @@
 	if (mailbox_search_deinit(&search_ctx) < 0) {
 		i_error("Mailbox %s: mail search failed: %s",
 			mailbox_get_vname(box),
-			mailbox_get_last_error(box, NULL));
+			mailbox_get_last_error(box, &importer->mail_error));
 		ret = -1;
 	}
 
 	if (mailbox_transaction_commit(&trans) < 0) {
 		i_error("Mailbox %s: UID reassign commit failed: %s",
 			mailbox_get_vname(box),
-			mailbox_get_last_error(box, NULL));
+			mailbox_get_last_error(box, &importer->mail_error));
 		ret = -1;
 	}
 	if (ret == 0) {
@@ -2534,7 +2550,7 @@
 						   &changes) < 0) {
 		i_error("Mailbox %s: Save commit failed: %s",
 			mailbox_get_vname(importer->box),
-			mailbox_get_last_error(importer->box, NULL));
+			mailbox_get_last_error(importer->box, &importer->mail_error));
 		/* removed wanted_uids that weren't actually saved */
 		array_delete(&importer->wanted_uids,
 			     array_count(&importer->saved_uids),
@@ -2558,7 +2574,8 @@
 		if (mailbox_transaction_commit(&importer->trans) < 0) {
 			i_error("Mailbox %s: Commit failed: %s",
 				mailbox_get_vname(importer->box),
-				mailbox_get_last_error(importer->box, NULL));
+				mailbox_get_last_error(importer->box,
+						       &importer->mail_error));
 			ret = -1;
 		}
 	}
@@ -2597,7 +2614,8 @@
 		if (mailbox_update(importer->box, &update) < 0) {
 			i_error("Mailbox %s: Update failed: %s",
 				mailbox_get_vname(importer->box),
-				mailbox_get_last_error(importer->box, NULL));
+				mailbox_get_last_error(importer->box,
+						       &importer->mail_error));
 			ret = -1;
 		}
 	}
@@ -2606,7 +2624,8 @@
 	if (mailbox_sync(importer->box, 0) < 0) {
 		i_error("Mailbox %s: Sync failed: %s",
 			mailbox_get_vname(importer->box),
-			mailbox_get_last_error(importer->box, NULL));
+			mailbox_get_last_error(importer->box,
+					       &importer->mail_error));
 		ret = -1;
 	}
 	if (ret == 0) {
@@ -2634,6 +2653,7 @@
 			i_error("Mailbox %s: Remote didn't send mail GUID=%s (UID=%u)",
 				mailbox_get_vname(importer->box),
 				mail->guid, mail->remote_uid);
+			importer->mail_error = MAIL_ERROR_TEMP;
 			importer->failed = TRUE;
 		}
 	}
@@ -2656,6 +2676,7 @@
 			i_error("Mailbox %s: Remote didn't send mail UID=%u",
 				mailbox_get_vname(importer->box),
 				mail->remote_uid);
+			importer->mail_error = MAIL_ERROR_TEMP;
 			importer->failed = TRUE;
 		}
 	}
@@ -2668,7 +2689,8 @@
 				uint64_t *last_common_modseq_r,
 				uint64_t *last_common_pvt_modseq_r,
 				uint32_t *last_messages_count_r,
-				bool *changes_during_sync_r)
+				bool *changes_during_sync_r,
+				enum mail_error *error_r)
 {
 	struct dsync_mailbox_importer *importer = *_importer;
 	struct mailbox_status status;
@@ -2677,8 +2699,10 @@
 	*_importer = NULL;
 	*changes_during_sync_r = FALSE;
 
-	if (!success)
+	if (!success && !importer->failed) {
+		importer->mail_error = MAIL_ERROR_TEMP;
 		importer->failed = TRUE;
+	}
 
 	if (!importer->new_uids_assigned && !importer->failed)
 		dsync_mailbox_import_assign_new_uids(importer);
@@ -2692,7 +2716,8 @@
 		if (mailbox_search_deinit(&importer->search_ctx) < 0) {
 			i_error("Mailbox %s: Search failed: %s",
 				mailbox_get_vname(importer->box),
-				mailbox_get_last_error(importer->box, NULL));
+				mailbox_get_last_error(importer->box,
+						       &importer->mail_error));
 			importer->failed = TRUE;
 		}
 	}
@@ -2730,7 +2755,8 @@
 		if (mailbox_delete(importer->box) < 0) {
 			i_error("Couldn't delete mailbox %s: %s",
 				mailbox_get_vname(importer->box),
-				mailbox_get_last_error(importer->box, NULL));
+				mailbox_get_last_error(importer->box,
+						       &importer->mail_error));
 			importer->failed = TRUE;
 		}
 		*last_messages_count_r = 0;
@@ -2739,7 +2765,9 @@
 		*last_messages_count_r = status.messages;
 	}
 
+	i_assert(importer->failed == (importer->mail_error != 0));
 	ret = importer->failed ? -1 : 0;
+	*error_r = importer->mail_error;
 	pool_unref(&importer->pool);
 	return ret;
 }
--- a/src/doveadm/dsync/dsync-mailbox-import.h	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-import.h	Thu Mar 19 00:41:19 2015 +0200
@@ -1,6 +1,8 @@
 #ifndef DSYNC_MAILBOX_IMPORT_H
 #define DSYNC_MAILBOX_IMPORT_H
 
+#include "mail-error.h"
+
 enum dsync_mailbox_import_flags {
 	DSYNC_MAILBOX_IMPORT_FLAG_MASTER_BRAIN		= 0x01,
 	DSYNC_MAILBOX_IMPORT_FLAG_WANT_MAIL_REQUESTS	= 0x02,
@@ -44,7 +46,8 @@
 				uint64_t *last_common_modseq_r,
 				uint64_t *last_common_pvt_modseq_r,
 				uint32_t *last_messages_count_r,
-				bool *changes_during_sync_r);
+				bool *changes_during_sync_r,
+				enum mail_error *error_r);
 
 const char *dsync_mailbox_import_get_proctitle(struct dsync_mailbox_importer *importer);
 
--- a/src/doveadm/dsync/dsync-mailbox-tree-fill.c	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-tree-fill.c	Thu Mar 19 00:41:19 2015 +0200
@@ -37,10 +37,13 @@
 static int
 dsync_mailbox_tree_add_exists_node(struct dsync_mailbox_tree *tree,
 				   const struct mailbox_info *info,
-				   struct dsync_mailbox_node **node_r)
+				   struct dsync_mailbox_node **node_r,
+				   enum mail_error *error_r)
 {
-	if (dsync_mailbox_tree_add_node(tree, info, node_r) < 0)
+	if (dsync_mailbox_tree_add_node(tree, info, node_r) < 0) {
+		*error_r = MAIL_ERROR_TEMP;
 		return -1;
+	}
 	(*node_r)->existence = DSYNC_MAILBOX_NODE_EXISTS;
 	return 0;
 }
@@ -71,7 +74,8 @@
 
 static int dsync_mailbox_tree_add(struct dsync_mailbox_tree *tree,
 				  const struct mailbox_info *info,
-				  const guid_128_t box_guid)
+				  const guid_128_t box_guid,
+				  enum mail_error *error_r)
 {
 	struct dsync_mailbox_node *node;
 	struct mailbox *box;
@@ -85,7 +89,7 @@
 		return 0;
 	if ((info->flags & MAILBOX_NOSELECT) != 0) {
 		return !guid_128_is_empty(box_guid) ? 0 :
-			dsync_mailbox_tree_add_exists_node(tree, info, &node);
+			dsync_mailbox_tree_add_exists_node(tree, info, &node, error_r);
 	}
 
 	/* get GUID and UIDVALIDITY for selectable mailbox */
@@ -102,6 +106,7 @@
 		default:
 			i_error("Failed to access mailbox %s: %s",
 				info->vname, errstr);
+			*error_r = error;
 			ret = -1;
 		}
 		mailbox_free(&box);
@@ -114,7 +119,7 @@
 		/* unwanted mailbox */
 		return 0;
 	}
-	if (dsync_mailbox_tree_add_exists_node(tree, info, &node) < 0)
+	if (dsync_mailbox_tree_add_exists_node(tree, info, &node, error_r) < 0)
 		return -1;
 	memcpy(node->mailbox_guid, metadata.guid,
 	       sizeof(node->mailbox_guid));
@@ -313,7 +318,8 @@
 int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree,
 			    struct mail_namespace *ns, const char *box_name,
 			    const guid_128_t box_guid,
-			    const char *const *exclude_mailboxes)
+			    const char *const *exclude_mailboxes,
+			    enum mail_error *error_r)
 {
 	const enum mailbox_list_iter_flags list_flags =
 		/* FIXME: we'll skip symlinks, because we can't handle them
@@ -348,25 +354,29 @@
 	while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
 		if (dsync_mailbox_info_is_wanted(info, box_name,
 						 exclude_mailboxes)) {
-			if (dsync_mailbox_tree_add(tree, info, box_guid) < 0)
+			if (dsync_mailbox_tree_add(tree, info, box_guid, error_r) < 0)
 				ret = -1;
 		}
 	} T_END;
 	if (mailbox_list_iter_deinit(&iter) < 0) {
-		i_error("Mailbox listing for namespace '%s' failed", ns->prefix);
+		i_error("Mailbox listing for namespace '%s' failed: %s",
+			ns->prefix, mailbox_list_get_last_error(ns->list, error_r));
 		ret = -1;
 	}
 
 	/* add subscriptions */
 	iter = mailbox_list_iter_init(ns->list, list_pattern, subs_list_flags);
 	while ((info = mailbox_list_iter_next(iter)) != NULL) {
-		if (dsync_mailbox_tree_add_node(tree, info, &node) < 0)
+		if (dsync_mailbox_tree_add_node(tree, info, &node) == 0)
+			node->subscribed = TRUE;
+		else {
+			*error_r = MAIL_ERROR_TEMP;
 			ret = -1;
-		else
-			node->subscribed = TRUE;
+		}
 	}
 	if (mailbox_list_iter_deinit(&iter) < 0) {
-		i_error("Mailbox listing for namespace '%s' failed", ns->prefix);
+		i_error("Mailbox listing for namespace '%s' failed: %s",
+			ns->prefix, mailbox_list_get_last_error(ns->list, error_r));
 		ret = -1;
 	}
 	if (ret < 0)
--- a/src/doveadm/dsync/dsync-mailbox-tree.h	Thu Mar 19 00:38:01 2015 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-tree.h	Thu Mar 19 00:41:19 2015 +0200
@@ -2,6 +2,7 @@
 #define DSYNC_MAILBOX_TREE_H
 
 #include "guid.h"
+#include "mail-error.h"
 
 struct mail_namespace;
 struct dsync_brain;
@@ -141,7 +142,8 @@
 int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree,
 			    struct mail_namespace *ns, const char *box_name,
 			    const guid_128_t box_guid,
-			    const char *const *exclude_mailboxes);
+			    const char *const *exclude_mailboxes,
+			    enum mail_error *error_r);
 
 /* Return all known deleted mailboxes and directories. */
 const struct dsync_mailbox_delete *