mdbox-storage-rebuild.c revision 72c25bce6471b026960d16c9ca8c180e0bc79409
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen struct mdbox_map_mail_index_header orig_map_hdr;
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen ARRAY_DEFINE(msgs, struct mdbox_rebuild_msg *);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainenmdbox_storage_rebuild_init(struct mdbox_storage *storage,
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen ctx = i_new(struct mdbox_storage_rebuild_context, 1);
9446c7a5d400cba60d097c528bd08312552438e3Timo Sirainen ctx->pool = pool_alloconly_create("dbox map rebuild", 1024*256);
f3e1593a7d9b02090575fb20db90a235e10145a5Timo Sirainen ctx->guid_hash = hash_table_create(default_pool, ctx->pool, 0,
aff1e150e13980354cfd794c74dac76a791a641eTimo Sirainenmdbox_storage_rebuild_deinit(struct mdbox_storage_rebuild_context *ctx)
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainenstatic int mdbox_rebuild_msg_offset_cmp(const void *p1, const void *p2)
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen const struct mdbox_rebuild_msg *const *m1 = p1, *const *m2 = p2;
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainenstatic int mdbox_rebuild_msg_uid_cmp(struct mdbox_rebuild_msg *const *m1,
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainenstatic int rebuild_file_mails(struct mdbox_storage_rebuild_context *ctx,
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen while ((ret = dbox_file_seek_next(file, &offset, &last)) >= 0) {
5fb80928bb367d7410d510d0d889909652553003Timo Sirainen if ((ret = dbox_file_metadata_read(file)) < 0)
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* file is corrupted. fix it and retry. */
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* use existing file header if it was ok */
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* seek to the offset where we last left off */
2978aafcf8c306adc2e012aed00546a63c677784Timo Sirainen guid = dbox_file_metadata_get(file, DBOX_METADATA_GUID);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen "Message is missing GUID");
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen rec = p_new(ctx->pool, struct mdbox_rebuild_msg, 1);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen mail_generate_guid_128_hash(guid, rec->guid_128);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen if (hash_table_lookup(ctx->guid_hash, rec->guid_128) != NULL) {
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* duplicate. save this as a refcount=0 to map,
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen so it will eventually be deleted. */
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen hash_table_insert(ctx->guid_hash, rec->guid_128, rec);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainenrebuild_rename_file(struct mdbox_storage_rebuild_context *ctx,
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen const char *dir, const char **fname_p, uint32_t *file_id_r)
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen const char *old_path, *new_path, *fname = *fname_p;
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen old_path = t_strconcat(dir, "/", fname, NULL);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen new_path = t_strdup_printf("%s/"MDBOX_MAIL_FILE_FORMAT,
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* use link()+unlink() instead of rename() to make sure we
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen don't overwrite any files. */
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen i_error("link(%s, %s) failed: %m", old_path, new_path);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainenstatic int rebuild_add_file(struct mdbox_storage_rebuild_context *ctx,
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen id_str = fname + strlen(MDBOX_MAIL_FILE_PREFIX);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen if (str_to_uint32(id_str, &file_id) < 0 || file_id == 0) {
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* m.*.broken files are created by file fixing
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen m.*.lock files are created if flock() isn't available */
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen if (ext == NULL || (strcmp(ext, ".broken") != 0 &&
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen "Skipping file with missing ID: %s/%s",
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen if (!seq_range_exists(&ctx->seen_file_ids, file_id)) {
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* duplicate file. either readdir() returned it twice
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch (unlikely) or it exists in both alt and primary storage.
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen to make sure we don't lose any mails from either of the
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen files, give this file a new ID and rename it. */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen if (rebuild_rename_file(ctx, dir, &fname, &file_id) < 0)
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen seq_range_array_add(&ctx->seen_file_ids, 0, file_id);
e02846284483d9d3c4b72835cd3dd6cc7f7e6740Timo Sirainen file = mdbox_file_init(ctx->storage, file_id);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen if ((ret = dbox_file_open(file, &deleted)) > 0 && !deleted)
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen i_error("mdbox rebuild: Failed to fix file %s/%s", dir, fname);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainenrebuild_add_missing_map_uids(struct mdbox_storage_rebuild_context *ctx,
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch unsigned int i, count;
26bedc1f8aace67ff8f19f625a403a097017845aTimo Sirainen msgs = array_get_modifiable(&ctx->msgs, &count);
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen for (i = 0; i < count; i++) {
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen mail_index_update_ext(ctx->atomic->sync_trans, seq,
5fb80928bb367d7410d510d0d889909652553003Timo Sirainenstatic int rebuild_apply_map(struct mdbox_storage_rebuild_context *ctx)
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen struct mdbox_rebuild_msg search_msg, *search_msgp = &search_msg;
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen unsigned int count;
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen array_sort(&ctx->msgs, mdbox_rebuild_msg_offset_cmp);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen msgs = array_get_modifiable(&ctx->msgs, &count);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen hdr = mail_index_get_header(ctx->atomic->sync_view);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen for (seq = 1; seq <= hdr->messages_count; seq++) {
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen if (mdbox_map_view_lookup_rec(map, ctx->atomic->sync_view,
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen /* look up the rebuild msg record for this message */
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen pos = bsearch(&search_msgp, msgs, count, sizeof(*msgs),
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* map record points to nonexistent or
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen a duplicate message. */
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen mail_index_expunge(ctx->atomic->sync_trans, seq);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen rebuild_add_missing_map_uids(ctx, hdr->next_uid);
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen /* afterwards we're interested in looking up map_uids.
7c7d2244502af05f77c74736dc1aec0123fdba80Timo Sirainen re-sort the messages to make it easier. */
b494ffed8ded8d170d9ace3dc607b1d278048241Timo Sirainen array_sort(&ctx->msgs, mdbox_rebuild_msg_uid_cmp);
static struct mdbox_rebuild_msg *
const void *data;
bool expunged;
if (map_uid == 0) {
} T_END;
const void *data;
int ret;
if (ret <= 0) {
int ret = 0;
MAILBOX_NOSELECT)) == 0) {
T_BEGIN {
} T_END;
if (ret < 0) {
return ret;
int ret;
if (ret < 0)
if (ret <= 0) {
unsigned int i, count;
for (i = 0; i < count; i++) {
const void *data;
bool expunged;
unsigned int i, count;
struct dirent *d;
int ret = 0;
} T_END;
return ret;
const void *data;
FALSE) < 0)
int ret;
if (ret == 0) {
return ret;
int ret;
return ret;