dovecot-2.0: mdbox: Fixed some race condition problems with purg...
dovecot at dovecot.org
dovecot at dovecot.org
Mon Jul 19 16:54:48 EEST 2010
details: http://hg.dovecot.org/dovecot-2.0/rev/e4d870bed095
changeset: 11856:e4d870bed095
user: Timo Sirainen <tss at iki.fi>
date: Mon Jul 19 14:54:36 2010 +0100
description:
mdbox: Fixed some race condition problems with purging.
diffstat:
src/lib-storage/index/dbox-multi/mdbox-purge.c | 72 ++++++++++++++++++++---
1 files changed, 61 insertions(+), 11 deletions(-)
diffs (123 lines):
diff -r 6217bc3589b8 -r e4d870bed095 src/lib-storage/index/dbox-multi/mdbox-purge.c
--- a/src/lib-storage/index/dbox-multi/mdbox-purge.c Mon Jul 19 14:54:12 2010 +0100
+++ b/src/lib-storage/index/dbox-multi/mdbox-purge.c Mon Jul 19 14:54:36 2010 +0100
@@ -141,10 +141,8 @@
off_t ret;
int read_errno;
- if (ctx->append_ctx == NULL) {
- ctx->atomic = mdbox_map_atomic_begin(ctx->storage->map);
+ if (ctx->append_ctx == NULL)
ctx->append_ctx = mdbox_map_append_begin(ctx->atomic);
- }
append_flags = !mdbox_purge_want_altpath(ctx, msg->map_uid) ? 0 :
DBOX_MAP_APPEND_FLAG_ALT;
@@ -184,6 +182,41 @@
}
static int
+mdbox_file_purge_check_refcounts(struct mdbox_purge_context *ctx,
+ const ARRAY_TYPE(mdbox_map_file_msg) *msgs_arr)
+{
+ struct mdbox_map *map = ctx->storage->map;
+ struct mdbox_map_mail_index_record rec;
+ uint16_t refcount;
+ const struct mdbox_map_file_msg *msgs;
+ unsigned int i, count;
+ int ret;
+
+ if (mdbox_map_atomic_lock(ctx->atomic) < 0)
+ return -1;
+
+ msgs = array_get(msgs_arr, &count);
+ for (i = 0; i < count; i++) {
+ if (msgs[i].refcount != 0)
+ continue;
+
+ ret = mdbox_map_lookup_full(map, msgs[i].map_uid, &rec,
+ &refcount);
+ if (ret <= 0) {
+ if (ret < 0)
+ return -1;
+ mdbox_map_set_corrupted(map,
+ "Purging unexpectedly lost map_uid=%u",
+ msgs[i].map_uid);
+ return -1;
+ }
+ if (refcount > 0)
+ return 0;
+ }
+ return 1;
+}
+
+static int
mdbox_file_purge(struct mdbox_purge_context *ctx, struct dbox_file *file)
{
struct mdbox_storage *dstorage = (struct mdbox_storage *)file->storage;
@@ -196,6 +229,9 @@
uoff_t offset;
int ret;
+ i_assert(ctx->atomic == NULL);
+ i_assert(ctx->append_ctx == NULL);
+
if ((ret = dbox_file_try_lock(file)) <= 0)
return ret;
@@ -224,6 +260,7 @@
/* sort messages by their offset */
array_sort(&msgs_arr, mdbox_map_file_msg_offset_cmp);
+ ctx->atomic = mdbox_map_atomic_begin(ctx->storage->map);
msgs = array_get(&msgs_arr, &count);
i_array_init(&copied_map_uids, I_MIN(count, 1));
i_array_init(&expunged_map_uids, I_MIN(count, 1));
@@ -270,11 +307,23 @@
"(%"PRIuUOFF_T" < %"PRIuUOFF_T")", offset, st.st_size);
ret = 0;
}
- array_free(&msgs_arr); msgs = NULL;
if (ret <= 0)
ret = -1;
- else if (ctx->append_ctx == NULL) {
+ else {
+ /* it's possible that one of the messages we purged was
+ just copied to another mailbox. the only way to prevent that
+ would be to keep map locked during the purge, but that could
+ keep it locked for too long. instead we'll check here if
+ there are such copies, and if there are cancel this file's
+ purge. */
+ ret = mdbox_file_purge_check_refcounts(ctx, &msgs_arr);
+ }
+ array_free(&msgs_arr); msgs = NULL;
+
+ if (ret <= 0) {
+ /* failed */
+ } else if (ctx->append_ctx == NULL) {
/* everything purged from this file */
ret = 1;
} else {
@@ -286,14 +335,15 @@
else
ret = 1;
}
+ if (ctx->append_ctx != NULL)
+ mdbox_map_append_free(&ctx->append_ctx);
+ (void)mdbox_map_atomic_finish(&ctx->atomic);
+
+ /* unlink only after unlocking map, so readers don't see it
+ temporarily vanished */
if (ret > 0)
(void)dbox_file_unlink(file);
- if (ctx->append_ctx != NULL) {
- mdbox_map_append_free(&ctx->append_ctx);
- if (mdbox_map_atomic_finish(&ctx->atomic) < 0)
- ret = -1;
- }
- if (ret < 0)
+ else
dbox_file_unlock(file);
array_free(&copied_map_uids);
array_free(&expunged_map_uids);
More information about the dovecot-cvs
mailing list