]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Removed .imap.index.tree file. For now we'll just rewrite .imap.index file
authorTimo Sirainen <tss@iki.fi>
Mon, 21 Jul 2003 14:35:39 +0000 (17:35 +0300)
committerTimo Sirainen <tss@iki.fi>
Mon, 21 Jul 2003 14:35:39 +0000 (17:35 +0300)
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

21 files changed:
src/lib-index/Makefile.am
src/lib-index/mail-index-compress.c
src/lib-index/mail-index-data.c
src/lib-index/mail-index-file.c [new file with mode: 0644]
src/lib-index/mail-index-fsck.c
src/lib-index/mail-index-open.c
src/lib-index/mail-index.c
src/lib-index/mail-index.h
src/lib-index/mail-modifylog.c
src/lib-index/mail-modifylog.h
src/lib-index/mail-tree-redblack.c [deleted file]
src/lib-index/mail-tree.c [deleted file]
src/lib-index/mail-tree.h [deleted file]
src/lib-index/maildir/maildir-rebuild.c
src/lib-index/maildir/maildir-sync.c
src/lib-index/mbox/mbox-index.c
src/lib-index/mbox/mbox-sync-full.c
src/lib-storage/index/index-expunge.c
src/lib-storage/index/index-storage.h
src/lib-storage/index/maildir/maildir-expunge.c
src/lib-storage/index/mbox/mbox-expunge.c

index 4db43f92518192293dea50cb7c437e245273287e..7a9aa5ce2c72a6df4c26a900a88b822a33f1eba8 100644 (file)
@@ -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 \
index 6726bb0700ab1be0601566cd29bccb7866e49385..0f78e0e01a88c3f990473a4a688ccff7fa42398a 100644 (file)
@@ -5,7 +5,6 @@
 #include "mail-index.h"
 #include "mail-index-data.h"
 #include "mail-index-util.h"
-#include "mail-tree.h"
 
 #include <stdio.h>
 #include <unistd.h>
@@ -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)
 {
index cf30e2a9c5125f913c48a62be98b8e35a62e7891..6403073dd6b5815364082c98ea6a0fc65ed2dd05 100644 (file)
@@ -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 (file)
index 0000000..7c2af42
--- /dev/null
@@ -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;
+}
index 987fc5f47b71f943d57d985d09ee22adcb91847b..f9b108de6418d12c68075f6bab47b8e74f3a841b 100644 (file)
@@ -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);
index e3c00f3d452aec9efcd8c72b4755413149762889..a5c27e2a0063259f86fa746dc6613e8b46e636d9 100644 (file)
@@ -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. */
index 4d3c8ff234b82dec9c34ee564213a9a826bcc9e6..c94ccc56c716a16acdd1da0aa1f507569a7d071a 100644 (file)
@@ -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);
        }
 }
 
index d6864b92ada7592f84deb48c833fae7daf1eee30..78ce3fa53ccbb7b911fab68d8179b7475b61442f 100644 (file)
@@ -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) \
index f198be924415551587b0020136e5ed46f36fe627..d98bb9502e671d990f5ca9e4422049b9efd73c9d 100644 (file)
@@ -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))
index 0efdb0f8303d24bff6f69db2cb934e7211d2fd2f..1589e6c374079b0ef33b51ecfae1edb2f8b1f92d 100644 (file)
@@ -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 (file)
index 770861a..0000000
+++ /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 (file)
index e6a1353..0000000
+++ /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 <unistd.h>
-#include <fcntl.h>
-
-#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 (file)
index 775c8c7..0000000
+++ /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
index 23604ab86d401516e1110db978205adfe208ab59..dee9c6779cad695eaebdd29cca658d17ee2aea99 100644 (file)
@@ -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;
index a3e50704609748f06728494576b6d3c7651f948b..6bba31c9edb092903518c78d75ec00688741e2a2 100644 (file)
@@ -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);
index 977d71f26c29952c664537f4d4a6a424298bd750..57e9d6f7e017e67d64142f7f4280f108bc06a919 100644 (file)
@@ -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 |
index 0934ca92a2b033d5fe65387fab7775dc36cdea2b..9e9833e82dcb3e4a269f3d1fac15a49266003c07 100644 (file)
@@ -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)) {
index afa7223c2f34f61e09ccb6d75b7126657ccf50c0..7a7771f9f06e3a41bc15d9a4de3734e3af7306a2 100644 (file)
@@ -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;
index 0a5a4469a9e8382a61fa4825c7d00fd8bc7d9df8..6ce3397dd6012ca6c900dd0e0450e6c31b132987 100644 (file)
@@ -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);
index 5550baf705186e36fa8ff56ef05195b8a490cd14..b28f0f471e786806fcc56befdc73d5ef58807515 100644 (file)
@@ -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) {
index cf83281d7a320d4082644fabedca143522d6b872..5820d0298e26a2b43f15062bfde36342696cc6ec 100644 (file)
@@ -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);