From: Timo Sirainen Date: Mon, 21 Jul 2003 14:35:39 +0000 (+0300) Subject: Removed .imap.index.tree file. For now we'll just rewrite .imap.index file X-Git-Tag: 1.1.alpha1~4472 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e2503d9e0306e3d256b314b5410460a18c8b99e4;p=thirdparty%2Fdovecot%2Fcore.git Removed .imap.index.tree file. For now we'll just rewrite .imap.index file whenever there's expunges. "Expunge binary tree" might be added later, but it's not really useful until you have thousands of messages in mailbox and you're deleting old messages from it. --HG-- branch : HEAD --- diff --git a/src/lib-index/Makefile.am b/src/lib-index/Makefile.am index 4db43f9251..7a9aa5ce2c 100644 --- a/src/lib-index/Makefile.am +++ b/src/lib-index/Makefile.am @@ -11,15 +11,14 @@ libindex_a_SOURCES = \ mail-custom-flags.c \ mail-index.c \ mail-index-compress.c \ + mail-index-file.c \ mail-index-fsck.c \ mail-index-data.c \ mail-index-open.c \ mail-index-update.c \ mail-index-update-cache.c \ mail-index-util.c \ - mail-modifylog.c \ - mail-tree.c \ - mail-tree-redblack.c + mail-modifylog.c noinst_HEADERS = \ mail-custom-flags.h \ diff --git a/src/lib-index/mail-index-compress.c b/src/lib-index/mail-index-compress.c index 6726bb0700..0f78e0e01a 100644 --- a/src/lib-index/mail-index-compress.c +++ b/src/lib-index/mail-index-compress.c @@ -5,7 +5,6 @@ #include "mail-index.h" #include "mail-index-data.h" #include "mail-index-util.h" -#include "mail-tree.h" #include #include @@ -45,75 +44,6 @@ int mail_index_truncate(struct mail_index *index) return TRUE; } -int mail_index_compress(struct mail_index *index) -{ - struct mail_index_record *rec, *hole_rec, *end_rec; - unsigned int idx; - int tree_fd; - - if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE)) - return FALSE; - - if (index->header->first_hole_records == 0) { - /* we don't need to compress after all. shouldn't happen.. */ - index->header->flags &= ~MAIL_INDEX_FLAG_COMPRESS; - return TRUE; - } - - if (!mail_index_verify_hole_range(index)) - return FALSE; - - /* if we get interrupted, the whole index is probably corrupted. - so keep rebuild-flag on while doing this */ - index->header->flags |= MAIL_INDEX_FLAG_REBUILD; - if (!mail_index_fmdatasync(index, sizeof(struct mail_index_header))) - return FALSE; - - /* first actually compress the data */ - hole_rec = INDEX_RECORD_AT(index, index->header->first_hole_index); - end_rec = INDEX_END_RECORD(index); - rec = hole_rec + index->header->first_hole_records; - while (rec < end_rec) { - if (rec->uid != 0) { - memcpy(hole_rec, rec, sizeof(struct mail_index_record)); - idx = INDEX_RECORD_INDEX(index, hole_rec); - if (!mail_tree_update(index->tree, rec->uid, idx)) - return FALSE; - hole_rec++; - } - rec++; - } - - /* truncate the file to get rid of the extra records */ - index->mmap_used_length = (size_t) ((char *) hole_rec - - (char *) index->mmap_base); - index->header->used_file_size = index->mmap_used_length; - - if (!mail_index_truncate(index)) - return FALSE; - - /* update headers */ - index->header->first_hole_index = 0; - index->header->first_hole_records = 0; - - /* make sure the whole file is synced before removing rebuild-flag */ - if (!mail_tree_sync_file(index->tree, &tree_fd)) - return FALSE; - - if (fdatasync(tree_fd) < 0) { - index_file_set_syscall_error(index, index->tree->filepath, - "fdatasync()"); - return FALSE; - } - - if (!mail_index_fmdatasync(index, index->mmap_used_length)) - return FALSE; - - index->header->flags &= ~(MAIL_INDEX_FLAG_COMPRESS | - MAIL_INDEX_FLAG_REBUILD); - return TRUE; -} - static int mail_index_copy_data(struct mail_index *index, int fd, const char *path) { diff --git a/src/lib-index/mail-index-data.c b/src/lib-index/mail-index-data.c index cf30e2a9c5..6403073dd6 100644 --- a/src/lib-index/mail-index-data.c +++ b/src/lib-index/mail-index-data.c @@ -216,6 +216,13 @@ static int mmap_update(struct mail_index_data *data, uoff_t pos, size_t size) return FALSE; } + if ((hdr->used_file_size & (INDEX_ALIGN_SIZE-1)) != 0) { + index_data_set_corrupted(data, + "used_file_size not aligned (%"PRIuUOFF_T")", + hdr->used_file_size); + return FALSE; + } + data->mmap_used_length = hdr->used_file_size; data->header = hdr; debug_mprotect(data->mmap_base, data->mmap_full_length, data->index); @@ -593,7 +600,7 @@ mail_index_data_lookup(struct mail_index_data *data, (data->mmap_used_length - pos < rec_hdr->data_size)) { index_data_set_corrupted(data, "Given data size larger than file size " - "(%"PRIuUOFF_T" + %u > %"PRIuSIZE_T") for record %u", + "(%u + %u > %"PRIuSIZE_T") for record %u", index_rec->data_position, rec_hdr->data_size, data->mmap_used_length, index_rec->uid); return NULL; diff --git a/src/lib-index/mail-index-file.c b/src/lib-index/mail-index-file.c new file mode 100644 index 0000000000..7c2af4221c --- /dev/null +++ b/src/lib-index/mail-index-file.c @@ -0,0 +1,116 @@ +/* Copyright (C) 2003 Timo Sirainen */ + +#include "lib.h" +#include "mail-index.h" +#include "mail-index-util.h" + +struct mail_index_record *mail_index_next(struct mail_index *index, + struct mail_index_record *rec) +{ + i_assert(index->lock_type != MAIL_LOCK_UNLOCK); + i_assert(rec >= INDEX_RECORD_AT(index, 0)); + + return rec+1 == INDEX_END_RECORD(index) ? NULL : rec+1; +} + +static int compress(struct mail_index *index, unsigned int remove_first_idx, + unsigned int remove_last_idx) +{ + struct mail_index_record *rec = INDEX_RECORD_AT(index, 0); + unsigned int idx_limit, count; + + idx_limit = MAIL_INDEX_RECORD_COUNT(index); + count = remove_last_idx - remove_first_idx + 1; + + memmove(rec + remove_first_idx, rec + remove_last_idx + 1, + (idx_limit - remove_last_idx - 1) * sizeof(*rec)); + + index->header->used_file_size -= sizeof(*rec) * count; + index->mmap_used_length -= sizeof(*rec) * count; + + return mail_index_truncate(index); +} + +int mail_index_expunge_record_range(struct mail_index *index, + struct mail_index_record *first_rec, + struct mail_index_record *last_rec) +{ + struct mail_index_record *rec; + unsigned int first_idx, last_idx, idx_limit; + + i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE); + + first_idx = INDEX_RECORD_INDEX(index, first_rec); + last_idx = INDEX_RECORD_INDEX(index, last_rec); + idx_limit = MAIL_INDEX_RECORD_COUNT(index); + + i_assert(first_idx <= last_idx); + i_assert(last_idx < idx_limit); + + index->header->messages_count -= last_idx - first_idx + 1; + for (rec = first_rec; rec <= last_rec; rec++) + mail_index_mark_flag_changes(index, rec, rec->msg_flags, 0); + + return compress(index, first_idx, last_idx); +} + +struct mail_index_record *mail_index_lookup(struct mail_index *index, + unsigned int seq) +{ + i_assert(index->lock_type != MAIL_LOCK_UNLOCK); + i_assert(seq > 0); + + if (seq > index->header->messages_count) + return NULL; + + return INDEX_RECORD_AT(index, seq-1); +} + +struct mail_index_record * +mail_index_lookup_uid_range(struct mail_index *index, unsigned int first_uid, + unsigned int last_uid, unsigned int *seq_r) +{ + struct mail_index_record *rec_p; + unsigned int idx_limit, idx, left_idx, right_idx; + + i_assert(index->lock_type != MAIL_LOCK_UNLOCK); + i_assert(first_uid > 0); + i_assert(first_uid <= last_uid); + + rec_p = INDEX_RECORD_AT(index, 0); + idx_limit = MAIL_INDEX_RECORD_COUNT(index); + + idx = 0; + left_idx = 0; + right_idx = idx_limit; + + while (left_idx < right_idx) { + idx = (left_idx + right_idx) / 2; + + if (rec_p[idx].uid < first_uid) + left_idx = idx+1; + else if (rec_p[idx].uid > first_uid) + right_idx = idx; + else + break; + } + + if (rec_p[idx].uid < first_uid || rec_p[idx].uid > last_uid) { + /* could still be the next one */ + idx++; + if (idx == idx_limit || + rec_p[idx].uid < first_uid || rec_p[idx].uid > last_uid) { + if (seq_r != NULL) *seq_r = 0; + return NULL; + } + } + + if (seq_r != NULL) + *seq_r = idx + 1; + return rec_p + idx; +} + +int mail_index_compress(struct mail_index *index __attr_unused__) +{ + return TRUE; +} diff --git a/src/lib-index/mail-index-fsck.c b/src/lib-index/mail-index-fsck.c index 987fc5f47b..f9b108de64 100644 --- a/src/lib-index/mail-index-fsck.c +++ b/src/lib-index/mail-index-fsck.c @@ -14,14 +14,6 @@ static void print_differences(struct mail_index *index, struct mail_index_header *old_hdr, struct mail_index_header *new_hdr) { - if (old_hdr->first_hole_index != new_hdr->first_hole_index) { - i_warning("fsck %s: first_hole_position %u != %u", - index->filepath, - old_hdr->first_hole_index, - new_hdr->first_hole_index); - } - CHECK(first_hole_records); - CHECK(next_uid); CHECK(messages_count); @@ -48,23 +40,21 @@ static void print_differences(struct mail_index *index, int mail_index_fsck(struct mail_index *index) { - /* we verify only the fields in the header. other problems will be - noticed and fixed while reading the messages. */ struct mail_index_header old_hdr, *hdr; struct mail_index_record *rec, *end_rec; - unsigned int max_uid, pos; + unsigned int max_uid; i_assert(index->lock_type != MAIL_LOCK_SHARED); - if (!mail_index_set_lock(index, MAIL_LOCK_EXCLUSIVE)) + /* Expunge tree can get easily corrupted, compress it away first. */ + if (!mail_index_compress(index)) return FALSE; + /* then we verify only the fields in the header. other problems will + be noticed and fixed while reading the messages. */ hdr = index->header; memcpy(&old_hdr, hdr, sizeof(struct mail_index_header)); - hdr->first_hole_index = 0; - hdr->first_hole_records = 0; - hdr->messages_count = 0; hdr->seen_messages_count = 0; hdr->deleted_messages_count = 0; @@ -77,20 +67,6 @@ int mail_index_fsck(struct mail_index *index) max_uid = 0; for (; rec < end_rec; rec++) { - if (rec->uid == 0) { - /* expunged message */ - pos = INDEX_RECORD_INDEX(index, rec); - if (hdr->first_hole_records == 0) { - hdr->first_hole_index = pos; - hdr->first_hole_records = 1; - } else if (hdr->first_hole_index + - hdr->first_hole_records == pos) { - /* hole continues */ - hdr->first_hole_records++; - } - continue; - } - if (rec->uid < max_uid) { index_set_corrupted(index, "UIDs are not ordered " "(%u < %u)", rec->uid, max_uid); diff --git a/src/lib-index/mail-index-open.c b/src/lib-index/mail-index-open.c index e3c00f3d45..a5c27e2a00 100644 --- a/src/lib-index/mail-index-open.c +++ b/src/lib-index/mail-index-open.c @@ -11,7 +11,6 @@ #include "mail-index.h" #include "mail-index-data.h" #include "mail-index-util.h" -#include "mail-tree.h" #include "mail-modifylog.h" #include "mail-custom-flags.h" @@ -111,13 +110,9 @@ static int index_open_and_fix(struct mail_index *index, } if ((flags & _MAIL_INDEX_OPEN_FLAG_CREATING) == 0) { - if (!mail_tree_open_or_create(index)) - return FALSE; if (!mail_modifylog_open_or_create(index)) return FALSE; } else { - if (!mail_tree_create(index)) - return FALSE; if (!mail_modifylog_create(index)) return FALSE; } @@ -128,11 +123,6 @@ static int index_open_and_fix(struct mail_index *index, return FALSE; } - if ((index->header->flags & MAIL_INDEX_FLAG_REBUILD_TREE) != 0) { - if (!mail_tree_rebuild(index->tree)) - return FALSE; - } - if (!rebuilt) { /* sync ourself. do it before updating cache and compression which may happen because of this. */ diff --git a/src/lib-index/mail-index.c b/src/lib-index/mail-index.c index 4d3c8ff234..c94ccc56c7 100644 --- a/src/lib-index/mail-index.c +++ b/src/lib-index/mail-index.c @@ -8,7 +8,6 @@ #include "mail-index.h" #include "mail-index-data.h" #include "mail-index-util.h" -#include "mail-tree.h" #include "mail-modifylog.h" #include "mail-custom-flags.h" @@ -180,11 +179,6 @@ void mail_index_close(struct mail_index *index) index->data = NULL; } - if (index->tree != NULL) { - mail_tree_free(index->tree); - index->tree = NULL; - } - if (index->modifylog != NULL) { mail_modifylog_free(index->modifylog); index->modifylog = NULL; @@ -220,11 +214,6 @@ static int mail_index_sync_file(struct mail_index *index) failed = FALSE; - if (index->tree != NULL) { - if (!mail_tree_sync_file(index->tree, &fsync_fds[1])) - failed = TRUE; - } - if (index->modifylog != NULL) { if (!mail_modifylog_sync_file(index->modifylog, &fsync_fds[2])) failed = TRUE; @@ -506,32 +495,6 @@ void mail_index_set_lock_notify_callback(struct mail_index *index, index->lock_notify_context = context; } -int mail_index_verify_hole_range(struct mail_index *index) -{ - struct mail_index_header *hdr; - unsigned int max_records; - - hdr = index->header; - if (hdr->first_hole_records == 0) - return TRUE; - - max_records = MAIL_INDEX_RECORD_COUNT(index); - if (hdr->first_hole_index >= max_records) { - index_set_corrupted(index, - "first_hole_index points outside file"); - return FALSE; - } - - /* check that first_hole_records is in valid range */ - if (max_records - hdr->first_hole_index < hdr->first_hole_records) { - index_set_corrupted(index, - "first_hole_records points outside file"); - return FALSE; - } - - return TRUE; -} - struct mail_index_header *mail_index_get_header(struct mail_index *index) { i_assert(index->lock_type != MAIL_LOCK_UNLOCK); @@ -539,126 +502,6 @@ struct mail_index_header *mail_index_get_header(struct mail_index *index) return index->header; } -struct mail_index_record *mail_index_lookup(struct mail_index *index, - unsigned int seq) -{ - struct mail_index_header *hdr; - struct mail_index_record *rec; - const char *error; - unsigned int idx; - - i_assert(seq > 0); - i_assert(index->lock_type != MAIL_LOCK_UNLOCK); - - hdr = index->header; - if (seq > hdr->messages_count) { - /* out of range */ - return NULL; - } - - if (!mail_index_verify_hole_range(index)) - return NULL; - - idx = seq-1; - if (hdr->first_hole_records == 0 || hdr->first_hole_index > idx) { - /* easy, it's just at the expected index */ - error = t_strdup_printf("Invalid first_hole_index in header: " - "%u", idx); - } else if (hdr->first_hole_records == - MAIL_INDEX_RECORD_COUNT(index) - hdr->messages_count) { - /* only one hole in file, skip it and we're at - correct position */ - idx += hdr->first_hole_records; - error = t_strdup_printf("Invalid hole locations in header: %u", - idx); - } else { - /* find from binary tree */ - idx = mail_tree_lookup_sequence(index->tree, seq); - if (idx == (unsigned int)-1) { - index_set_corrupted(index, - "Sequence %u not found from binary tree " - "(%u msgs says header)", - seq, hdr->messages_count); - return NULL; - } - - error = t_strdup_printf("Invalid offset returned by " - "binary tree: %u", idx); - } - - if (idx >= MAIL_INDEX_RECORD_COUNT(index)) { - index_set_corrupted(index, "%s", error); - return NULL; - } - - rec = INDEX_RECORD_AT(index, idx); - if (rec->uid == 0) { - index_set_corrupted(index, "%s", error); - return NULL; - } - - return rec; -} - -struct mail_index_record *mail_index_next(struct mail_index *index, - struct mail_index_record *rec) -{ - struct mail_index_record *end_rec; - - i_assert(index->lock_type != MAIL_LOCK_UNLOCK); - i_assert(rec >= (struct mail_index_record *) index->mmap_base); - - if (rec == NULL) - return NULL; - - /* go to the next non-deleted record */ - end_rec = INDEX_END_RECORD(index); - while (++rec < end_rec) { - if (rec->uid != 0) - return rec; - } - - return NULL; -} - -struct mail_index_record * -mail_index_lookup_uid_range(struct mail_index *index, unsigned int first_uid, - unsigned int last_uid, unsigned int *seq_r) -{ - struct mail_index_record *rec; - unsigned int idx; - - i_assert(index->lock_type != MAIL_LOCK_UNLOCK); - i_assert(first_uid > 0 && last_uid > 0); - i_assert(first_uid <= last_uid); - - idx = mail_tree_lookup_uid_range(index->tree, seq_r, - first_uid, last_uid); - if (idx == (unsigned int)-1) - return NULL; - - if (idx >= MAIL_INDEX_RECORD_COUNT(index)) { - index_set_error(index, "Corrupted binary tree for index %s: " - "lookup returned index outside range " - "(%u >= %"PRIuSIZE_T")", index->filepath, idx, - MAIL_INDEX_RECORD_COUNT(index)); - index->set_flags |= MAIL_INDEX_FLAG_REBUILD_TREE; - return NULL; - } - - rec = INDEX_RECORD_AT(index, idx); - if (rec->uid < first_uid || rec->uid > last_uid) { - index_set_error(index, "Corrupted binary tree for index %s: " - "lookup returned offset to wrong UID " - "(%u vs %u..%u)", index->filepath, - rec->uid, first_uid, last_uid); - index->set_flags |= MAIL_INDEX_FLAG_REBUILD_TREE; - return NULL; - } - - return rec; -} - const char *mail_index_lookup_field(struct mail_index *index, struct mail_index_record *rec, enum mail_data_field field) @@ -814,20 +657,7 @@ void mail_index_mark_flag_changes(struct mail_index *index, } } -static void update_first_hole_records(struct mail_index *index) -{ - struct mail_index_record *rec, *end_rec; - - /* see if first_hole_records can be grown */ - rec = INDEX_RECORD_AT(index, index->header->first_hole_index + - index->header->first_hole_records); - end_rec = INDEX_END_RECORD(index); - while (rec < end_rec && rec->uid == 0) { - index->header->first_hole_records++; - rec++; - } -} - +#if 0 static int mail_index_truncate_hole(struct mail_index *index) { index->header->used_file_size = sizeof(struct mail_index_header) + @@ -848,96 +678,36 @@ static int mail_index_truncate_hole(struct mail_index *index) return TRUE; } - -static void update_first_hole(struct mail_index *index, - struct mail_index_record *rec) -{ - struct mail_index_header *hdr = index->header; - unsigned int idx; - - idx = INDEX_RECORD_INDEX(index, rec); - if (hdr->first_hole_records == 0) { - /* first deleted message in index */ - hdr->first_hole_index = idx; - hdr->first_hole_records = 1; - } else if (idx+1 == hdr->first_hole_index) { - /* deleted the previous record before hole */ - hdr->first_hole_index--; - hdr->first_hole_records++; - } else if (idx == hdr->first_hole_index + hdr->first_hole_records) { - /* deleted the next record after hole */ - hdr->first_hole_records++; - update_first_hole_records(index); - } else { - /* second hole coming to index file */ - if (idx < hdr->first_hole_index) { - /* new hole before the old hole */ - hdr->first_hole_index = idx; - hdr->first_hole_records = 1; - } - } -} +#endif #define INDEX_NEED_COMPRESS(records, hdr) \ ((records) > INDEX_MIN_RECORDS_COUNT && \ (records) * (100-INDEX_COMPRESS_PERCENTAGE) / 100 > \ (hdr)->messages_count) -int mail_index_expunge(struct mail_index *index, struct mail_index_record *rec, - unsigned int seq, int external_change) +int mail_index_expunge(struct mail_index *index, + struct mail_index_record *first_rec, + struct mail_index_record *last_rec, + unsigned int first_seq, unsigned int last_seq, + int external_change) { - struct mail_index_header *hdr; - unsigned int records, uid; + unsigned int first_uid, last_uid; i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE); - i_assert(seq != 0); - i_assert(rec->uid != 0); - - if (!mail_index_verify_hole_range(index)) - return FALSE; - - hdr = index->header; - - /* setting UID to 0 is enough for deleting the mail from index */ - uid = rec->uid; - rec->uid = 0; + i_assert(first_seq != 0); + i_assert(first_seq <= last_seq); - update_first_hole(index, rec); + first_uid = first_rec->uid; + last_uid = last_rec->uid; - /* update message counts */ - if (hdr->messages_count == 0) { - /* corrupted */ - index_set_corrupted(index, - "Header says there's no mail while expunging"); + if (!mail_index_expunge_record_range(index, first_rec, last_rec)) return FALSE; - } - - hdr->messages_count--; - mail_index_mark_flag_changes(index, rec, rec->msg_flags, 0); - - (void)mail_index_data_delete(index->data, rec); - - records = MAIL_INDEX_RECORD_COUNT(index); - if (hdr->first_hole_index + hdr->first_hole_records == records) { - /* the hole reaches end of file, truncate it */ - (void)mail_index_truncate_hole(index); - } else { - if (INDEX_NEED_COMPRESS(records, hdr)) - hdr->flags |= MAIL_INDEX_FLAG_COMPRESS; - } - /* expunge() may be called while index is being rebuilt and when - tree file hasn't been opened yet */ - if (index->tree != NULL) - mail_tree_delete(index->tree, uid); - else { - /* make sure it also gets updated */ - index->header->flags |= MAIL_INDEX_FLAG_REBUILD_TREE; - } - - if (seq != 0 && index->modifylog != NULL) { - if (!mail_modifylog_add_expunge(index->modifylog, seq, - uid, external_change)) + if (index->modifylog != NULL) { + if (!mail_modifylog_add_expunges(index->modifylog, + first_seq, last_seq, + first_uid, last_uid, + external_change)) return FALSE; } @@ -1044,11 +814,6 @@ int mail_index_append_end(struct mail_index *index, index->header->messages_count++; rec->uid = index->header->next_uid++; - if (index->tree != NULL) { - mail_tree_insert(index->tree, rec->uid, - INDEX_RECORD_INDEX(index, rec)); - } - return TRUE; } @@ -1064,7 +829,7 @@ void mail_index_append_abort(struct mail_index *index, index->mmap_used_length -= sizeof(*rec); } else { /* mark it deleted */ - update_first_hole(index, rec); + (void)mail_index_expunge_record_range(index, rec, rec); } } diff --git a/src/lib-index/mail-index.h b/src/lib-index/mail-index.h index d6864b92ad..78ce3fa53c 100644 --- a/src/lib-index/mail-index.h +++ b/src/lib-index/mail-index.h @@ -5,7 +5,7 @@ #include "message-parser.h" #include "imap-util.h" -#define MAIL_INDEX_VERSION 2 +#define MAIL_INDEX_VERSION 3 #define INDEX_FILE_PREFIX ".imap.index" @@ -36,7 +36,6 @@ enum mail_index_header_flag { MAIL_INDEX_FLAG_CACHE_FIELDS = 0x0004, MAIL_INDEX_FLAG_COMPRESS = 0x0008, MAIL_INDEX_FLAG_COMPRESS_DATA = 0x0010, - MAIL_INDEX_FLAG_REBUILD_TREE = 0x0020, MAIL_INDEX_FLAG_DIRTY_MESSAGES = 0x0040, MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS = 0x0080, MAIL_INDEX_FLAG_MAILDIR_NEW = 0x0100 @@ -140,9 +139,6 @@ struct mail_index_header { uoff_t used_file_size; - unsigned int first_hole_index; - unsigned int first_hole_records; - unsigned int uid_validity; unsigned int next_uid; @@ -166,12 +162,11 @@ struct mail_index_data_header { struct mail_index_record { unsigned int uid; - unsigned int msg_flags; /* enum mail_flags */ + unsigned int msg_flags; + unsigned int data_position; /* first bit must be 0 */ unsigned int index_flags; /* enum mail_index_mail_flag */ unsigned int data_fields; /* enum mail_data_field */ - - uoff_t data_position; }; struct mail_index_data_record_header { @@ -268,7 +263,7 @@ struct mail_index { /* Return the next record after specified record, or NULL if it was last record. The index must be locked all the time between - lookup() and last next() call. */ + lookup() and last next() call. rec must not have been expunged. */ struct mail_index_record *(*next)(struct mail_index *index, struct mail_index_record *rec); @@ -309,18 +304,19 @@ struct mail_index { time_t (*get_internal_date)(struct mail_index *index, struct mail_index_record *rec); - /* Expunge a mail from index. Tree and modifylog is also updated. The + /* Expunge mails from index. Modifylog is also updated. The index must be exclusively locked before calling this function. - If seq is 0, the modify log isn't updated. This is useful if - after append() something goes wrong and you wish to delete the - mail immediately. If external_change is TRUE, the modify log is - always written. - Note that the sequence numbers also update immediately after this - call, so if you want to delete messages 1..4 just call this - function 4 times with seq being 1. */ - int (*expunge)(struct mail_index *index, struct mail_index_record *rec, - unsigned int seq, int external_change); + first_rec+1 .. last_rec-1 range may contain already expunged + records. + + Note that all record pointers are invalidated after this call as + expunging may radically modify the file. */ + int (*expunge)(struct mail_index *index, + struct mail_index_record *first_rec, + struct mail_index_record *last_rec, + unsigned int first_seq, unsigned int last_seq, + int external_change); /* Update mail flags. The index must be exclusively locked before calling this function. This shouldn't be called in the middle of @@ -376,7 +372,6 @@ struct mail_index { /* private: */ struct mail_index_data *data; - struct mail_tree *tree; struct mail_modify_log *modifylog; struct mail_custom_flags *custom_flags; @@ -459,7 +454,7 @@ struct mail_index { members.. */ #define MAIL_INDEX_PRIVATE_FILL \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, { 0, 0, 0 }, 0, 0, \ + 0, 0, 0, 0, 0, { 0, 0, 0 }, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ @@ -493,8 +488,11 @@ const void *mail_index_lookup_field_raw(struct mail_index *index, size_t *size); void mail_index_cache_fields_later(struct mail_index *index, enum mail_data_field field); -int mail_index_expunge(struct mail_index *index, struct mail_index_record *rec, - unsigned int seq, int external_change); +int mail_index_expunge(struct mail_index *index, + struct mail_index_record *first_rec, + struct mail_index_record *last_rec, + unsigned int first_seq, unsigned int last_seq, + int external_change); int mail_index_update_flags(struct mail_index *index, struct mail_index_record *rec, unsigned int seq, enum mail_flags flags, @@ -527,7 +525,6 @@ void mail_index_init_header(struct mail_index *index, struct mail_index_header *hdr); void mail_index_close(struct mail_index *index); int mail_index_fmdatasync(struct mail_index *index, size_t size); -int mail_index_verify_hole_range(struct mail_index *index); void mail_index_mark_flag_changes(struct mail_index *index, struct mail_index_record *rec, enum mail_flags old_flags, @@ -541,6 +538,9 @@ int mail_index_update_cache(struct mail_index *index); int mail_index_compress(struct mail_index *index); int mail_index_compress_data(struct mail_index *index); int mail_index_truncate(struct mail_index *index); +int mail_index_expunge_record_range(struct mail_index *index, + struct mail_index_record *first_rec, + struct mail_index_record *last_rec); /* Maximum allowed UID number. */ #define MAX_ALLOWED_UID 4294967295U /* 2^32 - 1 */ @@ -565,6 +565,8 @@ int mail_index_truncate(struct mail_index *index); #define INDEX_TRUNCATE_KEEP_PERCENTAGE 10 /* Compress the file when deleted space reaches n% of total size */ #define INDEX_COMPRESS_PERCENTAGE 50 +/* Compress the file when searching deleted records tree has to go this deep */ +#define INDEX_COMPRESS_DEPTH 10 /* uoff_t to index file for given record */ #define INDEX_FILE_POSITION(index, ptr) \ diff --git a/src/lib-index/mail-modifylog.c b/src/lib-index/mail-modifylog.c index f198be9244..d98bb9502e 100644 --- a/src/lib-index/mail-modifylog.c +++ b/src/lib-index/mail-modifylog.c @@ -770,8 +770,10 @@ static int mail_modifylog_append(struct modify_log_file *file, return TRUE; } -int mail_modifylog_add_expunge(struct mail_modify_log *log, unsigned int seq, - unsigned int uid, int external_change) +int mail_modifylog_add_expunges(struct mail_modify_log *log, + unsigned int first_seq, unsigned int last_seq, + unsigned int first_uid, unsigned int last_uid, + int external_change) { struct modify_log_file *file; struct modify_log_record rec, *recp; @@ -786,24 +788,24 @@ int mail_modifylog_add_expunge(struct mail_modify_log *log, unsigned int seq, if (file->last_expunge != NULL && file->last_expunge_external == external_change) { - if (seq+1 == file->last_expunge->seq1) { - i_assert(uid < file->last_expunge->uid1); - file->last_expunge->seq1 = seq; - file->last_expunge->uid1 = uid; + if (last_seq+1 == file->last_expunge->seq1) { + i_assert(last_uid < file->last_expunge->uid1); + file->last_expunge->seq1 = first_seq; + file->last_expunge->uid1 = first_uid; return TRUE; - } else if (seq == file->last_expunge->seq1) { - /* note the different weirder logic than with - flag changing, because of reordered seq numbers. */ - i_assert(uid > file->last_expunge->uid2); - file->last_expunge->seq2++; - file->last_expunge->uid2 = uid; + } else if (first_seq == file->last_expunge->seq1) { + /* note that the weird looking logic above is correct. + it's because of reordered seq numbers. */ + i_assert(first_uid > file->last_expunge->uid2); + file->last_expunge->seq2 = last_seq; + file->last_expunge->uid2 = last_uid; return TRUE; } } rec.type = RECORD_TYPE_EXPUNGE; - rec.seq1 = rec.seq2 = seq; - rec.uid1 = rec.uid2 = uid; + rec.seq1 = first_seq; rec.seq2 = last_seq; + rec.uid1 = first_uid; rec.uid2 = last_uid; recp = &rec; if (!mail_modifylog_append(file, &recp, external_change)) diff --git a/src/lib-index/mail-modifylog.h b/src/lib-index/mail-modifylog.h index 0efdb0f830..1589e6c374 100644 --- a/src/lib-index/mail-modifylog.h +++ b/src/lib-index/mail-modifylog.h @@ -37,8 +37,10 @@ void mail_modifylog_free(struct mail_modify_log *log); /* Append EXPUGE or FLAGS entry to modify log. Index must be exclusively locked before calling these functions, and modifylog must have been marked synced within the same lock. */ -int mail_modifylog_add_expunge(struct mail_modify_log *log, unsigned int seq, - unsigned int uid, int external_change); +int mail_modifylog_add_expunges(struct mail_modify_log *log, + unsigned int first_seq, unsigned int last_seq, + unsigned int first_uid, unsigned int last_uid, + int external_change); int mail_modifylog_add_flags(struct mail_modify_log *log, unsigned int seq, unsigned int uid, int external_change); diff --git a/src/lib-index/mail-tree-redblack.c b/src/lib-index/mail-tree-redblack.c deleted file mode 100644 index 770861aa4a..0000000000 --- a/src/lib-index/mail-tree-redblack.c +++ /dev/null @@ -1,910 +0,0 @@ -/* - Redblack balanced tree algorithm, http://libredblack.sourceforge.net/ - Copyright (C) Damian Ivereigh 2000 - - Modified to be suitable for mmap()ing and for IMAP server - Copyright (C) Timo Sirainen 2002 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. See the file COPYING for details. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* NOTE: currently this code doesn't do any bounds checkings. I'm not sure - if I should even bother, the code would just get uglier and slower. */ - -#include "lib.h" -#include "mail-index.h" -#include "mail-tree.h" - -/* #define DEBUG_TREE */ - -#ifndef DEBUG_TREE -# define rb_check(tree) -#endif - -/* Dummy (sentinel) node, so that we can make X->left->up = X -** We then use this instead of NULL to mean the top or bottom -** end of the rb tree. It is a black node. -*/ -#define RBNULL 0 - -/* If highest bit in node_count is set, the node is red. */ -#define RED_MASK (1U << (SIZEOF_INT*CHAR_BIT-1)) - -#define IS_NODE_BLACK(node) \ - (((node).node_count & RED_MASK) == 0) -#define IS_NODE_RED(node) \ - (((node).node_count & RED_MASK) != 0) - -#define NODE_SET_BLACK(node) \ - STMT_START { (node).node_count &= ~RED_MASK; } STMT_END -#define NODE_SET_RED(node) \ - STMT_START { (node).node_count |= RED_MASK; } STMT_END - -#define NODE_COPY_COLOR(dest, src) \ - STMT_START { \ - if (((src).node_count & RED_MASK) != \ - ((dest).node_count & RED_MASK)) \ - (dest).node_count ^= RED_MASK; \ - } STMT_END - -#define NODE_COUNT(node) \ - ((node).node_count & ~RED_MASK) - -#define NODE_COUNT_ADD(node, count) \ - STMT_START { (node).node_count += (count); } STMT_END - -/* -** OK here we go, the balanced tree stuff. The algorithm is the -** fairly standard red/black taken from "Introduction to Algorithms" -** by Cormen, Leiserson & Rivest. Maybe one of these days I will -** fully understand all this stuff. -** -** Basically a red/black balanced tree has the following properties:- -** 1) Every node is either red or black (color is RED or BLACK) -** 2) A leaf (RBNULL pointer) is considered black -** 3) If a node is red then its children are black -** 4) Every path from a node to a leaf contains the same no -** of black nodes -** -** 3) & 4) above guarantee that the longest path (alternating -** red and black nodes) is only twice as long as the shortest -** path (all black nodes). Thus the tree remains fairly balanced. -*/ - -static unsigned int -rb_alloc(struct mail_tree *tree) -{ - unsigned int x; - - if (tree->mmap_used_length == tree->mmap_full_length) { - if (!_mail_tree_grow(tree)) - return RBNULL; - } - - i_assert(tree->header->used_file_size == tree->mmap_used_length); - i_assert(tree->mmap_used_length + sizeof(struct mail_tree_node) <= - tree->mmap_full_length); - - x = (tree->mmap_used_length - sizeof(struct mail_tree_header)) / - sizeof(struct mail_tree_node); - - tree->header->used_file_size += sizeof(struct mail_tree_node); - tree->mmap_used_length += sizeof(struct mail_tree_node); - - memset(&tree->node_base[x], 0, sizeof(struct mail_tree_node)); - return x; -} - -static void -rb_move(struct mail_tree *tree, unsigned int src, unsigned int dest) -{ - struct mail_tree_node *node = tree->node_base; - - /* update parent */ - if (node[src].up != RBNULL) { - if (node[node[src].up].left == src) - node[node[src].up].left = dest; - else if (node[node[src].up].right == src) - node[node[src].up].right = dest; - } - - /* update children */ - if (node[src].left != RBNULL) - node[node[src].left].up = dest; - if (node[src].right != RBNULL) - node[node[src].right].up = dest; - - /* update root */ - if (tree->header->root == src) - tree->header->root = dest; - - memcpy(&node[dest], &node[src], sizeof(struct mail_tree_node)); - memset(&node[src], 0, sizeof(struct mail_tree_node)); -} - -static void -rb_free(struct mail_tree *tree, unsigned int x) -{ - unsigned int last; - - i_assert(tree->mmap_used_length >= - sizeof(struct mail_tree_header) + - sizeof(struct mail_tree_node)); - - /* get index to last used record */ - last = (tree->mmap_used_length - sizeof(struct mail_tree_header)) / - sizeof(struct mail_tree_node) - 1; - - if (last != x) { - /* move it over the one we want free'd */ - rb_move(tree, last, x); - } - - /* mark the moved node unused */ - tree->mmap_used_length -= sizeof(struct mail_tree_node); - tree->header->used_file_size -= sizeof(struct mail_tree_node); - - _mail_tree_truncate(tree); -} - -/* -** Rotate our tree thus:- -** -** X rb_left_rotate(X)---> Y -** / \ / \ -** A Y <---rb_right_rotate(Y) X C -** / \ / \ -** B C A B -** -** N.B. This does not change the ordering. -** -** We assume that neither X or Y is NULL -** -** Node count changes: -** X += C+1 X -= C+1 -** Y -= A+1 Y += A+1 -*/ - -static void -rb_left_rotate(struct mail_tree *tree, unsigned int x) -{ - struct mail_tree_node *node = tree->node_base; - unsigned int y, a_nodes, c_nodes; - - i_assert(x != RBNULL); - i_assert(node[x].right != RBNULL); - - y = node[x].right; - a_nodes = NODE_COUNT(node[node[x].left]); - c_nodes = NODE_COUNT(node[node[y].right]); - - /* Turn Y's left subtree into X's right subtree (move B) */ - node[x].right = node[y].left; - - /* If B is not null, set it's parent to be X */ - if (node[y].left != RBNULL) - node[node[y].left].up = x; - - /* Set Y's parent to be what X's parent was */ - node[y].up = node[x].up; - - /* if X was the root */ - if (node[x].up == RBNULL) { - tree->header->root = y; - } else { - /* Set X's parent's left or right pointer to be Y */ - if (x == node[node[x].up].left) - node[node[x].up].left = y; - else - node[node[x].up].right = y; - } - - /* Put X on Y's left */ - node[y].left = x; - - /* Set X's parent to be Y */ - node[x].up = y; - - /* Update node counts */ - NODE_COUNT_ADD(node[x], -(c_nodes+1)); - NODE_COUNT_ADD(node[y], a_nodes+1); -} - -static void -rb_right_rotate(struct mail_tree *tree, unsigned int y) -{ - struct mail_tree_node *node = tree->node_base; - unsigned int x, a_nodes, c_nodes; - - i_assert(y != RBNULL); - i_assert(node[y].left != RBNULL); - - x = node[y].left; - a_nodes = NODE_COUNT(node[node[x].left]); - c_nodes = NODE_COUNT(node[node[y].right]); - - /* Turn X's right subtree into Y's left subtree (move B) */ - node[y].left = node[x].right; - - /* If B is not null, set it's parent to be Y */ - if (node[x].right != RBNULL) - node[node[x].right].up = y; - - /* Set X's parent to be what Y's parent was */ - node[x].up = node[y].up; - - /* if Y was the root */ - if (node[y].up == RBNULL) - tree->header->root = x; - else { - /* Set Y's parent's left or right pointer to be X */ - if (y == node[node[y].up].left) - node[node[y].up].left = x; - else - node[node[y].up].right = x; - } - - /* Put Y on X's right */ - node[x].right = y; - - /* Set Y's parent to be X */ - node[y].up = x; - - /* Update node counts */ - NODE_COUNT_ADD(node[x], c_nodes+1); - NODE_COUNT_ADD(node[y], -(a_nodes+1)); -} - -/* Return index to the smallest key greater than x -*/ -static unsigned int -rb_successor(struct mail_tree *tree, unsigned int x) -{ - struct mail_tree_node *node = tree->node_base; - unsigned int y; - - if (node[x].right != RBNULL) { - /* If right is not NULL then go right one and - ** then keep going left until we find a node with - ** no left pointer. - */ - y = node[x].right; - while (node[y].left != RBNULL) - y = node[y].left; - } else { - /* Go up the tree until we get to a node that is on the - ** left of its parent (or the root) and then return the - ** parent. - */ - y = node[x].up; - while (y != RBNULL && x == node[y].right) { - x = y; - y = node[y].up; - } - } - - return y; -} - -/* Restore the reb-black properties after insert */ -static int -rb_insert_fix(struct mail_tree *tree, unsigned int z) -{ - struct mail_tree_node *node = tree->node_base; - unsigned int x, y, x_up_up; - - /* color this new node red */ - NODE_SET_RED(node[z]); - - /* Having added a red node, we must now walk back up the tree balancing - ** it, by a series of rotations and changing of colors - */ - x = z; - - /* While we are not at the top and our parent node is red - ** N.B. Since the root node is garanteed black, then we - ** are also going to stop if we are the child of the root - */ - - while (x != tree->header->root && IS_NODE_RED(node[node[x].up])) { - /* if our parent is on the left side of our grandparent */ - x_up_up = node[node[x].up].up; - if (node[x].up == node[x_up_up].left) { - /* get the right side of our grandparent (uncle?) */ - y = node[x_up_up].right; - if (IS_NODE_RED(node[y])) { - /* make our parent black */ - NODE_SET_BLACK(node[node[x].up]); - /* make our uncle black */ - NODE_SET_BLACK(node[y]); - /* make our grandparent red */ - NODE_SET_RED(node[x_up_up]); - - /* now consider our grandparent */ - x = x_up_up; - } else { - /* if we are on the right side of our parent */ - if (x == node[node[x].up].right) { - /* Move up to our parent */ - x = node[x].up; - rb_left_rotate(tree, x); - } - - /* make our parent black */ - NODE_SET_BLACK(node[node[x].up]); - /* make our grandparent red */ - NODE_SET_RED(node[x_up_up]); - /* right rotate our grandparent */ - rb_right_rotate(tree, x_up_up); - } - } else { - /* everything here is the same as above, but - ** exchanging left for right - */ - - y = node[x_up_up].left; - if (IS_NODE_RED(node[y])) { - NODE_SET_BLACK(node[node[x].up]); - NODE_SET_BLACK(node[y]); - NODE_SET_RED(node[x_up_up]); - - x = x_up_up; - } else { - if (x == node[node[x].up].left) { - x = node[x].up; - rb_right_rotate(tree, x); - } - - NODE_SET_BLACK(node[node[x].up]); - NODE_SET_RED(node[x_up_up]); - rb_left_rotate(tree, x_up_up); - } - } - } - - /* Set the root node black */ - NODE_SET_BLACK(node[tree->header->root]); - return z; -} - -/* Restore the reb-black properties after a delete */ -static void -rb_delete_fix(struct mail_tree *tree, unsigned int x) -{ - struct mail_tree_node *node = tree->node_base; - unsigned int w; - - while (x != tree->header->root && IS_NODE_BLACK(node[x])) { - if (x == node[node[x].up].left) { - w = node[node[x].up].right; - if (IS_NODE_RED(node[w])) { - NODE_SET_BLACK(node[w]); - NODE_SET_RED(node[node[x].up]); - rb_left_rotate(tree, node[x].up); - w = node[node[x].up].right; - } - - if (IS_NODE_BLACK(node[node[w].left]) && - IS_NODE_BLACK(node[node[w].right])) { - NODE_SET_RED(node[w]); - x = node[x].up; - } else { - if (IS_NODE_BLACK(node[node[w].right])) { - NODE_SET_BLACK(node[node[w].left]); - NODE_SET_RED(node[w]); - rb_right_rotate(tree, w); - w = node[node[x].up].right; - } - - - NODE_COPY_COLOR(node[w], node[node[x].up]); - NODE_SET_BLACK(node[node[x].up]); - NODE_SET_BLACK(node[node[w].right]); - rb_left_rotate(tree, node[x].up); - x = tree->header->root; - } - } else { - w = node[node[x].up].left; - if (IS_NODE_RED(node[w])) { - NODE_SET_BLACK(node[w]); - NODE_SET_RED(node[node[x].up]); - rb_right_rotate(tree, node[x].up); - w = node[node[x].up].left; - } - - if (IS_NODE_BLACK(node[node[w].right]) && - IS_NODE_BLACK(node[node[w].left])) { - NODE_SET_RED(node[w]); - x = node[x].up; - } else { - if (IS_NODE_BLACK(node[node[w].left])) { - NODE_SET_BLACK(node[node[w].right]); - NODE_SET_RED(node[w]); - rb_left_rotate(tree, w); - w = node[node[x].up].left; - } - - NODE_COPY_COLOR(node[w], node[node[x].up]); - NODE_SET_BLACK(node[node[x].up]); - NODE_SET_BLACK(node[node[w].left]); - rb_right_rotate(tree, node[x].up); - x = tree->header->root; - } - } - } - - NODE_SET_BLACK(node[x]); -} - -/* -** case 1 - only one child: -** -** Z --> Y -** / -** Y -** -** Node count changes: -** parents -= 1 -** -** case 2 - right child has no left child: -** -** Z Y -** / \ / \ -** A Y --> A X -** \ -** X -** -** Node count changes: -** parents -= 1 -** Y = Z-1 -** -** case 3 - right child has left child: -** -** Z Y -** / \ / \ -** A B --> A B -** / / -** .. .. -** / / -** Y X -** \ -** X -** -** Node count changes: -** parents -= 1 -** Y = Z-1 -** B .. X.up -= 1 (NOTE: X may not exist) -*/ - -/* Delete the node z, and free up the space -*/ -static void -rb_delete(struct mail_tree *tree, unsigned int z) -{ - struct mail_tree_node *node = tree->node_base; - unsigned int x, y, b; - - if (node[z].left == RBNULL || node[z].right == RBNULL) { - y = z; - b = RBNULL; - } else { - y = rb_successor(tree, z); - if (y == node[z].right) - b = RBNULL; - else - b = node[z].right; - } - - if (node[y].left != RBNULL) - x = node[y].left; - else - x = node[y].right; - - /* this may modify RBNULL, which IMHO is a bit nasty, - but rb_delete_fix() requires it to work properly. */ - node[x].up = node[y].up; - - if (node[y].up == RBNULL) { - tree->header->root = x; - } else { - if (y == node[node[y].up].left) - node[node[y].up].left = x; - else - node[node[y].up].right = x; - } - - if (y != z) { - node[z].key = node[y].key; - node[z].value = node[y].value; - } - - if (b != RBNULL) { - /* case 3 updates */ - while (b != x) { - NODE_COUNT_ADD(node[b], -1); - b = node[b].left; - } - } - - while (z != RBNULL) { - NODE_COUNT_ADD(node[z], -1); - z = node[z].up; - } - - if (IS_NODE_BLACK(node[y])) - rb_delete_fix(tree, x); - - rb_free(tree, y); -} - -#ifdef DEBUG_TREE -int -rb_check1(struct mail_tree *tree, unsigned int x) -{ - struct mail_tree_node *node = tree->node_base; - - if (IS_NODE_RED(node[x])) { - if (!IS_NODE_BLACK(node[node[x].left]) || - !IS_NODE_BLACK(node[node[x].right])) { - i_error("Children of red node not both black, x=%u", x); - return -1; - } - } - - if (node[x].left != RBNULL) { - if (node[node[x].left].up != x) { - i_error("x->left->up != x, x=%u", x); - return -1; - } - - if (rb_check1(tree, node[x].left)) - return -1; - } - - if (node[x].right != RBNULL) { - if (node[node[x].right].up != x) { - i_error("x->right->up != x, x=%u", x); - return -1; - } - - if (rb_check1(tree, node[x].right)) - return -1; - } - - return 0; -} - -int count_black(struct mail_tree *tree, unsigned int x) -{ - struct mail_tree_node *node = tree->node_base; - int nleft, nright; - - if (x == RBNULL) - return 1; - - nleft = count_black(tree, node[x].left); - nright = count_black(tree, node[x].right); - - if (nleft == -1 || nright == -1) - return -1; - - if (nleft != nright) { - i_error("Black count not equal on left & right, x=%u", x); - return -1; - } - - if (IS_NODE_BLACK(node[x])) - nleft++; - - return nleft; -} - -int count_nodes(struct mail_tree *tree, unsigned int x) -{ - struct mail_tree_node *node = tree->node_base; - int nleft, nright; - - if (x == RBNULL) - return 0; - - nleft = count_nodes(tree, node[x].left); - nright = count_nodes(tree, node[x].right); - - if (nleft == -1 || nright == -1) - return -1; - - if (nleft+nright+1 != (int)NODE_COUNT(node[x])) { - i_error("Invalid node count, x=%u, %d+%d+1 != %u", - x, nleft, nright, NODE_COUNT(node[x])); - return -1; - } - - return nleft+nright+1; -} - -void dumptree(struct mail_tree *tree, unsigned int x, int n) -{ - struct mail_tree_node *node = tree->node_base; - - if (x != RBNULL) { - n++; - i_error("Tree: %*s %u: left=%u, right=%u, color=%s, " - "nodes=%u, key=%u", - n, "", x, node[x].left, node[x].right, - IS_NODE_BLACK(node[x]) ? "BLACK" : "RED", - NODE_COUNT(node[x]), node[x].key); - - dumptree(tree, node[x].left, n); - dumptree(tree, node[x].right, n); - } -} - -int -rb_check(struct mail_tree *tree) -{ - struct mail_tree_node *node = tree->node_base; - unsigned int root; - - root = tree->header->root; - if (root == RBNULL) - return 0; - - if (node[root].up != RBNULL) { - i_error("Root up pointer not RBNULL"); - dumptree(tree, root, 0); - return 1; - } - - if (rb_check1(tree, root)) { - dumptree(tree, root, 0); - return 1; - } - - if (count_black(tree, root) == -1) { - dumptree(tree, root, 0); - return -1; - } - - if (count_nodes(tree, root) == -1) { - dumptree(tree, root, 0); - return -1; - } - - return 0; -} -#endif - -unsigned int mail_tree_lookup_uid_range(struct mail_tree *tree, - unsigned int *seq_r, - unsigned int first_uid, - unsigned int last_uid) -{ - struct mail_tree_node *node; - unsigned int x, y, seq; - - i_assert(first_uid > 0 && last_uid > 0); - i_assert(first_uid <= last_uid); - i_assert(tree->index->lock_type != MAIL_LOCK_UNLOCK); - - if (!_mail_tree_mmap_update(tree, FALSE)) - return (unsigned int)-1; - - rb_check(tree); - node = tree->node_base; - - if (seq_r != NULL) - *seq_r = 0; - - y = RBNULL; /* points to the parent of x */ - x = tree->header->root; - - /* walk x down the tree */ - seq = 0; - while (x != RBNULL) { - y = x; - - if (first_uid < node[x].key) - x = node[x].left; - else { - seq += NODE_COUNT(node[node[x].left])+1; - if (first_uid > node[x].key) - x = node[x].right; - else { - /* found it */ - if (seq_r != NULL) - *seq_r = seq; - return node[x].value; - } - } - } - - if (first_uid != last_uid) { - /* get the next key, make sure it's in range */ - if (node[y].key > first_uid) - x = y; - else - x = rb_successor(tree, y); - - if (node[x].key > last_uid) - x = RBNULL; - else { - if (seq_r != NULL) - *seq_r = seq+1; - } - } - - return x == RBNULL ? (unsigned int)-1 : node[x].value; -} - -unsigned int mail_tree_lookup_sequence(struct mail_tree *tree, unsigned int seq) -{ - struct mail_tree_node *node; - unsigned int x, upleft_nodes, left_nodes; - - i_assert(seq != 0); - i_assert(tree->index->lock_type != MAIL_LOCK_UNLOCK); - - if (!_mail_tree_mmap_update(tree, FALSE)) - return (unsigned int)-1; - - rb_check(tree); - node = tree->node_base; - - x = tree->header->root; - - /* walk x down the tree */ - seq--; - upleft_nodes = left_nodes = 0; - while (x != RBNULL) { - left_nodes = upleft_nodes + NODE_COUNT(node[node[x].left]); - - if (seq < left_nodes) - x = node[x].left; - else if (seq > left_nodes) { - upleft_nodes = left_nodes+1; - x = node[x].right; - } else { - /* found it */ - return node[x].value; - } - } - - return (unsigned int)-1; -} - -int mail_tree_insert(struct mail_tree *tree, - unsigned int uid, unsigned int index) -{ - struct mail_tree_node *node; - unsigned int x, z; - - i_assert(uid != 0); - i_assert(tree->index->lock_type == MAIL_LOCK_EXCLUSIVE); - - if (!_mail_tree_mmap_update(tree, FALSE)) - return FALSE; - - node = tree->node_base; - - /* we'll always insert to right side of the tree */ - x = tree->header->root; - if (x != RBNULL) { - while (node[x].right != RBNULL) - x = node[x].right; - } - - if (node[x].key >= uid) { - _mail_tree_set_corrupted(tree, - "UID to be inserted isn't higher than existing " - "(%u <= %u)", uid, node[x].key); - return FALSE; - } - - if ((z = rb_alloc(tree)) == RBNULL) - return FALSE; - - /* rb_alloc() may change mmap base */ - node = tree->node_base; - - node[z].key = uid; - node[z].value = index; - node[z].up = x; - node[z].node_count = 1; - node[z].left = RBNULL; - node[z].right = RBNULL; - - if (x == RBNULL) - tree->header->root = z; - else { - if (node[z].key < node[x].key) - node[x].left = z; - else - node[x].right = z; - } - - for (; x != RBNULL; x = node[x].up) - NODE_COUNT_ADD(node[x], 1); - - rb_insert_fix(tree, z); - rb_check(tree); - - tree->modified = TRUE; - return TRUE; -} - -int mail_tree_update(struct mail_tree *tree, - unsigned int uid, unsigned int index) -{ - struct mail_tree_node *node; - unsigned int x; - - i_assert(uid != 0); - i_assert(tree->index->lock_type == MAIL_LOCK_EXCLUSIVE); - - if (!_mail_tree_mmap_update(tree, FALSE)) - return FALSE; - - rb_check(tree); - node = tree->node_base; - - tree->modified = TRUE; - - x = tree->header->root; - while (x != RBNULL) { - if (uid < node[x].key) - x = node[x].left; - else if (uid > node[x].key) - x = node[x].right; - else { - /* found it */ - node[x].value = index; - return TRUE; - } - } - - _mail_tree_set_corrupted(tree, "Tried to update nonexisting UID %u", - uid); - return FALSE; -} - -void mail_tree_delete(struct mail_tree *tree, unsigned int uid) -{ - struct mail_tree_node *node; - unsigned int x; - - i_assert(uid != 0); - i_assert(tree->index->lock_type == MAIL_LOCK_EXCLUSIVE); - - if (!_mail_tree_mmap_update(tree, FALSE)) - return; - - node = tree->node_base; - - x = tree->header->root; - while (x != RBNULL) { - if (uid < node[x].key) - x = node[x].left; - else if (uid > node[x].key) - x = node[x].right; - else { - /* found it */ - rb_delete(tree, x); - rb_check(tree); - break; - } - } - - tree->modified = TRUE; -} diff --git a/src/lib-index/mail-tree.c b/src/lib-index/mail-tree.c deleted file mode 100644 index e6a1353cc6..0000000000 --- a/src/lib-index/mail-tree.c +++ /dev/null @@ -1,461 +0,0 @@ -/* Copyright (C) 2002 Timo Sirainen */ - -#include "lib.h" -#include "mmap-util.h" -#include "file-set-size.h" -#include "write-full.h" -#include "mail-index.h" -#include "mail-index-util.h" -#include "mail-tree.h" - -#include -#include - -#define MAIL_TREE_MIN_SIZE \ - (sizeof(struct mail_tree_header) + \ - INDEX_MIN_RECORDS_COUNT * sizeof(struct mail_tree_node)) - -static int tree_set_syscall_error(struct mail_tree *tree, const char *function) -{ - i_assert(function != NULL); - - if (ENOSPACE(errno)) { - tree->index->nodiskspace = TRUE; - return FALSE; - } - - index_set_error(tree->index, "%s failed with binary tree file %s: %m", - function, tree->filepath); - return FALSE; -} - -int _mail_tree_set_corrupted(struct mail_tree *tree, const char *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - t_push(); - - index_set_error(tree->index, "Corrupted binary tree file %s: %s", - tree->filepath, t_strdup_vprintf(fmt, va)); - - t_pop(); - va_end(va); - - /* make sure we don't get back here */ - tree->index->inconsistent = TRUE; - (void)unlink(tree->filepath); - - return FALSE; -} - -static int mmap_update(struct mail_tree *tree) -{ - i_assert(!tree->anon_mmap); - - if (tree->mmap_base != NULL) { - /* make sure we're synced before munmap() */ - if (!tree->anon_mmap && tree->modified && - msync(tree->mmap_base, tree->mmap_highwater, MS_SYNC) < 0) - return tree_set_syscall_error(tree, "msync()"); - tree->modified = FALSE; - - if (munmap(tree->mmap_base, tree->mmap_full_length) < 0) - tree_set_syscall_error(tree, "munmap()"); - } - - tree->mmap_used_length = 0; - tree->header = NULL; - tree->node_base = NULL; - - tree->mmap_base = mmap_rw_file(tree->fd, &tree->mmap_full_length); - if (tree->mmap_base == MAP_FAILED) { - tree->mmap_base = NULL; - return tree_set_syscall_error(tree, "mmap()"); - } - - debug_mprotect(tree->mmap_base, tree->mmap_full_length, tree->index); - return TRUE; -} - -static int mmap_verify(struct mail_tree *tree) -{ - struct mail_tree_header *hdr; - unsigned int extra; - - if (tree->mmap_full_length < - sizeof(struct mail_tree_header) + sizeof(struct mail_tree_node)) { - index_set_error(tree->index, "Too small binary tree file %s", - tree->filepath); - (void)unlink(tree->filepath); - return FALSE; - } - - extra = (tree->mmap_full_length - sizeof(struct mail_tree_header)) % - sizeof(struct mail_tree_node); - - if (extra != 0) { - /* partial write or corrupted - - truncate the file to valid length */ - tree->mmap_full_length -= extra; - if (ftruncate(tree->fd, (off_t)tree->mmap_full_length) < 0) - tree_set_syscall_error(tree, "ftruncate()"); - } - - hdr = tree->mmap_base; - if (hdr->used_file_size > tree->mmap_full_length) { - _mail_tree_set_corrupted(tree, - "used_file_size larger than real file size " - "(%"PRIuUOFF_T" vs %"PRIuSIZE_T")", - hdr->used_file_size, tree->mmap_full_length); - return FALSE; - } - - if (hdr->used_file_size < sizeof(struct mail_tree_header) || - (hdr->used_file_size - sizeof(struct mail_tree_header)) % - sizeof(struct mail_tree_node) != 0) { - _mail_tree_set_corrupted(tree, - "Invalid used_file_size in header (%"PRIuUOFF_T")", - hdr->used_file_size); - return FALSE; - } - - tree->header = tree->mmap_base; - tree->node_base = (struct mail_tree_node *) - ((char *) tree->mmap_base + sizeof(struct mail_tree_header)); - tree->sync_id = hdr->sync_id; - tree->mmap_used_length = hdr->used_file_size; - tree->mmap_highwater = tree->mmap_used_length; - return TRUE; -} - -int _mail_tree_mmap_update(struct mail_tree *tree, int forced) -{ - if (tree->index->mmap_invalidate && tree->mmap_base != NULL) { - if (msync(tree->mmap_base, tree->mmap_used_length, - MS_SYNC | MS_INVALIDATE) < 0) - return tree_set_syscall_error(tree, "msync()"); - } - - debug_mprotect(tree->mmap_base, tree->mmap_full_length, - tree->index); - - if (!forced && tree->header != NULL && - tree->sync_id == tree->header->sync_id) { - /* make sure file size hasn't changed */ - tree->mmap_used_length = tree->header->used_file_size; - if (tree->mmap_used_length > tree->mmap_full_length) { - i_panic("Tree file size was grown without " - "updating sync_id"); - } - - return TRUE; - } - - return mmap_update(tree) && mmap_verify(tree); -} - -static struct mail_tree *mail_tree_open(struct mail_index *index) -{ - struct mail_tree *tree; - const char *path; - int fd; - - path = t_strconcat(index->filepath, ".tree", NULL); - fd = open(path, O_RDWR | O_CREAT, 0660); - if (fd == -1) { - index_file_set_syscall_error(index, path, "open()"); - return NULL; - } - - tree = i_new(struct mail_tree, 1); - tree->fd = fd; - tree->index = index; - tree->filepath = i_strdup(path); - - index->tree = tree; - return tree; -} - -static struct mail_tree *mail_tree_create_anon(struct mail_index *index) -{ - struct mail_tree *tree; - - tree = i_new(struct mail_tree, 1); - tree->anon_mmap = TRUE; - tree->fd = -1; - tree->index = index; - tree->filepath = i_strdup_printf("(in-memory tree index for %s)", - index->mailbox_path); - - index->tree = tree; - return tree; -} - -int mail_tree_create(struct mail_index *index) -{ - struct mail_tree *tree; - - i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE); - - tree = !INDEX_IS_IN_MEMORY(index) ? mail_tree_open(index) : - mail_tree_create_anon(index); - if (tree == NULL) - return FALSE; - - if (!mail_tree_rebuild(tree)) { - mail_tree_free(tree); - return FALSE; - } - - return TRUE; -} - -static int mail_tree_open_init(struct mail_tree *tree) -{ - if (!mmap_update(tree)) - return FALSE; - - if (tree->mmap_full_length == 0) { - /* just created it */ - return FALSE; - } - - if (!mmap_verify(tree)) { - /* broken header */ - return FALSE; - } - - if (tree->header->indexid != tree->index->indexid) { - index_set_error(tree->index, - "IndexID mismatch for binary tree file %s", - tree->filepath); - - return FALSE; - } - - return TRUE; -} - -int mail_tree_open_or_create(struct mail_index *index) -{ - struct mail_tree *tree; - - tree = mail_tree_open(index); - if (tree == NULL) - return FALSE; - - if (!mail_tree_open_init(tree)) { - /* lock and check again, just to avoid rebuilding it twice - if two processes notice the error at the same time */ - if (!tree->index->set_lock(tree->index, MAIL_LOCK_EXCLUSIVE)) { - mail_tree_free(tree); - return FALSE; - } - - if (!mail_tree_open_init(tree)) { - if (!mail_tree_rebuild(tree)) { - mail_tree_free(tree); - return FALSE; - } - } - } - - return TRUE; -} - -static void mail_tree_close(struct mail_tree *tree) -{ - if (tree->anon_mmap) { - if (munmap_anon(tree->mmap_base, tree->mmap_full_length) < 0) - tree_set_syscall_error(tree, "munmap_anon()"); - } else if (tree->mmap_base != NULL) { - if (munmap(tree->mmap_base, tree->mmap_full_length) < 0) - tree_set_syscall_error(tree, "munmap()"); - } - tree->mmap_base = NULL; - tree->mmap_full_length = 0; - tree->mmap_used_length = 0; - tree->header = NULL; - - if (tree->fd != -1) { - if (close(tree->fd) < 0) - tree_set_syscall_error(tree, "close()"); - tree->fd = -1; - } - - i_free(tree->filepath); -} - -void mail_tree_free(struct mail_tree *tree) -{ - tree->index->tree = NULL; - - mail_tree_close(tree); - i_free(tree); -} - -static int mail_tree_init(struct mail_tree *tree) -{ - struct mail_tree_header hdr; - - /* first node is always used, and is the RBNULL node */ - memset(&hdr, 0, sizeof(struct mail_tree_header)); - hdr.indexid = tree->index->indexid; - hdr.used_file_size = sizeof(struct mail_tree_header) + - sizeof(struct mail_tree_node); - - if (tree->anon_mmap) { - tree->mmap_full_length = MAIL_TREE_MIN_SIZE; - tree->mmap_base = mmap_anon(tree->mmap_full_length); - if (tree->mmap_base == MAP_FAILED) - return tree_set_syscall_error(tree, "mmap_anon()"); - - memcpy(tree->mmap_base, &hdr, sizeof(struct mail_tree_header)); - return mmap_verify(tree); - } - - if (lseek(tree->fd, 0, SEEK_SET) < 0) - return tree_set_syscall_error(tree, "lseek()"); - - if (write_full(tree->fd, &hdr, sizeof(hdr)) < 0) - return tree_set_syscall_error(tree, "write_full()"); - - if (file_set_size(tree->fd, MAIL_TREE_MIN_SIZE) < 0) - return tree_set_syscall_error(tree, "file_set_size()"); - - return TRUE; -} - -int mail_tree_reset(struct mail_tree *tree) -{ - i_assert(tree->index->lock_type == MAIL_LOCK_EXCLUSIVE); - - if (!mail_tree_init(tree) || - (!tree->anon_mmap && !_mail_tree_mmap_update(tree, TRUE))) { - tree->index->header->flags |= MAIL_INDEX_FLAG_REBUILD_TREE; - return FALSE; - } - - return TRUE; -} - -int mail_tree_rebuild(struct mail_tree *tree) -{ - struct mail_index_record *rec; - - if (!tree->index->set_lock(tree->index, MAIL_LOCK_EXCLUSIVE)) - return FALSE; - - if (!mail_tree_reset(tree)) - return FALSE; - - rec = tree->index->lookup(tree->index, 1); - while (rec != NULL) { - if (!mail_tree_insert(tree, rec->uid, - INDEX_RECORD_INDEX(tree->index, rec))) { - tree->index->header->flags |= - MAIL_INDEX_FLAG_REBUILD_TREE; - return FALSE; - } - - rec = tree->index->next(tree->index, rec); - } - - return TRUE; -} - -int mail_tree_sync_file(struct mail_tree *tree, int *fsync_fd) -{ - *fsync_fd = -1; - - if (!tree->modified || tree->anon_mmap) - return TRUE; - - i_assert(tree->mmap_base != NULL); - - if (msync(tree->mmap_base, tree->mmap_highwater, MS_SYNC) < 0) - return tree_set_syscall_error(tree, "msync()"); - - tree->mmap_highwater = tree->mmap_used_length; - tree->modified = FALSE; - - *fsync_fd = tree->fd; - return TRUE; -} - -int _mail_tree_grow(struct mail_tree *tree) -{ - uoff_t new_fsize; - unsigned int grow_count; - void *base; - - grow_count = tree->index->header->messages_count * - INDEX_GROW_PERCENTAGE / 100; - if (grow_count < 16) - grow_count = 16; - - new_fsize = (uoff_t)tree->mmap_full_length + - (grow_count * sizeof(struct mail_tree_node)); - i_assert(new_fsize < OFF_T_MAX); - - if (tree->anon_mmap) { - i_assert(new_fsize < SSIZE_T_MAX); - - base = mremap_anon(tree->mmap_base, tree->mmap_full_length, - (size_t)new_fsize, MREMAP_MAYMOVE); - if (base == MAP_FAILED) - return tree_set_syscall_error(tree, "mremap_anon()"); - - tree->mmap_base = base; - tree->mmap_full_length = (size_t)new_fsize; - return mmap_verify(tree); - } - - if (file_set_size(tree->fd, (off_t)new_fsize) < 0) - return tree_set_syscall_error(tree, "file_set_size()"); - - /* file size changed, let others know about it too by changing - sync_id in header. */ - tree->header->sync_id++; - tree->modified = TRUE; - - if (!_mail_tree_mmap_update(tree, TRUE)) - return FALSE; - - return TRUE; -} - -void _mail_tree_truncate(struct mail_tree *tree) -{ - /* pretty much copy&pasted from mail_index_compress() */ - uoff_t empty_space, truncate_threshold; - - i_assert(tree->index->lock_type == MAIL_LOCK_EXCLUSIVE); - - if (tree->mmap_full_length <= MAIL_TREE_MIN_SIZE || tree->anon_mmap) - return; - - empty_space = tree->mmap_full_length - tree->mmap_used_length; - - truncate_threshold = - tree->mmap_full_length / 100 * INDEX_TRUNCATE_PERCENTAGE; - - if (empty_space > truncate_threshold) { - tree->mmap_full_length = tree->mmap_used_length + - (empty_space * INDEX_TRUNCATE_KEEP_PERCENTAGE / 100); - - /* keep the size record-aligned */ - tree->mmap_full_length -= (tree->mmap_full_length - - sizeof(struct mail_tree_header)) % - sizeof(struct mail_tree_node); - - if (tree->mmap_full_length < MAIL_TREE_MIN_SIZE) - tree->mmap_full_length = MAIL_TREE_MIN_SIZE; - - if (ftruncate(tree->fd, (off_t)tree->mmap_full_length) < 0) - tree_set_syscall_error(tree, "ftruncate()"); - - tree->header->sync_id++; - } -} diff --git a/src/lib-index/mail-tree.h b/src/lib-index/mail-tree.h deleted file mode 100644 index 775c8c7da3..0000000000 --- a/src/lib-index/mail-tree.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef __MAIL_TREE_H -#define __MAIL_TREE_H - -struct mail_tree { - struct mail_index *index; - - int fd; - char *filepath; - - void *mmap_base; - struct mail_tree_node *node_base; - size_t mmap_used_length; - size_t mmap_full_length; - size_t mmap_highwater; /* for msync()ing */ - - struct mail_tree_header *header; - unsigned int sync_id; - - unsigned int anon_mmap:1; - unsigned int modified:1; -}; - -struct mail_tree_header { - unsigned int indexid; - unsigned int sync_id; - - uoff_t used_file_size; - - unsigned int root; -}; - -struct mail_tree_node { - unsigned int left; - unsigned int right; - unsigned int up; - - /* number of child nodes + 1, used to figure out message - sequence numbers. also highest bit specifies if the node is - red or black */ - unsigned int node_count; - - unsigned int key; - unsigned int value; -}; - -int mail_tree_create(struct mail_index *index); -int mail_tree_open_or_create(struct mail_index *index); -void mail_tree_free(struct mail_tree *tree); - -int mail_tree_reset(struct mail_tree *tree); -int mail_tree_rebuild(struct mail_tree *tree); -int mail_tree_sync_file(struct mail_tree *tree, int *fsync_fd); - -/* Find first existing UID in range. Returns (unsigned int)-1 if not found. */ -unsigned int mail_tree_lookup_uid_range(struct mail_tree *tree, - unsigned int *seq_r, - unsigned int first_uid, - unsigned int last_uid); - -/* Find message by sequence number. Returns (unsigned int)-1 if not found. */ -unsigned int mail_tree_lookup_sequence(struct mail_tree *tree, - unsigned int seq); - -/* Insert a new record in tree. */ -int mail_tree_insert(struct mail_tree *tree, - unsigned int uid, unsigned int index); - -/* Update existing record in tree. */ -int mail_tree_update(struct mail_tree *tree, - unsigned int uid, unsigned int index); - -/* Delete record from tree. */ -void mail_tree_delete(struct mail_tree *tree, unsigned int uid); - -/* private: */ -int _mail_tree_set_corrupted(struct mail_tree *tree, const char *fmt, ...) - __attr_format__(2, 3); -int _mail_tree_mmap_update(struct mail_tree *tree, int forced); -int _mail_tree_grow(struct mail_tree *tree); -void _mail_tree_truncate(struct mail_tree *tree); - -#endif diff --git a/src/lib-index/maildir/maildir-rebuild.c b/src/lib-index/maildir/maildir-rebuild.c index 23604ab86d..dee9c6779c 100644 --- a/src/lib-index/maildir/maildir-rebuild.c +++ b/src/lib-index/maildir/maildir-rebuild.c @@ -38,11 +38,6 @@ int maildir_index_rebuild(struct mail_index *index) if (!mail_index_data_reset(index->data)) return FALSE; - if (index->tree != NULL) { - if (!mail_tree_reset(index->tree)) - return FALSE; - } - /* read the mails by syncing */ if (!index->sync_and_lock(index, FALSE, MAIL_LOCK_UNLOCK, NULL)) return FALSE; diff --git a/src/lib-index/maildir/maildir-sync.c b/src/lib-index/maildir/maildir-sync.c index a3e5070460..6bba31c9ed 100644 --- a/src/lib-index/maildir/maildir-sync.c +++ b/src/lib-index/maildir/maildir-sync.c @@ -468,14 +468,14 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx) { struct mail_index *index = ctx->index; struct maildir_uidlist *uidlist; - struct mail_index_record *rec; + struct mail_index_record *rec, *first_rec, *last_rec; struct maildir_hash_rec *hash_rec; struct maildir_uidlist_rec uid_rec; enum maildir_file_action action; const char *fname, **new_files, *dir; void *orig_key, *orig_value; - unsigned int seq, uid, last_uid, i, new_flag; - int new_dir; + unsigned int seq, first_seq, last_seq, uid, last_uid, i, new_flag; + int new_dir, skip_next; buffer_t *buf; if (ctx->new_count > 0) { @@ -489,7 +489,7 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx) return FALSE; } - seq = 0; + seq = 1; rec = index->lookup(index, 1); uidlist = ctx->uidlist; @@ -500,8 +500,11 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx) return FALSE; } + first_rec = last_rec = NULL; + first_seq = last_seq = 0; + skip_next = FALSE; while (rec != NULL) { - seq++; uid = rec->uid; + uid = rec->uid; /* skip over the expunged records in uidlist */ while (uid_rec.uid != 0 && uid_rec.uid < uid) { @@ -542,26 +545,36 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx) action = hash_rec != NULL ? ACTION(hash_rec) : MAILDIR_FILE_ACTION_NONE; switch (action) { + case MAILDIR_FILE_ACTION_UPDATE_CONTENT: + hash_rec->action = MAILDIR_FILE_ACTION_NEW | + (hash_rec->action & MAILDIR_FILE_FLAGS); + ctx->new_count++; + /* fall through */ case MAILDIR_FILE_ACTION_EXPUNGE: - if (!index->expunge(index, rec, seq, TRUE)) - return FALSE; - seq--; + if (first_rec == NULL) { + first_rec = rec; + first_seq = seq; + } + last_rec = rec; + last_seq = seq; break; case MAILDIR_FILE_ACTION_UPDATE_FLAGS: if (!maildir_update_filename(ctx, rec, orig_key)) return FALSE; if (!maildir_update_flags(ctx, rec, seq, fname)) return FALSE; - break; - case MAILDIR_FILE_ACTION_UPDATE_CONTENT: - if (!index->expunge(index, rec, seq, TRUE)) - return FALSE; - seq--; - hash_rec->action = MAILDIR_FILE_ACTION_NEW | - (hash_rec->action & MAILDIR_FILE_FLAGS); - ctx->new_count++; - break; + /* fall through */ case MAILDIR_FILE_ACTION_NONE: + if (first_rec != NULL) { + if (!index->expunge(index, first_rec, last_rec, + first_seq, last_seq, TRUE)) + return FALSE; + first_rec = NULL; + + seq = first_seq; + rec = index->lookup(index, seq); + skip_next = TRUE; + } break; default: i_panic("BUG: %s/%s suddenly appeared as UID %u", @@ -572,10 +585,23 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx) if (maildir_uidlist_next(uidlist, &uid_rec) < 0) return FALSE; } - rec = index->next(index, rec); + + if (skip_next) + skip_next = FALSE; + else { + rec = index->next(index, rec); + seq++; + } + } + + if (first_rec != NULL) { + if (!index->expunge(index, first_rec, last_rec, + first_seq, last_seq, TRUE)) + return FALSE; + seq = first_seq; } - if (seq != index->header->messages_count) { + if (seq-1 != index->header->messages_count) { index_set_corrupted(index, "Wrong messages_count in header " "(%u != %u)", seq, index->header->messages_count); diff --git a/src/lib-index/mbox/mbox-index.c b/src/lib-index/mbox/mbox-index.c index 977d71f26c..57e9d6f7e0 100644 --- a/src/lib-index/mbox/mbox-index.c +++ b/src/lib-index/mbox/mbox-index.c @@ -793,13 +793,16 @@ static int mbox_index_try_lock(struct mail_index *index, } static int mbox_index_expunge(struct mail_index *index, - struct mail_index_record *rec, - unsigned int seq, int external_change) + struct mail_index_record *first_rec, + struct mail_index_record *last_rec, + unsigned int first_seq, unsigned int last_seq, + int external_change) { - if (!mail_index_expunge(index, rec, seq, external_change)) + if (!mail_index_expunge(index, first_rec, last_rec, + first_seq, last_seq, external_change)) return FALSE; - if (seq == 1) { + if (first_seq == 1) { /* Our message containing X-IMAPbase was deleted. Get it back there. */ index->header->flags |= MAIL_INDEX_FLAG_DIRTY_MESSAGES | diff --git a/src/lib-index/mbox/mbox-sync-full.c b/src/lib-index/mbox/mbox-sync-full.c index 0934ca92a2..9e9833e82d 100644 --- a/src/lib-index/mbox/mbox-sync-full.c +++ b/src/lib-index/mbox/mbox-sync-full.c @@ -127,9 +127,11 @@ static int match_next_record(struct mail_index *index, struct mail_index_update *update; struct message_size hdr_parsed_size; struct mbox_header_context ctx; + struct mail_index_record *first_rec, *last_rec; uoff_t header_offset, body_offset, offset; uoff_t hdr_size, body_size; unsigned char current_digest[16]; + unsigned int first_seq, last_seq; int hdr_size_fixed; *next_rec = NULL; @@ -138,6 +140,8 @@ static int match_next_record(struct mail_index *index, skip_line(input); header_offset = input->v_offset; + first_rec = last_rec = NULL; + first_seq = last_seq = 0; hdr_size = 0; body_offset = 0; hdr_size_fixed = FALSE; do { if (!mbox_mail_get_location(index, rec, NULL, NULL, &body_size)) @@ -221,16 +225,30 @@ static int match_next_record(struct mail_index *index, if (!index->update_end(update)) return FALSE; - - *next_rec = rec; break; } /* try next message */ - (void)index->expunge(index, rec, seq, TRUE); - rec = index->next(index, rec); + if (first_rec == NULL) { + first_rec = rec; + first_seq = seq; + } + last_rec = rec; + last_seq = seq; + + rec = index->next(index, rec); seq++; } while (rec != NULL); + if (first_rec == NULL) + *next_rec = rec == NULL ? NULL : index->next(index, rec); + else { + if (!index->expunge(index, first_rec, last_rec, + first_seq, last_seq, TRUE)) + return FALSE; + + *next_rec = index->lookup(index, first_seq); + } + return TRUE; } @@ -296,14 +314,13 @@ static int mbox_sync_from_stream(struct mail_index *index, } seq++; - rec = index->next(index, rec); } /* delete the rest of the records */ - while (rec != NULL) { - (void)index->expunge(index, rec, seq, TRUE); - - rec = index->next(index, rec); + if (rec != NULL) { + if (!index->expunge(index, rec, INDEX_END_RECORD(index)-1, + seq, index->header->messages_count, TRUE)) + return FALSE; } if (!dirty && (index->header->flags & MAIL_INDEX_FLAG_DIRTY_MESSAGES)) { diff --git a/src/lib-storage/index/index-expunge.c b/src/lib-storage/index/index-expunge.c index afa7223c2f..7a7771f9f0 100644 --- a/src/lib-storage/index/index-expunge.c +++ b/src/lib-storage/index/index-expunge.c @@ -43,20 +43,36 @@ int index_expunge_seek_first(struct index_mailbox *ibox, unsigned int *seq, return TRUE; } -int index_expunge_mail(struct index_mailbox *ibox, - struct mail_index_record *rec, - unsigned int seq, int notify) +int index_expunge_mails(struct index_mailbox *ibox, + struct mail_index_record *first_rec, + struct mail_index_record *last_rec, + unsigned int first_seq, unsigned int last_seq, + int notify) { - if (!ibox->index->expunge(ibox->index, rec, seq, FALSE)) + unsigned int max; + + if (!ibox->index->expunge(ibox->index, first_rec, last_rec, + first_seq, last_seq, FALSE)) return FALSE; - if (seq <= ibox->synced_messages_count) { - if (notify) { - struct mail_storage *storage = ibox->box.storage; - storage->callbacks->expunge(&ibox->box, seq, - storage->callback_context); + if (first_seq > ibox->synced_messages_count) + return TRUE; + + max = last_seq > ibox->synced_messages_count ? + ibox->synced_messages_count : last_seq; + + ibox->synced_messages_count -= max - first_seq + 1; + if (notify) { + struct mail_storage_callbacks *cb; + void *cb_ctx; + + cb = ibox->box.storage->callbacks; + cb_ctx = ibox->box.storage->callback_context; + + while (max >= first_seq) { + cb->expunge(&ibox->box, first_seq, cb_ctx); + max--; } - ibox->synced_messages_count--; } return TRUE; diff --git a/src/lib-storage/index/index-storage.h b/src/lib-storage/index/index-storage.h index 0a5a4469a9..6ce3397dd6 100644 --- a/src/lib-storage/index/index-storage.h +++ b/src/lib-storage/index/index-storage.h @@ -69,9 +69,11 @@ unsigned int index_storage_get_recent_count(struct mail_index *index); int index_expunge_seek_first(struct index_mailbox *ibox, unsigned int *seq, struct mail_index_record **rec); -int index_expunge_mail(struct index_mailbox *ibox, - struct mail_index_record *rec, - unsigned int seq, int notify); +int index_expunge_mails(struct index_mailbox *ibox, + struct mail_index_record *first_rec, + struct mail_index_record *last_rec, + unsigned int first_seq, unsigned int last_seq, + int notify); void index_mailbox_check_add(struct index_mailbox *ibox, const char *path); void index_mailbox_check_remove_all(struct index_mailbox *ibox); diff --git a/src/lib-storage/index/maildir/maildir-expunge.c b/src/lib-storage/index/maildir/maildir-expunge.c index 5550baf705..b28f0f471e 100644 --- a/src/lib-storage/index/maildir/maildir-expunge.c +++ b/src/lib-storage/index/maildir/maildir-expunge.c @@ -6,15 +6,19 @@ int maildir_expunge_locked(struct index_mailbox *ibox, int notify) { - struct mail_index_record *rec; - unsigned int seq; + struct mail_index_record *rec, *first_rec, *last_rec; + unsigned int seq, first_seq, last_seq; int ret, no_permission = FALSE; if (!index_expunge_seek_first(ibox, &seq, &rec)) return FALSE; + first_rec = last_rec = NULL; + first_seq = last_seq = 0; while (rec != NULL) { - if (rec->msg_flags & MAIL_DELETED) { + if ((rec->msg_flags & MAIL_DELETED) == 0) + ret = FALSE; + else { t_push(); ret = maildir_expunge_mail(ibox->index, rec); t_pop(); @@ -23,16 +27,34 @@ int maildir_expunge_locked(struct index_mailbox *ibox, int notify) if (errno != EACCES) return FALSE; no_permission = TRUE; - seq++; } else { - if (!index_expunge_mail(ibox, rec, seq, notify)) - return FALSE; + if (first_rec == NULL) { + first_rec = rec; + first_seq = seq; + } + last_rec = rec; + last_seq = seq; } + } + + if (!ret && first_rec != NULL) { + if (!index_expunge_mails(ibox, first_rec, last_rec, + first_seq, last_seq, notify)) + return FALSE; + first_rec = NULL; + + seq = first_seq; + rec = ibox->index->lookup(ibox->index, seq); } else { seq++; + rec = ibox->index->next(ibox->index, rec); } + } - rec = ibox->index->next(ibox->index, rec); + if (first_rec != NULL) { + if (!index_expunge_mails(ibox, first_rec, last_rec, + first_seq, last_seq, notify)) + return FALSE; } if (no_permission) { diff --git a/src/lib-storage/index/mbox/mbox-expunge.c b/src/lib-storage/index/mbox/mbox-expunge.c index cf83281d7a..5820d0298e 100644 --- a/src/lib-storage/index/mbox/mbox-expunge.c +++ b/src/lib-storage/index/mbox/mbox-expunge.c @@ -15,11 +15,13 @@ static int expunge_real(struct index_mailbox *ibox, struct istream *input, struct ostream *output, int notify) { + struct mail_index_record *first_rec, *last_rec; uoff_t offset, hdr_size, body_size; uoff_t end_offset, from_offset, copy_size, old_limit; const unsigned char *data; size_t size; - int expunges, failed; + unsigned int first_seq, last_seq; + int expunges, skip_next, deleted, failed; if (seq == 1) end_offset = 0; @@ -39,7 +41,10 @@ static int expunge_real(struct index_mailbox *ibox, old_limit = input->v_limit; - expunges = FALSE; + first_rec = last_rec = NULL; + first_seq = last_seq = 0; + + expunges = FALSE; skip_next = FALSE; while (rec != NULL) { if (!mbox_mail_get_location(ibox->index, rec, &offset, &hdr_size, &body_size)) @@ -48,10 +53,14 @@ static int expunge_real(struct index_mailbox *ibox, from_offset = end_offset; end_offset = offset + hdr_size + body_size; - if (rec->msg_flags & MAIL_DELETED) { - if (!index_expunge_mail(ibox, rec, seq, notify)) - return FALSE; - seq--; + deleted = (rec->msg_flags & MAIL_DELETED) != 0; + if (deleted) { + if (first_rec == NULL) { + first_rec = rec; + first_seq = seq; + } + last_rec = rec; + last_seq = seq; if (!expunges) { /* first expunged record, seek to position @@ -60,7 +69,25 @@ static int expunge_real(struct index_mailbox *ibox, return FALSE; expunges = TRUE; } - } else if (expunges) { + } else if (first_rec != NULL) { + if (!index_expunge_mails(ibox, first_rec, last_rec, + first_seq, last_seq, notify)) + return FALSE; + first_rec = NULL; + + rec = ibox->index->lookup(ibox->index, first_seq); + seq = first_seq; + skip_next = TRUE; + } + + if (skip_next) + skip_next = FALSE; + else { + rec = ibox->index->next(ibox->index, rec); + seq++; + } + + if (expunges && !deleted) { /* seek to wanted input position, and copy this messages */ i_assert(input->v_offset <= from_offset); @@ -85,9 +112,12 @@ static int expunge_real(struct index_mailbox *ibox, if (failed || input->v_offset != end_offset) return FALSE; } + } - rec = ibox->index->next(ibox->index, rec); - seq++; + if (first_rec != NULL) { + if (!index_expunge_mails(ibox, first_rec, last_rec, + first_seq, last_seq, notify)) + return FALSE; } i_stream_skip(input, end_offset - input->v_offset);