changeset 5913:699356ffe549 HEAD

If uidlist changes while it's being iterated, don't crash.
author Timo Sirainen <tss@iki.fi>
date Sun, 08 Jul 2007 22:53:31 +0300
parents 53abd603e04b
children ae731dbf3a6f
files src/lib-storage/index/maildir/maildir-uidlist.c
diffstat 1 files changed, 35 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/maildir/maildir-uidlist.c	Sun Jul 08 22:19:56 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-uidlist.c	Sun Jul 08 22:53:31 2007 +0300
@@ -50,6 +50,8 @@
 	pool_t record_pool;
 	ARRAY_TYPE(maildir_uidlist_rec_p) records;
 	struct hash_table *files;
+	unsigned int change_counter;
+
 	struct dotlock_settings dotlock_settings;
 	struct dotlock *dotlock;
 
@@ -81,7 +83,11 @@
 };
 
 struct maildir_uidlist_iter_ctx {
+	struct maildir_uidlist *uidlist;
 	struct maildir_uidlist_rec *const *next, *const *end;
+
+	unsigned int change_counter;
+	uint32_t prev_uid;
 };
 
 static int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist,
@@ -372,6 +378,7 @@
 		uidlist->uid_validity = uid_validity;
 		uidlist->next_uid = next_uid;
 		uidlist->prev_read_uid = 0;
+		uidlist->change_counter++;
 
 		ret = 1;
 		while ((line = i_stream_read_next_line(input)) != NULL) {
@@ -772,6 +779,7 @@
 			    struct maildir_uidlist_rec, 1);
 		rec->uid = (uint32_t)-1;
 		array_append(&uidlist->records, &rec, 1);
+		uidlist->change_counter++;
 	}
 
 	if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
@@ -942,6 +950,8 @@
 
 	if (ctx->new_files_count != 0)
 		maildir_uidlist_assign_uids(ctx, count - ctx->new_files_count);
+
+	ctx->uidlist->change_counter++;
 }
 
 void maildir_uidlist_sync_finish(struct maildir_uidlist_sync_ctx *ctx)
@@ -1031,16 +1041,40 @@
 	unsigned int count;
 
 	ctx = i_new(struct maildir_uidlist_iter_ctx, 1);
+	ctx->uidlist = uidlist;
 	ctx->next = array_get(&uidlist->records, &count);
 	ctx->end = ctx->next + count;
+	ctx->change_counter = ctx->uidlist->change_counter;
 	return ctx;
 }
 
+static void
+maildir_uidlist_iter_update_idx(struct maildir_uidlist_iter_ctx *ctx)
+{
+	unsigned int old_rev_idx, idx, count;
+
+	old_rev_idx = ctx->end - ctx->next;
+	ctx->next = array_get(&ctx->uidlist->records, &count);
+	ctx->end = ctx->next + count;
+
+	idx = old_rev_idx >= count ? 0 :
+		count - old_rev_idx;
+	while (idx < count && ctx->next[idx]->uid <= ctx->prev_uid)
+		idx++;
+	while (idx > 0 && ctx->next[idx-1]->uid > ctx->prev_uid)
+		idx--;
+
+	ctx->next += idx;
+}
+
 int maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx,
 			      uint32_t *uid_r,
 			      enum maildir_uidlist_rec_flag *flags_r,
 			      const char **filename_r)
 {
+	if (ctx->change_counter != ctx->uidlist->change_counter)
+		maildir_uidlist_iter_update_idx(ctx);
+
 	if (ctx->next == ctx->end)
 		return 0;
 
@@ -1048,6 +1082,7 @@
 	*flags_r = (*ctx->next)->flags;
 	*filename_r = (*ctx->next)->filename;
 	ctx->next++;
+	ctx->prev_uid = *uid_r;
 	return 1;
 }